改善复杂集合视图构造中的滚动

时间:2018-10-12 14:40:21

标签: ios swift

我正在研究一个灵活的表单组件,该组件基本上由具有集合视图的UIViewController组成,该视图可以具有该类的无限嵌套的视图控制器。 在当前状态下,它或多或少都可以正常工作,但是不幸的是滚动不稳定,而且我还没有找到解决该问题的方法。也许你们当中有人想出一个好的解决方案。 我还尝试过缓存视图控制器,这有助于实现平滑的滚动,但是不幸的是,有了该解决方案,我开始出现显示问题。有时,单元格中的内容只是消失了,我无法弄清原因。

以下是整个项目的链接: https://d.pr/f/0ij57m

该项目的主要部分基本上是这个FlexibleViewController(通过情节提要设计)

class FlexibleViewController: UIViewController {

    var dataForDatasource = Array<Component>()
    var datasource: UICollectionViewDataSource?

    @IBOutlet var collectionView: UICollectionView! {
        didSet {
            let datasource = MyDatasource(data: dataForDatasource, parentController: self)
            self.datasource = datasource
            self.collectionView.dataSource = datasource
            self.collectionView.delegate = datasource
        }
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        self.view.setNeedsLayout()
        self.collectionView?.collectionViewLayout.invalidateLayout()
    }

    func invalidateLayout() {
        self.collectionView.collectionViewLayout.invalidateLayout()
        if let controller = self.parent as? FlexibleViewController {
            controller.invalidateLayout()
        }
    }
}

此数据源:

    class MyDatasource: NSObject, UICollectionViewDataSource, UICollectionViewDelegate {

    weak var parentController: UIViewController!
    var components: Array<Component>!

    init(data: Array<Component>, parentController: UIViewController) {
        self.components = data
        self.parentController = parentController
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return components.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let component = components[indexPath.row]

        var cell: UICollectionViewCell?
        if component.subComponents.count > 0 {
            let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collection", for: indexPath) as! TestCell

            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let vs = storyboard.instantiateViewController(withIdentifier: "flexi") as! FlexibleViewController
            vs.dataForDatasource = component.subComponents

            parentController.addChild(vs)
            collectionCell.addSubview(vs.view)
            vs.collectionView.backgroundColor = component.backgroundColor
            vs.didMove(toParent: parentController)
            collectionCell.controller = vs

            cell = collectionCell
        } else {
            cell = collectionView.dequeueReusableCell(withReuseIdentifier: "default", for: indexPath)
            cell!.backgroundColor = component.backgroundColor
        }

        return cell!
    }
}

extension MyDatasource: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        var sizeForItem: CGSize
        let component = components[indexPath.row]
        var width = Int(collectionView.frame.size.width)
        if component.width != -1 && component.width < width {
            width = component.width
        }

        var height: Int = 50
        if component.subComponents.count > 0 {
            let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: width, height: height), collectionViewLayout: UICollectionViewFlowLayout())
            let datasource = MyDatasource(data: component.subComponents, parentController: UIViewController())
            collectionView.dataSource = datasource
            collectionView.delegate = datasource
            collectionView.reloadData()
            height = Int(collectionView.collectionViewLayout.collectionViewContentSize.height)
            sizeForItem = CGSize(width: width, height: height)
        } else {
            sizeForItem = CGSize(width: width, height: height)
        }
        return sizeForItem

    }
}

如您所见,当该组件具有多个组件时,将通过情节提要实例化具有集合视图的新控制器,这可能并不理想(尽管并不像我预期的那样昂贵)。我试图重新初始化viewDidLoad中的所有控制器,但是就像我在介绍中所说的那样,该解决方案遇到显示问题。

该单元格非常简单,看起来像这样:

class TestCell: UICollectionViewCell {
    var controller: UIViewController?

    override func prepareForReuse() {
        super.prepareForReuse()
        controller?.willMove(toParent: nil)
        controller?.removeFromParent()
        controller?.view.removeFromSuperview()
    }
}

组件类如下:

class Component {
    var generatedId = NSUUID().uuidString
    var identifier: String?
    var subComponents = Array<Component>()
    var width: Int! = -1
    var type: String! = "default"
    var backgroundColor: UIColor! = .white
    var isOpen: Bool = false
}

在此先感谢您的帮助! :-)

1 个答案:

答案 0 :(得分:0)

您应该查看有关Prefetching Collection View Data的Apple出色的文档

它包含一个(快速)示例项目,并大量使用OperationQueueNSCache

基本思想是“预取”并缓存下一个项目,然后显示它们以平滑滚动。向后滚动时,将从缓存中获取项目。