簡體   English   中英

iOS:UICollectionView 動畫高度變化

[英]iOS: UICollectionView animate height change

我在UICollectionView視圖中有一個簡單的 UICollectionView。 我正在通過一個按鈕為集合視圖的頂部約束設置動畫。 在第一個按鈕點擊時,集合視圖單元格的動畫非常奇怪。 在隨后的敲擊之后,animation 是平滑的。

在此處輸入圖像描述

動畫方法:

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

看起來您正在為集合視圖的 Top Constraint 設置動畫,這會更改其Height

集合視圖僅在需要時渲染單元格。

因此,一開始只創建一個(或兩個)單元格。 然后,當您更改高度時,會創建並添加新單元格。 因此,您會看到“奇怪的 animation”。

您要做的不是為您的集合視圖設置底部約束。 相反,設置其高度約束,然后將頂部約束更改為上下“滑動”它:

在此處輸入圖像描述

我假設您正在使用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)
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM