简体   繁体   中英

How to reload custom UICollectionViewCell`s with custom UIViews (and transitions inside)

Hello all good people,

I am looking for some hint in terms of reloading data in my custom cells based on the indexPath row.

The context:

  • I have a tutorial slideshow, for which I am using UICollectionView . Into this view I insert custom UICollectionViewCell s, for each indexPath.row different custom one (so I register multiple nibs for UICollectionView ).

  • Within delegate method cellForItemAt indexPath based on the indexPath.row I dequeue particular custom cell I want

  • The tutorial slideshow has buttons with which you can navigate between specific cells (btw cells take most of the user screen), so you can navigate like Next or Back.

  • Each cell within its own custom class is defined with some transitions. So for example cell number 1 starts with some custom elements then after second or so it transits to another sets of elements. So I am creating something like animations. These transitions are performed via DispatchQueue.main.asyncAfter so I let the user to read the first screen I want, then second. Then user clicks the button Next and goes for another indexPath.row with sometimes another transitions within this next "screen"

  • So my intention is to have "clear" default view when the user for example navigates between the cells( indexPath.row /s) by clicking Next/Back, so when he goes from Screen2 to Screen1, I want him to see the Screen1 from beginning and see the transitions again. Now it ends up at the last screen which I transit to.

My actuall Question:

So is there any way how to refresh the cell with the current design or should I change the way how I structured my collection view?

Please see some example code snippets below.

1. VC with my UICollectionView

class IncomingInstructionsVC: UIViewController {

@IBOutlet weak var btnBack: UIButton!
@IBOutlet weak var btnNext: UIButton!
@IBOutlet weak var collectionView: UICollectionView!


override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.register(Screen1.nib, forCellWithReuseIdentifier: "Screen1ID")
    collectionView.register(Screen2.nib, forCellWithReuseIdentifier: "Screen2ID")
    collectionView.register(Screen3.nib, forCellWithReuseIdentifier: "Screen3ID")
    collectionView.register(Screen4.nib, forCellWithReuseIdentifier: "Screen4ID")
    collectionView.register(Screen5.nib, forCellWithReuseIdentifier: "Screen5ID")
    collectionView.register(Screen6.nib, forCellWithReuseIdentifier: "Screen6ID")
    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.isPagingEnabled = false

}

2. IBActions for navigation between UICollectionViewCell/s

@IBAction func btnNextTapped(_ sender: UIButton) {
    let visibleItems: NSArray = self.collectionView.indexPathsForVisibleItems as NSArray
    
    var minItem: NSIndexPath = visibleItems.object(at: 0) as! NSIndexPath
    for itr in visibleItems {
        if minItem.row > (itr as AnyObject).row {
            minItem = itr as! NSIndexPath
        }
    }
    let nextItem = IndexPath(row: minItem.row + 1, section: 0)
    self.collectionView.scrollToItem(at: nextItem as IndexPath, at: .left, animated: false)

}
@IBAction func btnBackTapped(_ sender: UIButton) {
    let visibleItems: NSArray = self.collectionView.indexPathsForVisibleItems as NSArray
    
    var minItem: NSIndexPath = visibleItems.object(at: 0) as! NSIndexPath
    for itr in visibleItems {
        
        if minItem.row > (itr as AnyObject).row {
            minItem = itr as! NSIndexPath
        }
    }
    let nextItem = IndexPath(row: minItem.row - 1, section: 0)
    self.collectionView.scrollToItem(at: nextItem as IndexPath, at: .left, animated: false)
}

3. Delegate method cellForItemAt indexPath:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    var cell = UICollectionViewCell()
    self.pageControl.currentPage = indexPath.row
    
    switch indexPath.row {
    case 0:
        cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Screen1ID", for: indexPath) as! Screen1
    case 1:
        cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Screen2ID", for: indexPath) as! Screen2
    case 2:
        cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Screen3ID", for: indexPath) as! Screen3
.
.
.
continues to the end of my tutorial
.
.
.
    }
    return cell
}

4. Custom Cell Class definiton with DispatchQueue.main.asyncAfter logic

.
.
.
Some IBOutlets here....
.
.
.

override func awakeFromNib() {
        super.awakeFromNib()
        commonInit()
    }
    
    func commonInit() {
        greyBackground.layer.cornerRadius = 10
        transition1.layer.cornerRadius = 10
        transition2.layer.cornerRadius = 10
        
        self.nameIncomingLabel.text = NSLocalizedString("name_inc_head", comment: "")
        self.mobileLabel.text = NSLocalizedString("mobile", comment: "")
        self.declineImageView.image = UIImage(named: "Reject")
        self.declineLabel.text = NSLocalizedString("Decline", comment: "")
        
        self.acceptImageView.image = UIImage(named: "Accept")
        self.acceptLabel.text = NSLocalizedString("accept", comment: "")
        
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in
            //transition to "Accept Call" command
            UIView.transition(with: transition1, duration: 1, options: .transitionCrossDissolve, animations: {
                transition1.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.9)
                
                transition1AcceptImage.image = UIImage(named: "Accept")
                transition1Label.font = UIFont.systemFont(ofSize: 21, weight: .semibold)
                transition1Label.text = NSLocalizedString("answer_incoming_call", comment: "")
                
                //transition to calling screen with icons
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [self] in
                    UIView.transition(with: transition2, duration: 0.5, options: .transitionCrossDissolve, animations: {
                        transition2.backgroundColor = #colorLiteral(red: 0.3019607843, green: 0.3019607843, blue: 0.3019607843, alpha: 1)
                        nameIncomingLabelTransition2.text = NSLocalizedString("name_inc_head", comment: "")


....it is continuing with the closing curly braces down below...

The intention of dequeueing reusable cells is to avoid the multiple creation of cells which could faster be reused by the container view (like table view or collection view). The container view caches a lot of cells that go off-screen, so you get back that cell once it becomes visible again. Since your animations are implemented in commonInit - which is only called by awakeFromNib - you only see them once, because the view is loaded only once into memory.

You need call commonInit manually after a cell has been dequeued, and not call it by awakeFromNib . Or better: Separate the only once stuff from the animation / content initialization part, and call the first from awakeFromNib , the latter once the view get's dequeued.

This is even better because awakeFromNib is called even before the view get's visible, hence you don't even know when it is being displayed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM