UICollectionView装饰视图 [英] UICollectionView Decoration View

查看:85
本文介绍了UICollectionView装饰视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有人为iOS 6 UICollectionView实现装饰视图?找到任何关于在网上实现装饰视图的教程是不可能的
。基本上在我的应用程序中我有多个部分,我只想在每个部分后面显示装饰视图。这应该很容易实现,但我没有运气。这让我疯狂......谢谢。

Has anyone implemented a decoration view for the iOS 6 UICollectionView? It's impossible to find any tutorial on implementing a decoration view on the web. Basically in my app I have multiple sections, and I just wanted to display a decoration view behind each section. This should be simple to implement but I'm having no luck. This is driving me nuts... Thanks.

推荐答案

这是一个集合视图布局装修视图教程在Swift中(这是Swift 3,Xcode 8种子6)。

Here's a collection view layout decoration view tutorial in Swift (this is Swift 3, Xcode 8 seed 6).

装饰视图不是UICollectionView功能;它们基本上属于UICollectionViewLayout。没有UICollectionView方法(或委托或数据源方法)提到装饰视图。 UICollectionView对它们一无所知;它只是按照它所说的去做。

Decoration views are not a UICollectionView feature; they essentially belong to the UICollectionViewLayout. No UICollectionView methods (or delegate or data source methods) mention decoration views. The UICollectionView knows nothing about them; it simply does what it is told.

要提供任何装饰视图,您需要一个UICollectionViewLayout子类;这个子类可以自由定义自己的属性和委托协议方法,自定义如何配置它的装饰视图,但这完全取决于你。

To supply any decoration views, you will need a UICollectionViewLayout subclass; this subclass is free to define its own properties and delegate protocol methods that customize how its decoration views are configured, but that's entirely up to you.

为了说明,我会子类UICollectionViewFlowLayout将标题标签强加到集合视图的内容矩形的顶部。这可能是对装饰视图的愚蠢使用,但它完美地说明了基本原理。为简单起见,我将首先对整个事物进行硬编码,使客户端无法自定义此视图的任何方面。

To illustrate, I'll subclass UICollectionViewFlowLayout to impose a title label at the top of the collection view's content rectangle. This is probably a silly use of a decoration view, but it illustrates the basic principles perfectly. For simplicity, I'll start by hard-coding the whole thing, giving the client no ability to customize any aspect of this view.

实现一个步骤有四个步骤布局子类中的装饰视图:

There are four steps to implementing a decoration view in a layout subclass:


  1. 定义UICollectionReusableView子类。

  1. Define a UICollectionReusableView subclass.

通过调用 register(_:forDecorationViewOfKind :) ,将UICollectionReusableView子类注册到布局( not 集合视图)。布局的初始化程序是一个很好的地方。

Register the UICollectionReusableView subclass with the layout (not the collection view), by calling register(_:forDecorationViewOfKind:). The layout's initializer is a good place to do this.

实现 layoutAttributesForDecorationView(ofKind:at:)返回定位UICollectionReusableView的布局属性。要构造布局属性,请调用 init(forDecorationViewOfKind:with :) 并配置属性。

Implement layoutAttributesForDecorationView(ofKind:at:) to return layout attributes that position the UICollectionReusableView. To construct the layout attributes, call init(forDecorationViewOfKind:with:) and configure the attributes.

覆盖 layoutAttributesForElements(in:),以便返回的数组中包含 layoutAttributesForDecorationView(ofKind:at:)的结果。

Override layoutAttributesForElements(in:) so that the result of layoutAttributesForDecorationView(ofKind:at:) is included in the returned array.

最后一步是导致装饰视图出现在集合视图中的原因。当集合视图调用 layoutAttributesForElements(in:)时,它会发现生成的数组包含指定类型的装饰视图的布局属性。集合视图对装饰视图一无所知,因此它返回到布局,询问这种装饰视图的实际实例。您已经注册了这种装饰视图以对应于您的UICollectionReusableView子类,因此您的UICollectionReusableView子类被实例化并返回该实例,并且集合视图根据布局属性对其进行定位。

The last step is what causes the decoration view to appear in the collection view. When the collection view calls layoutAttributesForElements(in:), it finds that the resulting array includes layout attributes for a decoration view of a specified kind. The collection view knows nothing about decoration views, so it comes back to the layout, asking for an actual instance of this kind of decoration view. You've registered this kind of decoration view to correspond to your UICollectionReusableView subclass, so your UICollectionReusableView subclass is instantiated and that instance is returned, and the collection view positions it in accordance with the layout attributes.

让我们按照步骤操作。定义UICollectionReusableView子类:

So let's follow the steps. Define the UICollectionReusableView subclass:

