ios – How you can repair animation hitch in assortment view?

ios – How you can repair animation hitch in assortment view?


I’ve a set view. Once I contact the cell, the animation from the gathering view cell class CarouselCell enjoying. Once I press on a cell, the method of downloading the file begins within the technique didSelectItemAt. To replace the method I take advantage of the code from right here reloadItem(indexPath: IndexPath) The issue is that if whereas downloading a file I attempt to contact a cell, the animation will play as soon as, like a soar or hitch, after which it received’t work in any respect when touched till the file is downloaded. Additionally, throughout downloading, when the cell is clicked once more, the strategy didSelectItemAt is not going to be referred to as till the file is downloaded. The truth that the didSelectItemAt technique just isn’t referred to as throughout file downloading is sweet. This prevents the identical file from being downloaded a number of occasions. However the issue is that I don’t perceive why this occurs as a result of I haven’t written such code wherever. How you can repair the animation and why didSelectItemAt not referred to as throughout the file is downloading?

code:

I eliminated all of the obtain associated code to save lots of house in query and changed it with a timer to simulate the file obtain progress

assortment:

class CarouselController: UIViewController, UICollectionViewDelegate {
        
    var progress = 0.0
    var collectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource<Part, Merchandise>?
    let sections = Bundle.primary.decode([Section].self, from: "carouselData.json")
    var objects = [DownloadItem]()
    
    override func viewDidLoad() {
        tremendous.viewDidLoad()
        
        setupCollectionView()
        setupScrollView()
        setupDownloadItems()
    }
    
    func setupScrollView() {
        collectionView.scrollToItem(at: IndexPath(merchandise: 0, part: 0), at: .centeredHorizontally, animated: false)
    }

    func setupDownloadItems() {
        let rely = dataSource!.snapshot().numberOfItems
        for index in 0...rely { objects.append(DownloadItem(merchandise: index)) }
    }
        
    func createDataSource() {
        dataSource = UICollectionViewDiffableDataSource<Part, Merchandise>(collectionView: collectionView) { collectionView, indexPath, merchandise in
            change self.sections[indexPath.section].identifier {
            case "carouselCell":
                let cell = self.configure(CarouselCell.self, with: merchandise, for: indexPath)
                if indexPath.row < self.objects.rely {
                    let merchandise = self.objects[indexPath.row]
                    cell.title.textual content = "(String(format: "%.f%%", merchandise.progress * 100))"
                }
                return cell
            default: return self.configure(CarouselCell.self, with: merchandise, for: indexPath)
            }
        }
    }
    
    func configure<T: SelfConfiguringCell>(_ cellType: T.Kind, with merchandise: Merchandise, for indexPath: IndexPath) -> T {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else { fatalError("(cellType)") }
        cell.configure(with: merchandise)
        return cell
    }
    
    func reloadData() {
        var snapshot = NSDiffableDataSourceSnapshot<Part, Merchandise>()
        snapshot.appendSections(sections)
        for part in sections { snapshot.appendItems(part.merchandise, toSection: part) }
        dataSource?.apply(snapshot)
    }
        
    func setupCollectionView() {
        collectionView = UICollectionView(body: view.bounds, collectionViewLayout: createCompositionalLayout())
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.isScrollEnabled = false
        collectionView.delegate = self
        collectionView.contentInsetAdjustmentBehavior = .by no means
        view.addSubview(collectionView)
        collectionView.register(CarouselCell.self, forCellWithReuseIdentifier: CarouselCell.reuseIdentifier)
        createDataSource()
        reloadData()
    }
    
    func createCompositionalLayout() -> UICollectionViewLayout {
        UICollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
            
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                                  heightDimension: .fractionalHeight(1))
            let merchandise = NSCollectionLayoutItem(layoutSize: itemSize)
                        
            let groupWidth = (layoutEnvironment.container.contentSize.width * 1.05)/3
            let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(groupWidth),
                                                   heightDimension: .absolute(groupWidth))
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
                        
            let part = NSCollectionLayoutSection(group: group)
            part.contentInsets = NSDirectionalEdgeInsets(
                prime: (layoutEnvironment.container.contentSize.peak/2) - (groupWidth/2),
                main: 0,
                backside: 0,
                trailing: 0)
            part.interGroupSpacing = 64
            part.orthogonalScrollingBehavior = .groupPagingCentered
            part.contentInsetsReference = .none
            part.visibleItemsInvalidationHandler = { (objects, offset, atmosphere) in
                
                objects.forEach { merchandise in
                    let distanceFromCenter = abs((merchandise.body.midX - offset.x) - atmosphere.container.contentSize.width / 2.0)
                    let minScale: CGFloat = 0.7
                    let maxScale: CGFloat = 1.1
                    let scale = max(maxScale - (distanceFromCenter / atmosphere.container.contentSize.width), minScale)
                    merchandise.rework = CGAffineTransform(scaleX: scale, y: scale)
                }
            }
            
            return part
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        
        print("-")
        
        _ = Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true) { timer in
            guard self.progress <= 1.0 else {
                timer.invalidate()
                self.progress = 0.0
                return
            }
            self.progress += 0.01
            self.objects[indexPath.row].progress = Float(self.progress)
            self.reloadItem(indexPath: .init(row: indexPath.row, part: 0))
        }
                
    }
    
    func reloadItem(indexPath: IndexPath) {
        guard let needReloadItem = dataSource!.itemIdentifier(for: indexPath) else { return }
        guard var snapshot = dataSource?.snapshot() else { return }
        snapshot.reloadItems([needReloadItem])
        dataSource?.apply(snapshot, animatingDifferences: false)
    }
    
}

