Markon
Markon

Reputation: 841

how to prevent delay when trying to animate invisible cells in collectionview

I would like to animate all of my collectionview cells and this simplified example illustrates the problem. The problem is that if I scroll down while animating cells the ones that are not visible gets animated with a delay. I would like invisible cells to just be in the final state of animation instead of having animation delay.

In the video you see that when I scroll to the bottom, the final cells starts to animate with delay.

enter image description here

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    var collectionview: UICollectionView!
    var moveText: Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionview = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        collectionview.translatesAutoresizingMaskIntoConstraints = false
        collectionview.dataSource = self
        collectionview.delegate = self
        collectionview.backgroundColor = .systemBackground
        collectionview.register(MyCell.self, forCellWithReuseIdentifier: "cell")
        view.addSubview(collectionview)
        
        NSLayoutConstraint.activate([
            collectionview.topAnchor.constraint(equalTo: view.topAnchor),
            collectionview.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            collectionview.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionview.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
        UIView.animate(withDuration: 1, delay: 0, options: [
            UIView.AnimationOptions.allowAnimatedContent,
            UIView.AnimationOptions.allowUserInteraction,
        ]) {
            cell.moveText = self.moveText
        }
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return .init(width: collectionview.frame.width, height: 120)
    }
    
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { timer in
            print("Starting animatings")
            self.moveText.toggle()
            self.collectionview.reloadData()

        }
    }
    
}

class MyCell: UICollectionViewCell {
    
    let label = UILabel()
    var moveText: Bool = false {
        didSet {
            if moveText == true {
                label.transform = CGAffineTransform(translationX: 50, y: 50)
            } else {
                label.transform = .identity
            }
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        label.text = "mytext"
        label.translatesAutoresizingMaskIntoConstraints = false
        addSubview(label)
        NSLayoutConstraint.activate([
            label.leadingAnchor.constraint(equalTo: leadingAnchor),
            label.topAnchor.constraint(equalTo: topAnchor)
        ])
        backgroundColor = .orange
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Upvotes: 0

Views: 145

Answers (1)

Witek Bobrowski
Witek Bobrowski

Reputation: 4259

instead of animating inside the cellForItemAt add this helper function.

private func animateVisibleCells() {
    collectionview.visibleCells.compactMap { $0 as? MyCell }.forEach { cell in
        UIView.animate(withDuration: 1, delay: 0, options: [
            UIView.AnimationOptions.allowAnimatedContent,
            UIView.AnimationOptions.allowUserInteraction,
        ]) {
            cell.moveText = self.moveText
        }
    }
}

Now call it instead of reloading the collection view

Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { timer in
    print("Starting animatings")
    self.moveText.toggle()
    self.animateVisibleCells()
}

and convert the cellForItemAt implementation to:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionview.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
    cell.moveText = moveText
    return cell
}

Upvotes: 1

Related Questions