您似乎正在为集合视图的顶部约束设置动画,这会更改其高度。
集合视图仅在需要时渲染单元格。
因此,一开始只创建一个(或两个)单元格。然后,当您更改高度时,会创建并添加新单元格。所以,你会看到一个“奇怪的动画”。
您想要做的是不为您的收藏视图设置底部约束。相反,设置其高度约束,然后将顶部约束更改为上下“滑动”:
我假设您正在使用 UICollectionViewCompositionalLayout.list 和 appearance: .insetGrouped ...
这是一个获得该结果的完整示例:
struct MyCVData: Hashable {
var name: String
}
class AnimCVViewController: UIViewController {
var myCollectionView: UICollectionView!
var dataSource: UICollectionViewDiffableDataSource<Section, MyCVData>!
var cvDataList: [MyCVData] = []
enum Section {
case main
}
var snapshot: NSDiffableDataSourceSnapshot<Section, MyCVData>!
var topConstraint: NSLayoutConstraint!
// when collection view is "Up" we want its
// Top to be 100-points from the Top of the view (safe area)
var topPosition: CGFloat = 100
// when collection view is "Down" we want its
// Top to be 80-points from the Bottom of the view (safe area)
var bottomPosition: CGFloat = 80
override func viewDidLoad() {
super.viewDidLoad()
// so we have a title if we're in a navigation controller
self.navigationController?.setNavigationBarHidden(true, animated: false)
view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
configureCollectionView()
buildData()
// create an Animate button
let btn = UIButton()
btn.backgroundColor = .yellow
btn.setTitle("Animate", for: [])
btn.setTitleColor(.black, for: .normal)
btn.setTitleColor(.lightGray, for: .highlighted)
btn.translatesAutoresizingMaskIntoConstraints = false
myCollectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(myCollectionView)
let g = view.safeAreaLayoutGuide
// start with the collection view "Down"
topConstraint = myCollectionView.topAnchor.constraint(equalTo: g.bottomAnchor, constant: -bottomPosition)
NSLayoutConstraint.activate([
// constrain the button at the Top, 200-pts width, centered horizontally
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
btn.widthAnchor.constraint(equalToConstant: 200.0),
btn.centerXAnchor.constraint(equalTo: g.centerXAnchor),
// button Height 10-points less than our collection view's Top Position
btn.heightAnchor.constraint(equalToConstant: topPosition - 10.0),
// activate top constraint
topConstraint,
// collection view Height should be the Height of the view (safe area)
// minus the Top Position
myCollectionView.heightAnchor.constraint(equalTo: g.heightAnchor, constant: -topPosition),
// let's use 40-points leading and trailing
myCollectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
myCollectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
])
// add an action for the button
btn.addTarget(self, action: #selector(animateAction), for: .touchUpInside)
}
@objc func animateAction() {
// if the topConstraint constant is -bottomPosition, that means it is "Down"
// so, if it's "Down"
// animate it so its Top is its own Height from the Bottom
// otherwise
// animate it so its Top is at bottomPosition
topConstraint.constant = topConstraint.constant == -bottomPosition ? -myCollectionView.frame.height : -bottomPosition
UIView.animate(withDuration: 1.0, animations: {
self.view.layoutIfNeeded()
})
}
func configureCollectionView() {
var layoutConfig = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
layoutConfig.backgroundColor = .red
let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
myCollectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout)
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, MyCVData> { (cell, indexPath, item) in
var content = UIListContentConfiguration.cell()
content.text = item.name
content.textProperties.font.withSize(8.0)
content.textProperties.font = UIFont.preferredFont(forTextStyle: .body)
content.textProperties.adjustsFontSizeToFitWidth = false
cell.contentConfiguration = content
}
dataSource = UICollectionViewDiffableDataSource<Section, MyCVData>(collectionView: myCollectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: MyCVData) -> UICollectionViewCell? in
// Dequeue reusable cell using cell registration (Reuse identifier no longer needed)
let cell = collectionView.dequeueConfiguredReusableCell(using: cellRegistration,
for: indexPath,
item: identifier)
return cell
}
}
func buildData() {
// create 20 data items ("Cell: 1" / "Cell: 2" / "Cell: 3" / etc...)
for i in 0..<20 {
let d = MyCVData(name: "Cell: \(i)")
cvDataList.append(d)
}
// Create a snapshot that define the current state of data source's data
self.snapshot = NSDiffableDataSourceSnapshot<Section, MyCVData>()
self.snapshot.appendSections([.main])
self.snapshot.appendItems(cvDataList, toSection: .main)
// Display data in the collection view by applying the snapshot to data source
self.dataSource.apply(self.snapshot, animatingDifferences: false)
}
}