cell

class CarouselCell: UICollectionViewCell, SelfConfiguringCell {
        
    static let reuseIdentifier: String = "carouselCell"
    
    var topSpaceView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    var imageView: UIImageView = {
        let picture = UIImageView()
        picture.contentMode = .scaleAspectFill
        picture.translatesAutoresizingMaskIntoConstraints = false
        return picture
    }()
    
    var titleImageView: UIImageView = {
        let picture = UIImageView()
        picture.contentMode = .scaleAspectFill
        picture.translatesAutoresizingMaskIntoConstraints = false
        return picture
    }()
    
    var textView: UIView = {
        let view = UIView()
        view.backgroundColor = .blue
        view.layer.borderColor = UIColor.crimson.cgColor
        view.layer.borderWidth = 3
        view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    var title: UILabel = {
        let label = UILabel()
        label.textColor = .white
        label.textAlignment = .middle
        label.sizeToFit()
        label.font = UIFont(title: "Marker Felt", measurement: 24)
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    var progressView: UIProgressView = {
        let view = UIProgressView(progressViewStyle: .bar)
        view.middle = view.middle
        view.setProgress(0.0, animated: true)
        view.trackTintColor = .clear
        view.tintColor = .white.withAlphaComponent(0.15)
        view.clipsToBounds = true
        view.layer.borderColor = UIColor.crimson.cgColor
        view.layer.borderWidth = 3
        view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    var bottomSpaceView: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    var stackView: UIView = {
        let stack = UIView()
        stack.layer.shadowColor = UIColor.black.cgColor
        stack.layer.shadowOffset = CGSize.zero
        stack.layer.shadowOpacity = 0.6
        stack.layer.shadowRadius = 3
        stack.translatesAutoresizingMaskIntoConstraints = false
        return stack
    }()
    
    override var isHighlighted: Bool {
        didSet { if oldValue == false && isHighlighted { spotlight() }
            else if oldValue == true && !isHighlighted { unHighlight() }
        }
    }
    
    var imageTask: AnyCancellable?
    var titleImageTask: AnyCancellable?
    
    override func prepareForReuse() {
        tremendous.prepareForReuse()
        imageView.picture = nil
        titleImageView.picture = nil
        imageTask?.cancel()
    }
    
    override init(body: CGRect) {
        tremendous.init(body: body)
        setupViews()
        setupConstraints()
    }
    
    required init?(coder: NSCoder) {
        fatalError("error")
    }
    
    func setupViews() {
        stackView.addSubview(topSpaceView)
        stackView.addSubview(imageView)
        stackView.addSubview(titleImageView)
        stackView.addSubview(textView)
        stackView.addSubview(bottomSpaceView)
        
        contentView.addSubview(stackView)
        
        textView.addSubview(progressView)
        textView.addSubview(title)
        
        stackView.sendSubview(toBack: textView)
        textView.bringSubview(toFront: title)
    }
        
    func setupConstraints() {

        stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        stackView.topAnchor.constraint(equalTo: contentView.topAnchor, fixed: 0).isActive = true
        stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, fixed: 0).isActive = true

        topSpaceView.topAnchor.constraint(equalTo: stackView.topAnchor, fixed: 0).isActive = true
        topSpaceView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.07).isActive = true
        topSpaceView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        topSpaceView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true

        imageView.topAnchor.constraint(equalTo: topSpaceView.bottomAnchor, fixed: 0).isActive = true
        imageView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.7).isActive = true
        imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, fixed: 0).isActive = true
        imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, fixed: 0).isActive = true

        titleImageView.topAnchor.constraint(equalTo: topSpaceView.bottomAnchor, fixed: 0).isActive = true
        titleImageView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.7).isActive = true
        titleImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, fixed: 0).isActive = true
        titleImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, fixed: 0).isActive = true

        textView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.16).isActive = true
        textView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, fixed: 24).isActive = true
        textView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, fixed: -24).isActive = true
        textView.topAnchor.constraint(equalTo: imageView.bottomAnchor, fixed: -6).isActive = true
        textView.layer.cornerRadius = contentView.body.measurement.peak*0.16/2

        title.leadingAnchor.constraint(equalTo: textView.leadingAnchor, fixed: 0).isActive = true
        title.trailingAnchor.constraint(equalTo: textView.trailingAnchor, fixed: 0).isActive = true
        title.topAnchor.constraint(equalTo: textView.topAnchor, fixed: 0).isActive = true
        title.bottomAnchor.constraint(equalTo: textView.bottomAnchor, fixed: 0).isActive = true

        progressView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.16).isActive = true
        progressView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, fixed: 24).isActive = true
        progressView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, fixed: -24).isActive = true
        progressView.topAnchor.constraint(equalTo: imageView.bottomAnchor, fixed: -6).isActive = true
        progressView.layer.cornerRadius = contentView.body.measurement.peak*0.16/2

        bottomSpaceView.topAnchor.constraint(equalTo: textView.bottomAnchor, fixed: 0).isActive = true
        bottomSpaceView.heightAnchor.constraint(equalTo: contentView.heightAnchor, multiplier: 0.07).isActive = true
        bottomSpaceView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        bottomSpaceView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
    }
        
    personal func animateScale(to scale: CGFloat, period: TimeInterval) {
        UIView.animate( withDuration: period, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, choices: [.beginFromCurrentState], animations: {
            self.stackView.rework = .init(scaleX: scale, y: scale)
            self.textView.rework = .init(scaleX: scale, y: scale)
        }, completion: nil)
    }
    func spotlight() {
        animateScale(to: 0.9, period: 0.4)
    }
    func unHighlight() {
        animateScale(to: 1, period: 0.38)
    }
        
    func configure(with merchandise: Merchandise) {
        
        title.textual content = merchandise.title
        textView.backgroundColor = .inexperienced
        textView.layer.borderColor = UIColor.inexperienced.cgColor
        progressView.layer.borderColor = UIColor.inexperienced.cgColor
        
        titleImageTask = Future<UIImage?, By no means>() { promise in
            UIImage(named: merchandise.titleImage)?.prepareForDisplay(completionHandler: { loadedImage in
                promise(End result.success(loadedImage))
            })
        }
        .obtain(on: DispatchQueue.primary)
        .sink { picture in
            self.titleImageView.picture = picture
        }
        
        imageTask = Future<UIImage?, By no means>() { promise in
            UIImage(named: merchandise.picture)?.prepareForDisplay(completionHandler: { loadedImage in
                promise(End result.success(loadedImage))
            })
        }
        .obtain(on: DispatchQueue.primary)
        .sink { picture in
            self.imageView.picture = picture
        }
    }
}

