iOS:UICollectionView 动画高度变化

问题描述 投票:0回答:2

我在视图控制器中有一个简单的

UICollectionView
。我通过按钮对集合视图的顶部约束进行动画处理。点击第一个按钮后,集合视图单元格的动画效果非常奇怪。后续点击后,动画变得流畅。

制作动画的方法:

@objc func animateAction() {
        
   UIView.animate(withDuration: 1) {
            
          self.animateUp.toggle()
          self.topConstraint.constant = self.animateUp ?  100 : self.view.bounds.height - 100
          self.view.layoutIfNeeded()
   }  
} 

编辑:实际需要构建什么:

ios swift uicollectionview nslayoutconstraint uiviewanimation
2个回答
0
投票

看起来您正在对集合视图的顶部约束进行动画处理,这会更改其高度

集合视图仅在需要时渲染单元格。

因此,一开始只创建一个(或两个)单元格。然后,当您更改高度时,将创建并添加新单元格。所以,你会看到一个“奇怪的动画”。

您想要做的是NOT为您的集合视图设置底部约束。相反,设置其高度约束,然后更改顶部约束以使其上下“滑动”:

我假设您正在使用

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

0
投票

我遇到了这个问题,这对我有帮助。 需要在 collectionView(_:cellForItemAt:) 的末尾添加这一行

UIView.performWithoutAnimation {
    cell.layoutIfNeeded()
}
return cell
© www.soinside.com 2019 - 2024. All rights reserved.