【问题标题】:iOS: UICollectionView animate height changeiOS:UICollectionView 动画高度变化
【发布时间】:2022-01-19 12:47:11
【问题描述】:

我在视图控制器中有一个简单的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


    【解决方案1】:

    您似乎正在为集合视图的顶部约束设置动画,这会更改其高度

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

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

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

    我假设您正在使用 UICollectionViewCompositionalLayout.listappearance: .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)
        }
    }
    

    【讨论】:

    • 谢谢。它正在工作!但是有没有办法用集合视图为高度设置动画?我正在为 iOS 14 构建一个可滚动的底页。集合视图将成为底页的一部分。我正在试验 UISheetPresentationController (iOS 15),看起来视图控制器的高度发生了变化。
    • @iqra - 您需要显示用于“底页”的代码
    猜你喜欢
    • 2015-02-27
    • 2018-09-13
    • 1970-01-01
    • 2013-06-06
    • 1970-01-01
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多