Additionally tried utilizing touchesBegan/touchesEnded as a substitute of isHighlighted however the outcome was the identical:

    override func touchesBegan(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        tremendous.touchesBegan(touches, with: occasion)
        spotlight()
    }
        
    override func touchesEnded(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        tremendous.touchesEnded(touches, with: occasion)
        unHighlight()
    }
        
    override func touchesCancelled(_ touches: Set<UITouch>, with occasion: UIEvent?) {
        tremendous.touchesCancelled(touches, with: occasion)
        unHighlight()
    }

extra:

    extension Bundle {
        func decode<T: Decodable>(_ kind: T.Kind, from file: String) -> T {
            guard let url = self.url(forResource: file, withExtension: nil) else {
                fatalError("Did not find (file) in bundle.")
            }
            guard let knowledge = strive? Information(contentsOf: url) else {
                fatalError("Did not load (file) from bundle.")
            }
            let decoder = JSONDecoder()
            guard let loaded = strive? decoder.decode(T.self, from: knowledge) else {
                fatalError("Did not decode (file) from bundle.")
            }
            return loaded
        }
    }
    
    protocol SelfConfiguringCell {
        static var reuseIdentifier: String { get }
        func configure(with merchandise: Merchandise)
    }
    
    public struct Part: Decodable, Hashable {
        let index: Int          
        let identifier: String 
        let title: String       
        let subtitle: String   
        let merchandise: [Item]      
    }
    
    public struct Merchandise: Decodable, Hashable {
        let index: Int               
        let title: String           
        let picture: String           
        let titleImage: String       
        let backgroundColor: String  
        let borderColor: String      
    } 

json

[
    {
        "index": 1,
        "identifier": "carouselCell",
        "title": "",
        "subtitle": "",
        "item": [
            {
                "index": 1,
                "title": "0",
                "image": "cover1",
                "titleImage": "title1",
                "backgroundColor": "5896CC",
                "borderColor": "3274AF",
            },
            {
                "index": 2,
                "title": "0",
                "image": "cover2",
                "titleImage": "title2",
                "backgroundColor": "895138",
                "borderColor": "703A22",
            },
            {
                "index": 3,
                "title": "0",
                "image": "cover3",
                "titleImage": "title3",
                "backgroundColor": "804136",
                "borderColor": "62322A",
            },
            {
                "index": 4,
                "title": "0",
                "image": "cover4",
                "titleImage": "title4",
                "backgroundColor": "3E78A3",
                "borderColor": "2A597D",
            },
        ]
    },
]
    

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.