Collection view with segment duplicate cells

I have a problem when I use collection view with segmentView.

I have a viewController which has a segmentView with 4 elements ( 0 => All , 1 => Photos , 2 => Audios , 3 => videos ) here's and example :

And I have one collectionView to display the data depend on which category clicked from the segmentController

It's working and displaying the data and here's my collectionView methods to display data

 // MARK: Datasource collection method

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // All Items

        if segmentStatues == 0
        {
            let cellForAll = collectionView.dequeueReusableCell(withReuseIdentifier: "AllItemsCell", for: indexPath) as! AllItemsCollectionViewCell

            // reset elements before declare

            cellForAll.cellImage.image = nil
            cellForAll.playBtn.isHidden = true

            cellForAll.layer.borderWidth = 1
            cellForAll.layer.borderColor = UIColor(red:0.93, green:0.93, blue:0.93, alpha:1.0).cgColor
            // When type is an image
            if sociaPosts[(indexPath as NSIndexPath).row].postType == "image"
            {

                cellForAll.cellImage.sd_setImage(with: URL(string: sociaPosts[(indexPath as NSIndexPath).row].postUrl))


            }else if sociaPosts[(indexPath as NSIndexPath).row].postType == "audio"
            {

                cellForAll.cellImage.image  = UIImage(named: "recorde_icon")


            }else if sociaPosts[(indexPath as NSIndexPath).row].postType == "video"
            {
                cellForAll.cellImage.sd_setImage(with: URL(string: sociaPosts[(indexPath as NSIndexPath).row].thumbURL))
                cellForAll.playBtn.isHidden = false
            }

            return cellForAll

        }else{

            // Cell if Images or videos or audios

            let newCell = collectionView.dequeueReusableCell(withReuseIdentifier: "beepbeep", for: indexPath) as! userProfileImageCollectionViewCell

            // If the type is images

            if  segmentStatues == 1
            {   
                // Set image URL

                newCell.cellImage.sd_setImage(with: URL(string: imagesPosts[(indexPath as NSIndexPath).row].postUrl))

                // Get image likes and comments and shares

                newCell.likeCount.text = String(imagesPosts[(indexPath as NSIndexPath).row].likes_count)
                newCell.commentCount.text = String(imagesPosts[(indexPath as NSIndexPath).row].comments_count)
                newCell.shareCount.text = String(imagesPosts[(indexPath as NSIndexPath).row].shares_count)

            } else if segmentStatues == 3
            {
                // For Video player

             var player = newCell.customVid
            player = Bundle.main.loadNibNamed("VieoPlayer", owner: self, options: nil)?.last as? videoPlayer

           player?.newIntitVideoPlayer(forViewController: self, videoURL: videosPosts[indexPath.row].postUrl , videoThumbnail: URL(string: videosPosts[indexPath.row].thumbURL), onReady: nil)
                // Set video URL and Thumbnail

                // Get video likes and comments and shares
                newCell.likeCount.text = String(videosPosts[(indexPath as NSIndexPath).row].likes_count)
                newCell.commentCount.text = String(videosPosts[(indexPath as NSIndexPath).row].comments_count)
                newCell.shareCount.text = String(videosPosts[(indexPath as NSIndexPath).row].shares_count)

            }else if segmentStatues == 2
            {
                // Audio player
                let ad = Bundle.main.loadNibNamed("AudioPlayerView", owner: self, options: nil)?.last as! AudioPlayerView
                ad.frame = CGRect(x: (newCell.contentView.frame.size.width - newCell.contentView.frame.size.height * 0.6) / 2, y: 20 , width: newCell.contentView.frame.size.height * 0.6, height: newCell.contentView.frame.size.height * 0.6)
                ad.playImageView.image = nil

                ad.tag = 6666

                newCell.contentView.addSubview(ad)

                // Set audio URL
                ad.audioPath = URL(string: audiosPosts[indexPath.row].postUrl)
                ad.viewInitialization()

                // Get Audio  likes and comments and shares
                newCell.likeCount.text = String(audiosPosts[(indexPath as NSIndexPath).row].likes_count)
                newCell.commentCount.text = String(audiosPosts[(indexPath as NSIndexPath).row].comments_count)
                newCell.shareCount.text = String(audiosPosts[(indexPath as NSIndexPath).row].shares_count)   

            }
            return newCell
        } 
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        if self.segmentStatues == 0
        {

            return  sociaPosts.count
        }else if self.segmentStatues == 1
        {
            return imagesPosts.count
        }else if self.segmentStatues == 2
        {
            return audiosPosts.count
        }else
        {
            return videosPosts.count
        }
    }

