Home > Enterprise >  Swift custom UICollectionViewCell subViews disappear when imageView image set at ViewController
Swift custom UICollectionViewCell subViews disappear when imageView image set at ViewController

Time:01-13

I faced weird issue while handling UICollectionView

I Created simple custom UICollectionViewCell, which has only one imageView and Label:

There's default placeholder image for Cell's imageView and updating imageView.image from collectionView(_:cellForItemAt:). But When image is set, all subview of cell disappears:

(Cells are not disappear at same time because downloading & setting image is async)

Note: Sample data I used is not wrong (Same data works for TableView in same app)

Why this happens and how can I fix it?

this is Sample data I used:

    let movies = [
        MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955),
        MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955),
        MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955),
        MovieFront(title: "Spider-Man: No Way Home", posterPath: "1g0dhYtq4irTY1GPXvft6k4YLjm.jpg", genre: "Genre", releaseDate: "2021-12-15", ratingScore: 8.4, ratingCount: 3955)
    ]

this is my part of ViewController:


    lazy var collectionView = { () -> UICollectionView in
        
        // FlowLayout
        var flowLayout = UICollectionViewFlowLayout()
        flowLayout.headerReferenceSize = CGSize(width: self.preferredContentSize.width, height: 180)
        flowLayout.sectionInset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
        flowLayout.minimumInteritemSpacing = 20
        flowLayout.minimumLineSpacing = 20

        // Collection View
        var collectionView =  UICollectionView(frame: self.view.frame, collectionViewLayout: flowLayout)
        collectionView.register(DiscoverCollectionViewCell.self, forCellWithReuseIdentifier: identifiers.discover_collection_cell)
        collectionView.register(DiscoverCollectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: identifiers.discover_collection_header)
        collectionView.backgroundColor = UIColor(named: Colors.background)
        
        return collectionView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "Discover"
        
        collectionView.dataSource = self
        collectionView.delegate = self
        
        self.view.backgroundColor = UIColor(named: Colors.background)
    
        self.view.addSubview(collectionView)
        collectionView.snp.makeConstraints { $0.edges.equalTo(self.view.safeAreaLayoutGuide) }
        
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        // Sample Cell
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifiers.discover_collection_cell, for: indexPath) as? DiscoverCollectionViewCell else { return DiscoverCollectionViewCell() }
        
        let movie = movies[indexPath.row]

        cell.movieTitle.text = movie.title
    
        DispatchQueue.global().async {
            guard let imageURL = URL(string: "https://image.tmdb.org/t/p/original/\(movie.posterPath)") else { return }
            guard let imageData = try? Data(contentsOf: imageURL) else { return }
            
            DispatchQueue.main.sync {
                cell.posterImage.image = UIImage(data: imageData)
            }
        }
        return cell
    }

and this is my custom CollectionViewCell, I used Snapkit, Then library:

class DiscoverCollectionViewCell: UICollectionViewCell {
    
    //MARK: Create properties
    lazy var posterImage = UIImageView().then {
        $0.image = UIImage(named: "img_placeholder")
        $0.contentMode = .scaleAspectFit
    }
    
    lazy var movieTitle = UILabel().then {
        $0.font = UIFont.systemFont(ofSize: 15)
        $0.textColor = .white
        $0.numberOfLines = 2
        $0.minimumScaleFactor = 10
    }
        
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // add to view
        self.addSubview(posterImage)
        self.addSubview(movieTitle)
        
        //MARK: Add Constraints
        posterImage.snp.makeConstraints { make in
            make.top.left.right.equalToSuperview()
        }
        
        movieTitle.snp.makeConstraints { make in
            make.top.equalTo(posterImage.snp.bottom).offset(5)
            make.bottom.greaterThanOrEqualToSuperview()
            make.leading.equalTo(posterImage.snp.leading)
            make.trailing.equalTo(posterImage.snp.trailing)
        }
        
        self.backgroundColor = .blue
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

CodePudding user response:

Although I am not sure, Because collection view cells are being reused the init of cells only gets called at the first time, not the time when image data is getting loaded from the server.

Try moving your layout-related code(specifically adding subviews and constraining them) in a different method of the cell and call it every time image gets loaded.

CodePudding user response:

Two issues with your cell's layout...

    // add to view
    self.addSubview(posterImage)
    self.addSubview(movieTitle)
    
    //MARK: Add Constraints
    posterImage.snp.makeConstraints { make in
        make.top.left.right.equalToSuperview()
    }
  1. You should always add UI elements to the cell's .contentView, not to the cell itself.

  2. You did not constrain the bottom of the image view.

     // add to ContentView!
     self.contentView.addSubview(posterImage)
     self.contentView.addSubview(movieTitle)
    
     //MARK: Add Constraints
     posterImage.snp.makeConstraints { make in
         make.top.left.right.bottom.equalToSuperview()
     }
    
  •  Tags:  
  • Related