WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: UICollectionViewCell UI bugs

Forums > iOS

Hey guys. Cut to the chase. Trynna create some cells in my CollectionView. Cells will populate with the object of type Tweet. Some Tweet(s) has urlToExpand, some has value nil. Based on that, button "See more" will be visible or nah. I am certain that the data is passed correctly into Cell as I have checked it with the print. However button works randomly. Sometimes it does display, sometimes it doesn't. I have no idea why.

Here is my dataSource update in VC

 private func configureDataSource() {
        dataSource = UICollectionViewDiffableDataSource<Section, Tweet>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, tweet) -> UICollectionViewCell? in
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SearchTweetsCell.reuseId, for: indexPath) as! SearchTweetsCell

            cell.set(with: tweet, user: self.user)
            cell.delegateSafari = self

            return cell
        })

        dataSource.supplementaryViewProvider = { (collectionView, kind, indexPath) in
            let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: SearchTweetsVCCollectionHeader.reuseId, for: indexPath) as! SearchTweetsVCCollectionHeader
            header.set(with: self.user)
            return header
        }
    }

Here is the function from collectionViewCell that I am calling in my cv while populating cells.

func set(with usersTweet: Tweet, user: User) {
        self.user                               = user
        tweet                                   = usersTweet
        urlString                               = usersTweet.urlToExpandWithSafari
        tweetBodyLabel.text                     = usersTweet.tweetText
        timeDateLabel.text                      = usersTweet.createdAt.formatToTwitterPostDate()

        sharesView.set(itemInfoType: .shares,   with: usersTweet.retweetCounter.convertToKMFormatStr())
        likesView.set(itemInfoType: .likes,     with: usersTweet.likesCounter.convertToKMFormatStr())

        guard tweet.urlToExpandWithSafari != nil else {
            DispatchQueue.main.async { self.goSafariButton.removeFromSuperview() }
            goSafariButton.isEnabled = false
            return
        }
        DispatchQueue.main.async { self.goSafariButton.setTitle(TweetStrings.seeFull, for: .normal) }
    }

Many thanks for any help.

1      

Hi,

you sure the self.goSafariButton.removeFromSuperview() is needed? CollectionView recycles the cells, so if you once remove the button, it will not be present if the cell is recycled unless you add it again. I would use isHidden property instead.

1      

Hey Filip, thanks I ve changed that to isHidden property. But it did not change that strange behaviour. Any other suggestions? Many thanks

1      

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

My guess is that you need to setup/reset your cell properly in the prepareForReuse method of your custom cell class. There you have you make sure the button is added on reuse when needed. And I agree with Filip, I would hide / show the button, instead of removing it all together!

    override func prepareForReuse() {
        //Do all setup necessary to start with a fresh cell

}

Hope this helps!

2      

Hope this best budget shotgun microphone will help.

1      

Hey guys, thanks for help!

Gakkienl, do you mean that?

 override func prepareForReuse() {
        guard favoriteTweet.urlToExpandWithSafari != nil else {
            DispatchQueue.main.async { self.goSafariButton.isHidden = true }
            goSafariButton.isEnabled = false
            return
        }
        DispatchQueue.main.async { self.goSafariButton.setTitle(TweetStrings.seeFull, for: .normal) }
    }

Sadly cell still behaves the same way. Buttons do not follow conditional as they suppose to. Or am I doing it wrong?

1      

I would lose the dispatchQueue ...

1      

Yes, my vote is also for removing the DispatchQueue. Is there a reason you have it?

Because with this async I think it is possible that this code gets called after prepareForReuse and configure have already been called.

1      

Hey lads,

Not sure actually why I had it. Before, the code was placed in a function set(...) that was called from the VC where the collectionView and the dataSource were configured. I thought it may be not dispatched from the main queue... So it is then by the default?

I lost Dispatch Queue but the cells behave same way as they did..

 override func prepareForReuse() {
        guard tweet.urlToExpandWithSafari != nil else {
            self.goSafariButton.isHidden = true
            goSafariButton.isEnabled = false
            return
        }
        self.goSafariButton.setTitle(TweetStrings.seeFull, for: .normal)
    }

Any suggestions?

1      

Yes, I somehow missed it previously. In prepareForReuse you should reset the state of the cell tomthe default and not use the stored model, because it will get overwritten by new model in the configure method.

1      

Can you elaborate? I am not sure if I understand :(

1      

Sure. The prepareForReuse method is used as a sort of cleanup. This is called when ColletionView is preparing existing cell for reuse with the new data. And the actual setup based on the data model is in your set method.

Generally if you have for example UILabel that is not shown all the time, you will decide (based on the model) if it should be visible or not and do this isHidden configuration in your set method. And to not have to write else case that would either shown/hidden the UILabel. You do this "reset" in the prepareForReuse.

Suppose you had this code:

if shouldDisplayLabel {
    myLabel.text = "Hello"
} else {
   myLabel.isHidden = true
}

Once your code would hit the else block then once the cell would get recycled for new data, you would not see the myLabel even if the shouldDisplayLabel was true. Because the label is already hidden :-)

So the pattern is that in your prepareForReuse method you set the myLabel.isHidden = false to automatically "reset" it and then the condition will work for the next model just fine.

You can also just set isHidden to false in the if block in this example, but then you configuration method gets a more complicated.

2      

Worked like a dream!

For interested here is the solution:

Function set in collectionViewReusableCell, called when configuring data source from VC:

 func set(with usersTweet: Tweet, user: User) {
        self.user                               = user
        tweet                                   = usersTweet
        urlString                               = usersTweet.urlToExpandWithSafari
        tweetBodyLabel.text                     = usersTweet.tweetText
        timeDateLabel.text                      = usersTweet.createdAt.formatToTwitterPostDate()

        sharesView.set(itemInfoType: .shares,   with: usersTweet.retweetCounter.convertToKMFormatStr())
        likesView.set(itemInfoType: .likes,     with: usersTweet.likesCounter.convertToKMFormatStr())

        if tweet.urlToExpandWithSafari == nil { goSafariButton.isHidden = true }
        self.goSafariButton.setTitle(TweetStrings.seeFull, for: .normal)
        print(usersTweet)
    }

Function prepareForReuse from collectionViewReusableCell:

override func prepareForReuse() {
        self.goSafariButton.isHidden = false
    }

1      

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.