ios - 改进复杂集合视图构造中的滚动
问题描述
我正在开发一个灵活的表单组件,它基本上由一个带有集合视图的 UIViewController 组成,它可以具有该类的无限嵌套视图控制器。在当前状态下,它或多或少都可以正常工作,但不幸的是滚动不稳定,我还没有找到解决该问题的方法。也许你们中的某个人有一个好的解决方案的想法。我还尝试过缓存视图控制器,这有助于实现平滑滚动,但不幸的是,使用该解决方案,我开始出现显示问题。有时单元格的内容就消失了,我无法弄清楚原因。
这是完整项目的链接: https ://d.pr/f/0ij57m
这个项目的主要部分基本上就是这个FlexibleViewController(通过storyboard设计的)
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
}
提前感谢您的帮助!:-)
解决方案
你应该看看 Apple 关于Prefetching Collection View Data的优秀文档
它包含一个 (Swift) 示例项目,并大量使用OperationQueue
and NSCache
。
基本思想是“预取”并在显示下一个项目之前缓存它们以进行平滑滚动。向后滚动时,将从缓存中获取项目。
推荐阅读
- javascript - php标头更改后Javascript动画不起作用
- python - 如何将子进程中的密码传递给 MySQL
- c++ - 如何提升 cppcheck 中的特定样式错误
- excel - 根据另一列上的 IF 语句在 Msgbox 中显示单元格值 - Excel VBA
- reactjs - 如何通过 axios 在 Reactjs 中使用多个参数发出 post 请求
- swift - 我可以制作一个 Swift 枚举泛型,以便我可以使用它的案例来推断泛型类的类型吗?
- android - 调用 ViewModelProvider(Activity) 和 ViewModelProvider(Fragment) 有什么区别?
- python - 'is' 运算符如何不使用 input() 方法?
- html - 如何在输入下移动按钮?
- php - 我如何在 PHP HTML 中循环具有特定日期和时间范围的 TABLE TD