Search code examples
swiftuicollectionviewcell

Can't get custom cell in CollectionView to work properly


I created a custom cell for my CollectionView, however, I got problems with displaying the cell as I want. This is the code for the custom cell:

import Foundation
import UIKit

class CardCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupCell()
        setupViewsInCell()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupCell()
        setupViewsInCell()
    }

    var postTitle: UILabel = {
        let label = UILabel()
        label.text = "POST TITLE"
        label.font = UIFont.boldSystemFont(ofSize: 14)
        label.textColor = UIColor.appColors.mainBlack
        label.translatesAutoresizingMaskIntoConstraints = false

        return label
    }()

    //postImage need to be the image the user uploaded.
    var postImage: UIImageView = {
        let image = UIImageView(image: UIImage(named: "placeholder-image"))
        image.contentMode = .scaleAspectFit
        image.clipsToBounds = true
        image.translatesAutoresizingMaskIntoConstraints = false

        return image
    }()

    var likeImage: UIImageView = {
        let image = UIImageView(image: UIImage(named: "like"))
        image.contentMode = .scaleAspectFit
        image.clipsToBounds = true
        image.translatesAutoresizingMaskIntoConstraints = false

        return image
    }()

    var numberOfLikes: UILabel = {
        let label = UILabel()
        label.text = "20"
        label.font = UIFont.systemFont(ofSize: 8)
        label.textColor = UIColor.appColors.mainBlack
        label.translatesAutoresizingMaskIntoConstraints = false

        return label
    }()

    var dislikeImage: UIImageView = {
        let image = UIImageView(image: UIImage(named: "dislike"))
        image.contentMode = .scaleAspectFit
        image.clipsToBounds = true
        image.translatesAutoresizingMaskIntoConstraints = false

        return image
    }()

    var numberOfDislikes: UILabel = {
        let label = UILabel()
        label.text = "34"
        label.font = UIFont.systemFont(ofSize: 8)
        label.textColor = UIColor.appColors.mainBlack
        label.translatesAutoresizingMaskIntoConstraints = false

        return label
    }()

    func setupCell(){
        backgroundColor = UIColor.appColors.mainWhite
        layer.cornerRadius = 10
        clipsToBounds = true
        layer.shadowColor = UIColor(white: 0, alpha: 0.25).cgColor
        layer.shadowOffset = CGSize.zero
        layer.shadowOpacity = 1
        layer.shadowRadius = 2
        layer.masksToBounds = false
    }

    func setupViewsInCell(){

        let stackView = UIStackView(arrangedSubviews: [likeImage, numberOfLikes, dislikeImage, numberOfDislikes])
        stackView.axis = .horizontal
        stackView.distribution = .fillEqually
        stackView.translatesAutoresizingMaskIntoConstraints = false

        self.addSubview(postTitle)
        self.addSubview(postImage)
        self.addSubview(stackView)

        NSLayoutConstraint.activate([
            postTitle.topAnchor.constraint(equalTo: self.topAnchor, constant: 0),
            postTitle.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8),
            postImage.topAnchor.constraint(equalTo: postTitle.bottomAnchor, constant: 10),
            postImage.leftAnchor.constraint(equalTo: self.leftAnchor),
            postImage.rightAnchor.constraint(equalTo: self.rightAnchor),
            postImage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 30),
            stackView.topAnchor.constraint(equalTo: postImage.bottomAnchor, constant: 50),
            stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0),
            stackView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 8),
            stackView.heightAnchor.constraint(equalToConstant: 40),
            stackView.widthAnchor.constraint(equalToConstant: self.frame.width / 2),
        ])
    }
}

The few problems I have, and couldn't find solution for are this:

  1. The title isn't even showing, I tried to change the constant few times, but still nothing.
  2. constraits problem, I set the stackView topAnchor to the postImage bottomAnchor, but it still won't work. This is a screenshot from the simulator.
  3. as you can see in the screenshot below, the collection view is off to the side for some reason. I'm guessing it is a collectionView problem and not a cell problem, but I couldn't find a solution for this neither.

enter image description here


Solution

    1. Change this constant to negative postImage.topAnchor.constraint(equalTo: postTitle.bottomAnchor, constant: 10), when you use bottomAnchor or rightAnchor the constant need to be negative. If you want to understand more, see Bottom and Right constraints of view are negative?

    2. The same problem with the constraint constant stackView.topAnchor.constraint(equalTo: postImage.bottomAnchor, constant: 50), try to change to -50

    3. If you want to create Collection View with dynamic cell height, you need to calculate the cell size. You can do this by creating your custom view layout, see https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest this is a good tutorial to create a custom UICollectionViewLayout, or you can calculate the cell size inside the method func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize using the systemLayoutSizeFitting, if all constraints are set right, this method will return the size of the cell, but you need to remember that this function will be called every time a cell appears, a solution to this is save the height and check if a content was already used to calculate the height of a cell. If you need to change the height of the cell in another time, you need to update the saved value as well or delete it to calculate the size again.

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 50)
            let dummyCell = Cell(frame: frame)
            let targetSize = CGSize(width: view.frame.width, height: 1000)
            let dummyData = data[indexPath.row]
            dummyCell.data = dummyData
            dummyCell.layoutIfNeeded()
            let height = max(MINIMUM_CELL_SIZE, 
             dummyCell.systemLayoutSizeFitting(targetSize).height)
            return CGSize(width: view.frame.width, height: height)
        }