Swift UICollectionView:使用动画和自定义布局调整单元格大小
我有一个带有自定义布局和可扩散数据源的集合视图。我想用动画调整单元格的大小 具有自定义布局:它看起来像是动画期间缩放到新大小的单元格内容。只有当单元达到其最终大小时,才会最终重新绘制(请参见下面的动画) 使用库存流布局时子视图在动画中的布局正确:标签保持在顶部,不调整大小,开关也是如此 因此,我假设问题在布局实现中,可以向布局添加哪些内容以允许在动画期间使用正确的布局 说明 本例中使用的自定义布局是,我在代码中使用了自定义布局。我注意到,在某些情况下,始终返回相同的布局属性对象是很重要的,而Swift UICollectionView:使用动画和自定义布局调整单元格大小,swift,uicollectionviewlayout,Swift,Uicollectionviewlayout,我有一个带有自定义布局和可扩散数据源的集合视图。我想用动画调整单元格的大小 具有自定义布局:它看起来像是动画期间缩放到新大小的单元格内容。只有当单元达到其最终大小时,才会最终重新绘制(请参见下面的动画) 使用库存流布局时子视图在动画中的布局正确:标签保持在顶部,不调整大小,开关也是如此 因此,我假设问题在布局实现中,可以向布局添加哪些内容以允许在动画期间使用正确的布局 说明 本例中使用的自定义布局是,我在代码中使用了自定义布局。我注意到,在某些情况下,始终返回相同的布局属性对象是很重要的,而FS
FSQCollectionViewAlignedLayout
不这样做。我修改它不返回副本,结果是一样的
对于单元格(在xib中定义),单元格的contentMode
及其contentView
设置为.top
。单元格的contentView
包含两个子视图:一个标签(在layoutSubviews
中布局)和一个开关(带约束布局,但其存在对动画没有影响)
控制器的代码为:
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout {
enum Section {
case main
}
@IBOutlet weak var collectionView : UICollectionView!
var layout = FSQCollectionViewAlignedLayout()
var dataSource : UICollectionViewDiffableDataSource<Section, String>!
var cellHeight : CGFloat = 100
override func loadView() {
super.loadView()
layout.defaultCellSize = CGSize(width: 150, height: 100)
collectionView.setCollectionViewLayout(layout, animated: false) // uncommented to use the stock flow layout
dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { cv, indexPath, item in
guard let cell = cv.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? Cell else { return nil }
cell.label.text = item
return cell
})
var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
snapshot.appendSections([.main])
snapshot.appendItems(["1", "2"], toSection: .main)
dataSource.apply(snapshot)
}
override func viewDidLoad() {
super.viewDidLoad()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 150, height: cellHeight)
}
@IBAction func resizeAction(_ sender: Any) {
if let layout = collectionView.collectionViewLayout as? FSQCollectionViewAlignedLayout {
UIView.animate(withDuration: 0.25) {
layout.defaultCellSize.height += 50
let invalidation = FSQCollectionViewAlignedLayoutInvalidationContext()
invalidation.invalidateItems(at: [[0, 0], [0, 1]])
layout.invalidateLayout(with: invalidation)
self.view.layoutIfNeeded()
}
}
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
UIView.animate(withDuration: 0.25) {
self.cellHeight += 50
let invalidation = UICollectionViewFlowLayoutInvalidationContext()
invalidation.invalidateItems(at: [[0, 0], [0, 1]])
layout.invalidateLayout(with: invalidation)
self.view.layoutIfNeeded()
}
}
}
}
class Cell : UICollectionViewCell {
@IBOutlet weak var label: UILabel!
override func layoutSubviews() {
super.layoutSubviews()
label.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 30)
}
}
类ViewController:UIViewController、UICollectionViewDelegateFlowLayout{
枚举部分{
主箱
}
@ibvar collectionView:UICollectionView!
var layout=FSQCollectionViewAlignedLayout()
var数据源:UICollectionViewDiffableDataSource!
变量单元格高度:CGFloat=100
重写func loadView(){
super.loadView()
layout.defaultCellSize=CGSize(宽:150,高:100)
collectionView.setCollectionViewLayout(布局,动画:false)//未注释以使用库存流布局
dataSource=UICollectionViewDiffableDataSource(collectionView:collectionView,cellProvider:{cv,indexPath,中的项
guard let cell=cv.dequeueReuseAbleCell(withReuseIdentifier:“cell”,for:indexPath)作为?cell else{return nil}
cell.label.text=项
返回单元
})
var snapshot=NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
snapshot.appendItems([“1”,“2”],toSection:.main)
dataSource.apply(快照)
}
重写func viewDidLoad(){
super.viewDidLoad()
}
func collectionView(collectionView:UICollectionView,布局collectionViewLayout:UICollectionViewLayout,sizeForItemAt indexPath:indexPath)->CGSize{
返回CGSize(宽度:150,高度:cellHeight)
}
@iAction func resizeAction(\发送方:任意){
如果让布局=collectionView.collectionViewLayout为?FSQCollectionViewAlignedLayout{
UIView.animate(持续时间:0.25){
layout.defaultCellSize.height+=50
let invalidation=FSQCollectionViewAlignedLayoutInvalidationContext()
无效。无效项(位于:[[0,0],[0,1]])
布局.失效失效(带:失效)
self.view.layoutifneed()
}
}
如果让布局=collectionView.collectionViewLayout为?UICollectionViewFlowLayout{
UIView.animate(持续时间:0.25){
self.cellHeight+=50
let invalidation=UICollectionViewFlowLayoutInvalidationContext()
无效。无效项(位于:[[0,0],[0,1]])
布局.失效失效(带:失效)
self.view.layoutifneed()
}
}
}
}
类单元格:UICollectionViewCell{
@IBVAR标签:UILabel!
覆盖func布局子视图(){
super.layoutSubviews()
label.frame=CGRect(x:0,y:0,宽度:bounds.width,高度:30)
}
}
我在这个问题上花了一张开发者支持票。答案是不支持重新加载单元格时的自定义动画 因此,作为一种解决方法,我最终创建了新的单元实例,并将其添加到视图层次结构中,然后添加自动布局约束,以便它们与正在调整大小的单元完全重叠。然后在这些新添加的单元格上运行动画。动画结束时,将删除这些单元格