-1

I am trying to build my tableview with custom UITableViewCell but facing some issues in coinstraints. Following is my custom UITablviewCell class:

class MovieCell: UITableViewCell {
static let identifier = "MovieCell"
let cellMargin = 15.0

private lazy var containerView: UIView = {
   let view = UIView()
    view.backgroundColor = .lightGray
    view.layer.cornerRadius = 5
    view.translatesAutoresizingMaskIntoConstraints = false
    view.clipsToBounds = true
    return view
}()

private let movieNameLabel: UILabel = {
   let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.font = UIFont.movieAppBoldFont(size: 15)
    label.numberOfLines = 0
    return label
}()

private let movieImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true
    return imageView
}()

var movieCellViewModel : MoviewCellViewModel = MoviewCellViewModel(image: nil, name: "") {
    didSet {
        movieNameLabel.text = movieCellViewModel.name
    }
}

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0)
    layoutSubView()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

private func layoutSubView() {
    addSubview(containerView)
    containerView.addSubview(movieNameLabel)
    containerView.addSubview(movieImageView)
    
    let marginGuide = containerView.layoutMarginsGuide
    
    NSLayoutConstraint.activate([
        containerView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
        containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
        containerView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
        containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin),
        
        movieImageView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor, constant: 5),
        movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
        movieImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
        movieImageView.widthAnchor.constraint(equalToConstant: 50),
        movieImageView.heightAnchor.constraint(equalToConstant: 50),

        movieNameLabel.leadingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 5),
        movieNameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: -5),
        movieNameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
        movieNameLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5)
    ])
}

}

I get the following result with the above code: enter image description here

I am trying to create a UIView and add content to it. I am adding it because I want to show some empty space as a separator in tableview.

1 Answer 1

1

Couple issues...

Your first line in layoutSubView() is:

addSubview(containerView)

where is needs to be:

contentView.addSubview(containerView)

Second, you have movieImageView constrained to contentView:

movieImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),

where it should be constrained to marginGuide:

movieImageView.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5),

Third, you have movieNameLabel.leadingAnchor constrained to marginGuide.trailingAnchor:

movieNameLabel.leadingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 5),

where it should be constrained to movieImageView.trailingAnchor:

movieNameLabel.leadingAnchor.constraint(equalTo: movieImageView.trailingAnchor, constant: 5),

Making those changes will give you this (I set image view background to blue, and label background to cyan):

enter image description here

However, when you run the app, you'll see lots of Unable to simultaneously satisfy constraints. messages. This is common when using subviews with subviews in cells.

To get rid of the auto-layout complaints, we can give the containerView bottom anchor a less-than-required priority:

let bottomC = containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin)
bottomC.priority = .required - 1

and then activate that in your constraints block:

//containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin),
bottomC,

Here's your complete MovieCell class with those changes:

class MovieCell: UITableViewCell {
    static let identifier = "MovieCell"
    let cellMargin = 15.0
    
    private lazy var containerView: UIView = {
        let view = UIView()
        view.backgroundColor = .lightGray
        view.layer.cornerRadius = 5
        view.translatesAutoresizingMaskIntoConstraints = false
        view.clipsToBounds = true
        return view
    }()
    
    private let movieNameLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = .systemFont(ofSize: 15, weight: .bold) // UIFont.movieAppBoldFont(size: 15)
        label.numberOfLines = 0
        return label
    }()
    
    private let movieImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()
    
    var movieCellViewModel : MoviewCellViewModel = MoviewCellViewModel(image: nil, name: "") {
        didSet {
            movieNameLabel.text = movieCellViewModel.name
        }
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0)
        layoutSubView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func layoutSubView() {
        contentView.addSubview(containerView)
        containerView.addSubview(movieNameLabel)
        containerView.addSubview(movieImageView)
        
        let marginGuide = containerView.layoutMarginsGuide
    
        let bottomC = containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin)
        bottomC.priority = .required - 1

        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
            containerView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
            containerView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
//          containerView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: -cellMargin),
            bottomC,
            
            movieImageView.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor, constant: 5),
            movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
//          movieImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5),
            movieImageView.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5),
            movieImageView.widthAnchor.constraint(equalToConstant: 50),
            movieImageView.heightAnchor.constraint(equalToConstant: 50),
            
//          movieNameLabel.leadingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: 5),
            movieNameLabel.leadingAnchor.constraint(equalTo: movieImageView.trailingAnchor, constant: 5),
            movieNameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor, constant: -5),
            movieNameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 5),
            movieNameLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -5)
        ])

        // during dev, so we can easily see the frames
        movieImageView.backgroundColor = .systemBlue
        movieNameLabel.backgroundColor = .cyan
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.