Animation in UICollectionViewCell Stops When Reloading Array Data

22 views Asked by At

When using a UICollectionViewCell with animation triggered by a gesture recognizer, the animation stops abruptly when the array data associated with the collection view is reloaded using reloadData() method. The UICollectionViewCell contains a UILabel with an animation that scales the cell upon a long-press gesture. However, when reloadData() is called to update the data in the collection view, the animation of the UICollectionViewCell resets to its initial state, causing it to stop abruptly. This behavior disrupts the user experience and prevents the animation from completing smoothly as intended. The challenge is to find a solution that allows the animation to continue seamlessly even after the data is reloaded, ensuring a smooth and uninterrupted user interaction.

Horizontal Cell of UICollectionView:

final class HorizontalCell: UICollectionViewCell {
    static let reuseIdentifier = "HorizontalCollectionViewCell"
    
    private let numberLabel = UILabel()
    private var originalTransform: CGAffineTransform?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
        setupGestureRecognizers()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        numberLabel.text = nil
    }
    
    private func setupViews() {
        contentView.addSubview(numberLabel)
        numberLabel.textAlignment = .center
        numberLabel.layer.cornerRadius = 10
        numberLabel.layer.borderWidth = 3
        numberLabel.layer.borderColor = UIColor.lightGray.cgColor
        numberLabel.backgroundColor = .white
        numberLabel.textColor = .black
        numberLabel.layer.masksToBounds = true
        configure(with: item)
        
        numberLabel.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(10)
        }
    }
    
    var item: Int! = 0 {
        didSet {
            configure(with: item)
        }
    }
    
    private func configure(with number: Int) {
        numberLabel.text = "\(number)"
    }
    
    private func setupGestureRecognizers() {
        let tapGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        tapGesture.minimumPressDuration = 0
        tapGesture.delegate = self
        addGestureRecognizer(tapGesture)
    }
    
    @objc private func handleTap(_ gesture: UILongPressGestureRecognizer) {
        switch gesture.state {
        case .began:
            DispatchQueue.global(qos: .userInteractive).async {
                let animation = CABasicAnimation(keyPath: "transform.scale")
                animation.duration = 0.1
                animation.fromValue = 1.0
                animation.toValue = 0.8
                animation.isRemovedOnCompletion = false
                animation.fillMode = .forwards
                
                CATransaction.begin()
                CATransaction.setCompletionBlock {
                    self.transform = self.transform.scaledBy(x: 0.8, y: 0.8)
                }
                
                self.layer.add(animation, forKey: nil)
                CATransaction.commit()
            }
        case .ended, .cancelled:
            DispatchQueue.global(qos: .userInteractive).async {
                let animation = CABasicAnimation(keyPath: "transform.scale")
                animation.duration = 0.1
                animation.fromValue = 0.8
                animation.toValue = 1.0
                animation.isRemovedOnCompletion = false
                animation.fillMode = .forwards
                
                CATransaction.begin()
                CATransaction.setCompletionBlock {
                    self.transform = self.originalTransform ?? .identity
                }
                
                self.layer.add(animation, forKey: nil)
                CATransaction.commit()
            }
        default:
            break
        }
    }

}

extension HorizontalCell: UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        true
    }
}

Timer starting method from Update Service class:

private func startTimer() {
        timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateRandomNumber), userInfo: nil, repeats: true)
        let runloop = RunLoop.main
        runloop.add(timer!, forMode: .common)
    }
    
    @objc private func updateRandomNumber() {
        for index in 0..<verticalItems.count {
            var updatedHorizontalItems = verticalItems[index].horizontalItems
            if let randomIndex = updatedHorizontalItems.indices.randomElement() {
                updatedHorizontalItems[randomIndex] = Int.random(in: 1...100)
            }
            verticalItems[index].horizontalItems = updatedHorizontalItems
        }
    }

HorizontalCell is stored into UITableViewCell, which sets a new item for HorizontalCell and reloads a UICollectionView in didSet { }. UITableView reloads in UIViewController method when gets any changes from array in UpdateService.

0

There are 0 answers