I have 2 custom cells in my collection view one for all items and the other for (images - audios - videos ) view

my problem is data is displaying but cells duplicated when i change between segment in userProfileImageCollectionViewCell

these 2 pictures show the issue

Displaying all audios cell when i click in audio segmnet

But after I click in image or video segment cell duplicated like this

it's happen in audio - images - videos only**(userProfileImageCollectionViewCell)**

and finally here's my userProfileImageCollectionViewCell code :

updateded :

import UIKit
import SDWebImage
class userProfileImageCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var customVid: videoPlayer!
    @IBOutlet weak var cellImage: UIImageView!

    @IBOutlet weak var likeButton: UIButton!

    @IBOutlet weak var likeCount: UILabel!
    @IBOutlet weak var commentButton: UIButton!

    @IBOutlet weak var commentCount: UILabel!
    @IBOutlet weak var shareButton: UIButton!
    @IBOutlet weak var shareCount: UILabel!

    override func prepareForReuse() {
        cellImage.sd_setImage(with: nil)
       self.customVid.subviews.forEach({ $0.removeFromSuperview() })

    }
}

Answers


The issue is that UICollectionViewCell (as well as UITableViewCell by the way) are reused.

To put it simply : When a cell disappear from screen when you scroll, it may come back at the top/bottom (according to the scroll direction). But it's not a new "clean" cell, it's the previous one. That's the reuse system.

So, when you did: newCell.contentView.addSubview(ad) you added each times a new subview without checking if there was one already. The subview where juste piling. It's easy to check it with the 3D Hierarchy Debug of XCode.

Since you have a userProfileImageCollectionViewCell file, the best solution is to use prepareForReuse(). Create a IBOutlet (let's call it customVid) that will add as its subview the ad/player. In prepareForReuse() remove all the subview of customVid. In collectionView:cellForRowAtIndexPath:, do something like that:

var player = Bundle.main.loadNibNamed("VieoPlayer", owner: self, options: nil)?.last as? videoPlayer 
player?.newIntitVideoPlayer(forViewController: self, videoURL: videosPosts 
newCell.customVid.addSubview(player)

Replicate the same mechanism for the ad (you can use the same subview, it's up to you).

Now, a few suggestions not related to your issue:

Name your class starting with an uppercase: userProfileImageCollectionViewCell => UserProfileImageCollectionViewCell

I'd create methods in userProfileImageCollectionViewCell to make your code more readable. I don't speak Swift, so my next lines of code may not compile, but you should get the idea:

func fillWithVideo(videoParam video: CustomVideoClass) {
     var player = newCell.customVid
     player = Bundle.main.loadNibNamed("VieoPlayer", owner: self, options: nil)?.last as? videoPlayer
     player?.newIntitVideoPlayer(forViewController: NOTself, videoURL: video.postUrl, videoThumbnail: URL(string: video.thumbURL), onReady: nil)
    self.customVid.addSubview(player)
    // Set video URL and Thumbnail

    // Get video likes and comments and shares
     self.likeCount.text = String(video.likes_count)
     self.commentCount.text = String(video.comments_count)
     self.shareCount.text = String(video.shares_count)
}

In collectionView:cellForRowAtIndexPath:

//If it's the correct section
let video = videosPosts[(indexPath as NSIndexPath).row] as CustomVideoClass
cell fillWithVideo(video)

Note that it seems that you use the UIViewController, so you may want to add the UIViewController parameter in order to get the newIntitVideoPlayer() method to work, but I didn't want to put it in the previous example to keep it more simple. You may need to replace the lines:

cell fillWithVideo(video andViewController:self)

and

func fillWithVideo(videoParam video: CustomVideoClass andViewController viewController: UIViewController)
player?.newIntitVideoPlayer(forViewController: viewController, videoURL: video.postUrl, videoThumbnail: URL(string: video.thumbURL), onReady: nil)

Etc. for each part specific part. It make just easier to read what you are doing. Handle only in collectionView:cellForRowAtIndexPath: the logic (which section should get which item, etc.) and the rest to the cell that may adapt.


Need Your Help

Button onclick only works once

php jquery laravel

I have this html code for "Comentário" section:

Which control to use for quick text input (inputbox)?

c# .net winforms messagebox inputbox

I need a quick text input dialog box (MessageBox with a single text box in it). Is there any control available or should I use a form?