如何将自定义 UICollectionReusableView 用作 collection 视图的 header 部分?

How to use custom UICollectionReusableView as section header of collection view?

我已经被逼疯了几个小时,因为我无法解决这个问题。

我有一个 collection 视图,它可以有不同编号的不同部分。每个项目中的项目。对于每个部分,我需要使用不同类型的部分 header。因此,为此,我将使用 UICollectionReusableView。但是我似乎无法通过 UINib 注册来成功使用 UICollectionReusableView 的自定义子类。

A crash happens when I downcast the reusable view to my subclass. Like:

let friendHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, 
                                    withReuseIdentifier: "FriendHeaderView", 
                                    for: indexPath) as! FriendHeaderView

下面是代码片段:

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    private let viewModel = ProfileViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.dataSource = self
        collectionView.delegate = self
        // more code
        collectionView.register(UINib(nibName: "FriendHeaderView", bundle: nil), 
                                forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, 
                                withReuseIdentifier: "FriendHeaderView")
    }
}

下面是数据源实现:

extension ViewController: UICollectionViewDataSource {

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        // valid implementation
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // valid implementation
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        // valid implementation

    }

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        switch kind {
        case UICollectionView.elementKindSectionHeader:
            let friendHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FriendHeaderView", for: indexPath) as! FriendHeaderView

            // *** Crash happens here *** //

            return friendHeader
        default:
            assert(false, "Invalid element type")
        }

    }

}

而且我不知道为什么 collectionView(_:layout:referenceSizeForHeaderInSection:) 也需要实施。所以这里是:

extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        let size = CGSize(width: collectionView.bounds.width, height: 100)
        return size
    }

}

Okay, now come to the point: The above mentioned crash doesn't happen at all if I don't downcast with as! operator. Well, if I use section header from the storyboard instead of UINib registration, there is no crash.

如果我需要多种类型 header,那么我不能同时使用故事板方法或不使用 down-casting 方法,因为我需要向那些 header 提供数据秒。


如何使用从 Interface Builder 构建的视图拥有多个类型 header?


我根据上面所说的做了一个demo project。如果有人感兴趣,请检查一下。

一旦您在 Xib 文件中分配了正确的 class 和标识符,它就可以正常工作。

好吧,经过更多调查和@good4pc 在接受的答案中的输入(实际上我在查看答案之前自己发现了这一点)似乎问题实际上是由于 [= 的一些不需要的问题而发生的35=]Xcode。

当我们使用 .xib 创建任何视图(最好是 UITableViewCellUICollectionViewCell)时,会自动为 .xib 中的 class 提供身份身份检查员。但 UICollectionReusableView 的情况并非如此。请参阅下面随附的屏幕截图,以便于理解。

这是一个 UICollectionViewCell subclassed with .xib:

这是一个 UICollectionReusableView subclassed with .xib:


So the key is to provide the class identity of the .xib file which is done from the attributes inspector.