【问题标题】:UICollectionView with Compositional Layout & Diff Datasources disappears on scroll具有组合布局和差异数据源的 UICollectionView 在滚动时消失
【发布时间】:2020-11-07 10:40:08
【问题描述】:

我目前有一个 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()
    }
}

【问题讨论】:

    标签: ios swift uicollectionview


    【解决方案1】:

    原因是您的数据源是您的 setup 方法 (let dataSource...) 中的一个局部变量。因此,它在最初填充集合视图时只工作一次,然后就烟消云散,留下没有数据的集合视图。

    数据源需要是视图控制器的一个属性,这样它才能持久存在!一旦你做出改变,一切都会好起来的。

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

    这是对您的代码的最小重写;找我的 cmets 解释这些变化。

    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()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多