ios - 具有组合布局和差异数据源的 UICollectionView 在滚动时消失

标签 ios swift uicollectionview

我目前有一个 UICollectionView,我正在使用组合布局和 Diffable 数据源。我并没有做任何疯狂的事情,只是将 150 个单元格加载到其中并定义了 4 列布局。每当我点击 collectionview 滚动它似乎正在消失的项目时,我似乎遇到了一些奇怪的行为......下面是我的文件,其中包含我正在使用的整个代码,以便您可以复制并粘贴它并看到这个奇怪的行为。有谁知道为什么会发生这种情况?

import UIKit

class ViewController: UIViewController {
    
    private lazy var myCollectionViewLayout = MyCollectionViewLayout()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
        // Do any additional setup after loading the view.
    }
}

private extension ViewController {
    
    func setup() {
        
        let collectionVw = UICollectionView(frame: .zero, collectionViewLayout: myCollectionViewLayout.createLayout())
        collectionVw.translatesAutoresizingMaskIntoConstraints = false
        collectionVw.register(MyCustomCell.self, forCellWithReuseIdentifier: MyCustomCell.cellId)

        self.view.addSubview(collectionVw)
        
        NSLayoutConstraint.activate([
            collectionVw.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            collectionVw.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
            collectionVw.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            collectionVw.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
        ])
        
        let dataSource = UICollectionViewDiffableDataSource<Int, UUID>(collectionView: collectionVw) { (collectionView, indexPath, item) -> UICollectionViewCell? in

            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCustomCell.cellId, for: indexPath) as? MyCustomCell
            cell?.configure(at: indexPath.row)
            return cell
        }

        collectionVw.dataSource = dataSource
        
        var snapshot = NSDiffableDataSourceSnapshot<Int, UUID>()

        snapshot.appendSections([0])

        Range(0...150).forEach { item in
            snapshot.appendItems([UUID()], toSection: 0)
        }

        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

class MyCollectionViewLayout {
    
    func createLayout() -> UICollectionViewCompositionalLayout {
        
        let layout =  UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnv: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
            
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.25),
                                                  heightDimension: .fractionalHeight(1))
                
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                                   heightDimension: .fractionalWidth(0.25))
            
            let item = NSCollectionLayoutItem(layoutSize: itemSize)

            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                           subitem: item,
                                                           count: 4)
            group.interItemSpacing = .fixed(16)

            let section = NSCollectionLayoutSection(group: group)

            section.interGroupSpacing = 16
            section.contentInsets = NSDirectionalEdgeInsets(top: 0,
                                                            leading: 16,
                                                            bottom: 0,
                                                            trailing: 16)

            return section
        }
        
        return layout
        
    }
}

class MyCustomCell: UICollectionViewCell {
    
    static let cellId = "MyCustomCell"
    
    private var lbl: UILabel?
    
    func configure(at index: Int) {
        
        self.contentView.layer.cornerRadius = 8
        self.contentView.backgroundColor = .blue
        
        lbl = UILabel()
        lbl?.translatesAutoresizingMaskIntoConstraints = false
        lbl?.text = index.description
        lbl?.textAlignment = .center
        lbl?.font = .preferredFont(forTextStyle: .headline)
        
        self.contentView.addSubview(lbl!)
        
        NSLayoutConstraint.activate([
            lbl!.topAnchor.constraint(equalTo: self.contentView.topAnchor),
            lbl!.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),
            lbl!.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
            lbl!.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor)
        ])
        
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        lbl?.removeFromSuperview()
    }
}

最佳答案

原因是您的数据源是您的setup 方法中的局部变量(let dataSource...)。因此它只工作一次,最初填充 Collection View ,然后消失在一阵烟雾中,留下没有数据的 Collection View 。

数据源必须是 View Controller 的一个属性,这样它才能持久存在!一旦做出改变,一切都会好起来的。

这样做实际上有点棘手,因为数据源取决于 Collection View 。所以你必须使用惰性实例化。您使用空布局创建 Collection View ,并在设置的第一步将所有内容连接在一起。

这是对代码的最小重写;查找我解释更改的评论。

import UIKit


class ViewController: UIViewController {
    // delete; this is pointless
    // private lazy var myCollectionViewLayout = MyCollectionViewLayout()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }
    