class MyTitleView : UICollectionReusableView {
    weak var lab : UILabel!
    override init(frame: CGRect) {
        super.init(frame:frame)
        let lab = UILabel(frame:self.bounds)
        self.addSubview(lab)
        lab.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        lab.font = UIFont(name: "GillSans-Bold", size: 40)
        lab.text = "Testing"
        self.lab = lab
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

现在我们转向我们的UICollectionViewLayout子类,我称之为MyFlowLayout。我们在布局的初始化程序中注册了MyTitleView;我还为剩下的步骤定义了一些我需要的私有属性:

Now we turn to our UICollectionViewLayout subclass, which I'll call MyFlowLayout. We register MyTitleView in the layout's initializer; I've also defined some private properties that I'll need for the remaining steps:

private let titleKind = "title"
private let titleHeight : CGFloat = 50
private var titleRect : CGRect {
    return CGRect(10,0,200,self.titleHeight)
}
override init() {
    super.init()
    self.register(MyTitleView.self, forDecorationViewOfKind:self.titleKind)
}

实现 layoutAttributesForDecorationView(ofKind:at:)

override func layoutAttributesForDecorationView(
    ofKind elementKind: String, at indexPath: IndexPath) 
    -> UICollectionViewLayoutAttributes? {
        if elementKind == self.titleKind {
            let atts = UICollectionViewLayoutAttributes(
                forDecorationViewOfKind:self.titleKind, with:indexPath)
            atts.frame = self.titleRect
            return atts
        }
        return nil
}

覆盖 layoutAttributesForElements(在:);这里的索引路径是任意的(我在上面的代码中忽略了它):

Override layoutAttributesForElements(in:); the index path here is arbitrary (I ignored it in the preceding code):

override func layoutAttributesForElements(in rect: CGRect) 
    -> [UICollectionViewLayoutAttributes]? {
        var arr = super.layoutAttributesForElements(in: rect)!
        if let decatts = self.layoutAttributesForDecorationView(
            ofKind:self.titleKind, at: IndexPath(item: 0, section: 0)) {
                if rect.intersects(decatts.frame) {
                    arr.append(decatts)
                }
        }
        return arr
}

这个有效!标题标签`Testing''出现在集合视图的顶部。

This works! A title label reading ``Testing'' appears at the top of the collection view.

现在我将展示如何使标签可定制。我们将允许客户端设置确定标题的属性,而不是标题测试。我将给我的布局子类一个公共标题属性:

Now I'll show how to make the label customizable. Instead of the title "Testing," we'll allow the client to set a property that determines the title. I'll give my layout subclass a public title property:

class MyFlowLayout : UICollectionViewFlowLayout {
    var title = ""
    // ...
}

使用此布局的人应设置此属性。例如,假设此集合视图显示美国50个州:

Whoever uses this layout should set this property. For example, suppose this collection view is displaying the 50 U.S. states:

func setUpFlowLayout(_ flow:UICollectionViewFlowLayout) {
    flow.headerReferenceSize = CGSize(50,50)
    flow.sectionInset = UIEdgeInsetsMake(0, 10, 10, 10)
    (flow as? MyFlowLayout)?.title = "States" // *
}

现在我们来一个好奇的谜题。我们的布局有一个 title 属性,其值必须以某种方式传递给我们的MyTitleView实例。但什么时候可能发生?我们不负责实例化MyTitleView;当集合视图在后台请求实例时,它会自动发生。 MyFlowLayout实例和MyTitleView实例不会相遇。

We now come to a curious puzzle. Our layout has a title property, the value of which needs to be communicated somehow to our MyTitleView instance. But when can that possibly happen? We are not in charge of instantiating MyTitleView; it happens automatically, when the collection view asks for the instance behind the scenes. There is no moment when the MyFlowLayout instance and the MyTitleView instance meet.

解决方案是使用布局属性作为信使。 MyFlowLayout从不符合MyTitleView,但它确实创建了布局属性对象,该对象被传递到集合视图以配置MyFlowLayout。所以布局属性对象就像一个信封。通过继承UICollectionViewLayoutAttributes,我们可以在该信封中包含我们喜欢的任何信息 - 例如标题:

The solution is to use the layout attributes as a messenger. MyFlowLayout never meets MyTitleView, but it does create the layout attributes object that gets passed to the collection view to configure MyFlowLayout. So the layout attributes object is like an envelope. By subclassing UICollectionViewLayoutAttributes, we can include in that envelope any information we like — such as a title:

class MyTitleViewLayoutAttributes : UICollectionViewLayoutAttributes {
    var title = ""
}

这是我们的信封!现在我们重写 layoutAttributesForDecorationView 的实现。当我们实例化布局属性对象时,我们实例化我们的子类并设置其 title 属性:

There's our envelope! Now we rewrite our implementation of layoutAttributesForDecorationView. When we instantiate the layout attributes object, we instantiate our subclass and set its title property:

override func layoutAttributesForDecorationView(
    ofKind elementKind: String, at indexPath: IndexPath) -> 
    UICollectionViewLayoutAttributes? {
        if elementKind == self.titleKind {
            let atts = MyTitleViewLayoutAttributes( // *
                forDecorationViewOfKind:self.titleKind, with:indexPath)
            atts.title = self.title // *
            atts.frame = self.titleRect
            return atts
        }
        return nil
}

最后,在MyTitleView中,我们实现了 apply(_:)方法。当集合视图配置装饰视图时,将调用此方法 - 将布局属性对象作为其参数!所以我们拿出标题并将其用作我们标签的文本:

Finally, in MyTitleView, we implement the apply(_:) method. This will be called when the collection view configures the decoration view — with the layout attributes object as its parameter! So we pull out the title and use it as the text of our label:

class MyTitleView : UICollectionReusableView {
    weak var lab : UILabel!
    // ... the rest as before ...
    override func apply(_ atts: UICollectionViewLayoutAttributes) {
        if let atts = atts as? MyTitleViewLayoutAttributes {
            self.lab.text = atts.title
        }
    }
}






很容易看到如何扩展示例以使字体和高度等标签功能可自定义。由于我们是UICollectionViewFlowLayout的子类,因此可能还需要进一步修改以通过按下其他元素为装饰视图腾出空间。另外,从技术上讲,我们应该在MyTitleView中覆盖 isEqual(_:)来区分不同的标题。所有这些都留给读者练习。


It's easy to see how you might extend the example to make such label features as font and height customizable. Since we are subclassing UICollectionViewFlowLayout, some further modifications might also be needed to make room for the decoration view by pushing down the other elements. Also, technically, we should override isEqual(_:) in MyTitleView to differentiate between different titles. All of that is left as an exercise for the reader.

这篇关于UICollectionView装饰视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