ios 如何像 UITableView 的 tableHeaderView 一样在 UICollectionView 中添加 HeaderView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13522075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add HeaderView in UICollectionView like UITableView's tableHeaderView
提问by ChrHansen
How can I add a header view / top view (not section header) at the top of a UICollectionView?
如何在 a 的顶部添加标题视图/顶视图(不是节标题)UICollectionView?
It should act excactly as UITableView's tableHeaderViewproperty.
它应该完全作为UITableView的tableHeaderView财产。
So it needs to sit on top of the first section header view (before the section at index 0), scroll along with the rest of the content, and have user interaction.
因此,它需要位于第一个部分标题视图的顶部(在索引 0 处的部分之前),与其余内容一起滚动,并进行用户交互。
The best I've come up with so far is to make a special XIB (with MyCollectionReusableViewsubclass of UICollectionReusableViewas the File's owner) for the first section header view that is big enough to also contain my subviews in header, it's kind of a hack, I think, and I haven't managed to detect touches.
到目前为止我想出的最好的方法是为第一部分标题视图制作一个特殊的 XIB(MyCollectionReusableView子类UICollectionReusableView为文件的所有者),该视图足够大以在标题中包含我的子视图,这是一种黑客,我想想,我还没有设法检测到触摸。
Not sure if I can make my MyCollectionReusableViewsubclass to allow touches or there's a better way.
不确定我是否可以让我的MyCollectionReusableView子类允许触摸或者有更好的方法。
采纳答案by rdelmar
I've put a view at the top of a collection view by just adding a UIView as a subview of the collection view. It does scroll up with the collection view and it has normal UIView user interaction. This works ok if you have no section header, but doesn't work if you do. In that situation, I don't see anything wrong with the way you're doing it. I'm not sure why you're not detecting touches, they work fine for me.
我通过添加 UIView 作为集合视图的子视图,将视图放在集合视图的顶部。它确实随着集合视图向上滚动,并且具有正常的 UIView 用户交互。如果您没有节标题,这可以正常工作,但如果您没有,则行不通。在那种情况下,我认为你的做法没有任何问题。我不确定为什么你没有检测到触摸,它们对我来说很好用。
回答by dxci
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 320, self.view.frame.size.height) collectionViewLayout:flowlayout];
self.collectionView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0);
UIImageView *imagev = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"015.png"]];
imagev.frame = CGRectMake(0, -50, 320, 50);
[self.collectionView addSubview: imagev];
[self.view addSubview: _collectionView];
I use the attribute contentInsetto insert a frame to the top of the UICollectionView, then I add the image view to it, and it succeeds. I think it can act excactly as UITableView's tableHeaderViewproperty. What do you think?
我使用该属性contentInset在 顶部插入一个框架UICollectionView,然后向其添加图像视图,它成功了。我认为它完全可以充当UITableView's tableHeaderViewproperty。你怎么认为?
回答by Scott Kaiser
The best way to achieve this is to make a section header for the first section. Then set the UICollectionViewFlowLayout's property:
实现这一点的最佳方法是为第一部分制作一个部分标题。然后设置 UICollectionViewFlowLayout 的属性:
@property(nonatomic) BOOL sectionHeadersPinToVisibleBounds
or swift
或迅速
var sectionHeadersPinToVisibleBounds: Bool
to NO (false for swift). This will ensure that the section header scrolls continuously with the cells and does not get pinned to the top like a section header normally would.
为 NO(对于 swift 为 false)。这将确保节标题随单元格连续滚动,并且不会像节标题通常那样固定在顶部。
回答by Swift Rabbit
I ended up using a UICollectionView extension to add my custom header. Using a specific tag, I'm able to fake the stored property to identify my custom header (all transparent from outside). You might have to scroll to a specific offset if the layout of the UICollectionView is not completed when you add your custom header.
我最终使用 UICollectionView 扩展来添加我的自定义标题。使用特定的标签,我可以伪造存储的属性来识别我的自定义标头(所有外部都是透明的)。如果在添加自定义标题时 UICollectionView 的布局未完成,您可能需要滚动到特定的偏移量。
extension UICollectionView {
var currentCustomHeaderView: UIView? {
return self.viewWithTag(CustomCollectionViewHeaderTag)
}
func asssignCustomHeaderView(headerView: UIView, sideMarginInsets: CGFloat = 0) {
guard self.viewWithTag(CustomCollectionViewHeaderTag) == nil else {
return
}
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
headerView.frame = CGRect(x: sideMarginInsets, y: -height + self.contentInset.top, width: self.frame.width - (2 * sideMarginInsets), height: height)
headerView.tag = CustomCollectionViewHeaderTag
self.addSubview(headerView)
self.contentInset = UIEdgeInsets(top: height, left: self.contentInset.left, bottom: self.contentInset.bottom, right: self.contentInset.right)
}
func removeCustomHeaderView() {
if let customHeaderView = self.viewWithTag(CustomCollectionViewHeaderTag) {
let headerHeight = customHeaderView.frame.height
customHeaderView.removeFromSuperview()
self.contentInset = UIEdgeInsets(top: self.contentInset.top - headerHeight, left: self.contentInset.left, bottom: self.contentInset.bottom, right: self.contentInset.right)
}
}
}
CustomCollectionViewHeaderTag refers to the tag number you give to your header. Make sure it is not the tag of anotherView embedded in your UICollectionView.
CustomCollectionViewHeaderTag 是指您为标题提供的标签编号。确保它不是嵌入在 UICollectionView 中的 anotherView 的标签。
回答by Billthas
I think the best way to accomplish this it to subclass UICollectionViewLayout(UICollectionViewFlowLayout) and add the needed header or footer attributes.
我认为实现这一点的最佳方法是继承 UICollectionViewLayout(UICollectionViewFlowLayout) 并添加所需的页眉或页脚属性。
Since all the items including the supplementary view in UICollectionView layout according to its collectionViewLayoutproperty, it is the collectionviewlayoutthat decides whether there is a header(footer) or not.
由于 UICollectionView 布局中包括补充视图在内的所有项目都根据其collectionViewLayout属性collectionviewlayout来决定是否有页眉(页脚)。
Use contentInsetis easier, but there may be problem when you want to hide or show the header dynamically.
使用contentInset更容易,但是当您想动态隐藏或显示标题时可能会出现问题。
I have written a protocol to accomplish this, which could be adopted by any subclass of UICollectionViewLayout to make it have a header or footer. You can find it here.
我已经编写了一个协议来实现这一点,它可以被 UICollectionViewLayout 的任何子类采用以使其具有页眉或页脚。你可以在这里找到它。
The main idea is to create a UICollectionViewLayoutAttributesfor the header and return it in layoutAttributesForElements(in rect: CGRect)if needed, you will have to modify all the other attributes, all of their locations should be moved down by header height because the header is above them all.
主要思想是UICollectionViewLayoutAttributes为标题创建一个并在layoutAttributesForElements(in rect: CGRect)需要时将其返回,您将不得不修改所有其他属性,它们的所有位置都应按标题高度向下移动,因为标题在它们之上。
回答by user3836066
private var PreviousInsetKey: Void?
extension UIView {
var previousInset:CGFloat {
get {
return objc_getAssociatedObject(self, &PreviousInsetKey) as? CGFloat ?? 0.0
}
set {
if newValue == -1{
objc_setAssociatedObject(self, &PreviousInsetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
else{
objc_setAssociatedObject(self, &PreviousInsetKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
func addOnCollectionView(cv: UICollectionView){
if self.superview == nil{
var frame = self.frame
frame.size.width = SCREEN_WIDTH
cv.frame = frame
self.addOnView(view: cv)
let flow = cv.collectionViewLayout as! UICollectionViewFlowLayout
var inset = flow.sectionInset
previousInset = inset.top
inset.top = frame.size.height
flow.sectionInset = inset
}
}
func removeFromCollectionView(cv: UICollectionView){
if self.superview == nil{
return
}
self.removeFromSuperview()
let flow = cv.collectionViewLayout as! UICollectionViewFlowLayout
var inset = flow.sectionInset
inset.top = previousInset
flow.sectionInset = inset
previousInset = -1
}
func addOnView(view: UIView){
view.addSubview(self)
self.translatesAutoresizingMaskIntoConstraints = false
let leftConstraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0)
let widthConstraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.size.width)
let heightConstraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.size.height)
let topConstraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0)
view.addConstraints([leftConstraint, widthConstraint, heightConstraint, topConstraint])
self.layoutIfNeeded()
view.layoutIfNeeded()
}
}