    // need persistent pointer to collection view
    let collectionVw = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())

    // need persistent pointer to data source
    lazy var dataSource = UICollectionViewDiffableDataSource<Int, UUID>(collectionView: collectionVw) { (collectionView, indexPath, item) -> UICollectionViewCell? in

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCustomCell.cellId, for: indexPath) as? MyCustomCell
        cell?.configure(at: indexPath.row)
        return cell
    }

}

private extension ViewController {
    
    func setup() {
        
        
//        let collectionVw = UICollectionView(frame: .zero, collectionViewLayout: myCollectionViewLayout.createLayout())
        // everything is persistent, now just hook them together
        collectionVw.collectionViewLayout = MyCollectionViewLayoutMaker.createLayout()
        
        collectionVw.translatesAutoresizingMaskIntoConstraints = false
        collectionVw.register(MyCustomCell.self, forCellWithReuseIdentifier: MyCustomCell.cellId)

        self.view.addSubview(collectionVw)
        
        NSLayoutConstraint.activate([
            collectionVw.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            collectionVw.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
            collectionVw.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            collectionVw.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
        ])
        
//        let dataSource = UICollectionViewDiffableDataSource<Int, UUID>(collectionView: collectionVw) { (collectionView, indexPath, item) -> UICollectionViewCell? in
//
//            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCustomCell.cellId, for: indexPath) as? MyCustomCell
//            cell?.configure(at: indexPath.row)
//            return cell
//        }

        collectionVw.dataSource = dataSource
        
        var snapshot = NSDiffableDataSourceSnapshot<Int, UUID>()

        snapshot.appendSections([0])

        Range(0...150).forEach { item in
            snapshot.appendItems([UUID()], toSection: 0)
        }

        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

class MyCollectionViewLayoutMaker {
    // make this static; the class is not the layout, it serves no purpose except as a factory
    static func createLayout() -> UICollectionViewCompositionalLayout {
        
        let layout =  UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnv: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
            
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.25),
                                                  heightDimension: .fractionalHeight(1))
                
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                                   heightDimension: .fractionalWidth(0.25))
            
            let item = NSCollectionLayoutItem(layoutSize: itemSize)

            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                           subitem: item,
                                                           count: 4)
            group.interItemSpacing = .fixed(16)

            let section = NSCollectionLayoutSection(group: group)

            section.interGroupSpacing = 16
            section.contentInsets = NSDirectionalEdgeInsets(top: 0,
                                                            leading: 16,
                                                            bottom: 0,
                                                            trailing: 16)

            return section
        }
        
        return layout
        
    }
}

class MyCustomCell: UICollectionViewCell {
    
    static let cellId = "MyCustomCell"
    
    private var lbl: UILabel?
    
    func configure(at index: Int) {
        
        self.contentView.layer.cornerRadius = 8
        self.contentView.backgroundColor = .blue
        
        lbl = UILabel()
        lbl?.translatesAutoresizingMaskIntoConstraints = false
        lbl?.text = index.description
        lbl?.textAlignment = .center
        lbl?.font = .preferredFont(forTextStyle: .headline)
        
        self.contentView.addSubview(lbl!)
        
        NSLayoutConstraint.activate([
            lbl!.topAnchor.constraint(equalTo: self.contentView.topAnchor),
            lbl!.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),
            lbl!.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
            lbl!.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor)
        ])
        
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        lbl?.removeFromSuperview()
    }
}

关于ios - 具有组合布局和差异数据源的 UICollectionView 在滚动时消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62957098/

相关文章:

ios - 如何使用约束使按钮成为 UITableViewCell 宽度的一半( Storyboard或以编程方式)

ios - clocationmanager 在前几秒内返回零纬度和经度。我怎么能够

swift - 使用 use_modular_headers 复制 React 定义 cocoapods

ios - 嵌入在 UITableViewCell 中的动态、自定义布局 UICollectionView

c++ - AudioKit 的 AKNodeOutputPlot 如何提取数据?

ios - 网页 View : fatal error: unexpectedly found nil while unwrapping an Optional value

ios - 滚动后在 Collection View 中查看更新太迟

ios - UICollectionView 中的选择和取消选择问题

ios - 具有两个动态高度 UILabel 的自调整单元格

objective-c - Objective C 数学