UIKit UIImageView with Offset UIImage - ios

I'm currently adding a UIImageView to a Table Cell, and when the cell is populated with data, I'm adding a UIImage (if it exists) to the UIImageView, however, the UIImage is offset like this:
I'm using AutoLayout to setup the Cell, as follows:
import Foundation
import UIKit
class CatalogItemTableCell: UITableViewCell {
let itemImageView: UIImageView = {
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.contentMode = .scaleAspectFit
imgView.clipsToBounds = true
imgView.layer.borderColor = UIColor(named: "FontColor")?.cgColor
imgView.layer.borderWidth = 1.0
imgView.layer.cornerRadius = 4.0
imgView.layer.masksToBounds = true
return imgView
}()
let nameLabel: UILabel = {
let label = UILabel()
label.lineBreakMode = .byTruncatingTail
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.boldSystemFont(ofSize: 18)
return label
}()
let skuLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 10)
return label
}()
let qtyOnHand: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let priceLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier);
containerView.addSubview(itemImageView)
containerView.addSubview(nameLabel)
containerView.addSubview(skuLabel)
containerView.addSubview(descriptionLabel)
containerView.addSubview(qtyOnHand)
containerView.addSubview(priceLabel)
self.contentView.addSubview(containerView)
NSLayoutConstraint.activate(
[
containerView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10.0),
containerView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10.0),
containerView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10.0),
containerView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10.0),
]);
NSLayoutConstraint.activate(
[
itemImageView.topAnchor.constraint(equalTo: containerView.topAnchor),
itemImageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
itemImageView.trailingAnchor.constraint(equalTo: nameLabel.leadingAnchor, constant: -10),
itemImageView.widthAnchor.constraint(equalToConstant: 80),
itemImageView.heightAnchor.constraint(equalToConstant: 80)
]);
NSLayoutConstraint.activate([
nameLabel.topAnchor.constraint(equalTo: containerView.topAnchor),
nameLabel.leadingAnchor.constraint(equalTo: itemImageView.trailingAnchor, constant: 10.0),
nameLabel.bottomAnchor.constraint(equalTo: skuLabel.topAnchor, constant: -10)
]);
NSLayoutConstraint.activate([
skuLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10.0),
skuLabel.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor),
skuLabel.bottomAnchor.constraint(equalTo: descriptionLabel.topAnchor, constant: -10)
]);
NSLayoutConstraint.activate([
descriptionLabel.topAnchor.constraint(equalTo: skuLabel.bottomAnchor, constant: -10.0),
descriptionLabel.leadingAnchor.constraint(equalTo: skuLabel.leadingAnchor),
descriptionLabel.bottomAnchor.constraint(equalTo: qtyOnHand.topAnchor, constant: -10)
]);
NSLayoutConstraint.activate([
qtyOnHand.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 10.0),
qtyOnHand.leadingAnchor.constraint(equalTo: descriptionLabel.leadingAnchor),
qtyOnHand.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
]);
NSLayoutConstraint.activate([
priceLabel.topAnchor.constraint(equalTo: containerView.topAnchor),
priceLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
]);
}
required init?(coder: NSCoder) {
super.init(coder: coder);
}
}
I'm also setting up the image using Alamoimagefire to scale it correctly, as I couldn't get scaling to work right by just adding the image to the UIImageView
let image = UIImage(contentsOfFile: fileUrl.path)
let size = CGSize(width: 80, height: 80)
let scaledImage = image!.af.imageAspectScaled(toFit: size)
cell.imageView?.image = scaledImage
Any help appreciated to help me understand what would cause this.

First, you shouldn't need to scale the image - using .contentMode = .scaleAspectFit on your image view should work properly.
The problem you are hitting, though, is you're setting the image on the wrong image view...
cell.imageView?.image = scaledImage
should be:
cell.itemImageView.image = scaledImage
but, again, no need to scale it first.

Related

How do I arrange the Profile Image View to pin next to the Username at the top in the arranged Stack View?

So I have a stack view and the profile image needs to go next to the the username and stay there. How do I do that in this arranged stack view without conflicts because I have tried to anchor it to the top. Like this but no results:
Image of what I am trying to achieve
But currently it keeps doing this:
What is currently happening
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.addSubview(userHandel)
profileImageView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
let innerPostStackView = UIStackView(arrangedSubviews: [profileNameLabel, userHandel, postTextLabel])
innerPostStackView.axis = .vertical
let postStackView = UIStackView(arrangedSubviews: [profileImageView, innerPostStackView])
postStackView.translatesAutoresizingMaskIntoConstraints = false
postStackView.alignment = .center
postStackView.spacing = 10
contentView.addSubview(postStackView)
NSLayoutConstraint.activate([
postStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
postStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
postStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
postTextLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15)
])
This is what I Have tried with the stack views. I cannot get it to work the way I want it to look.
This how your cell looks like:
class MyCell: UITableViewCell {
let profileNameLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .black
label.backgroundColor = .clear
label.font = .systemFont(ofSize: 20, weight: .bold)
label.text = "Minions"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let userHandel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .systemBlue
label.backgroundColor = .clear
label.font = .systemFont(ofSize: 14, weight: .semibold)
label.text = "#Minions"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let postTextLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textColor = .black
label.backgroundColor = .clear
label.text = "Every Mac comes with a one-year limited warranty(opens in a new window) and up to 90 days of complimentary technical support(opens in a new window). AppleCare+ for Mac extends your coverage from your AppleCare+ purchase date and adds unlimited incidents of accidental damage protection, each subject to a service fee of $99 for screen damage or external enclosure damage, or $299 for other accidental damage, plus applicable tax. In addition, you’ll get 24/7 priority access to Apple experts via chat or phone. For complete details, see the terms(opens in a new window)."
return label
}()
let costant: CGFloat = 60
let profileImageView: UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .darkGray.withAlphaComponent(0.2)
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
let containerView: UIView = {
let v = UIView()
v.backgroundColor = .clear
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .white
let image = UIImage(named: "minions")?.withRenderingMode(.alwaysOriginal)
profileImageView.image = image
profileImageView.widthAnchor.constraint(equalToConstant: costant).isActive = true // set here profileImageView wudth
profileImageView.layer.cornerRadius = costant / 2
contentView.backgroundColor = .white
containerView.addSubview(profileNameLabel)
profileNameLabel.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
profileNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
profileNameLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
profileNameLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
containerView.addSubview(userHandel)
userHandel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor).isActive = true
userHandel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
userHandel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
userHandel.heightAnchor.constraint(equalToConstant: 20).isActive = true
let totalUpStack = UIStackView(arrangedSubviews: [profileImageView, containerView])
totalUpStack.axis = .horizontal
totalUpStack.spacing = 6
totalUpStack.distribution = .fill
totalUpStack.translatesAutoresizingMaskIntoConstraints = false
totalUpStack.heightAnchor.constraint(equalToConstant: costant).isActive = true
let completeStack = UIStackView(arrangedSubviews: [totalUpStack, postTextLabel])
completeStack.axis = .vertical
completeStack.spacing = 6
completeStack.distribution = .fill
completeStack.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(completeStack)
completeStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10).isActive = true
completeStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10).isActive = true
completeStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
completeStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
This is the result:
I'm not sure if it will help you. You can ignore UIStackView and use auto-layout like this:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.addSubview(userHandel)
contentView.addSubview(postTextLabel)
// activate autolayout constraints:
NSLayoutConstraint.activate([
// profileImageView:
profileImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
profileImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 16),
profileImageView.heightAnchor.constraint(equalToConstant: 52),
profileImageView.widthAnchor.constraint(equalToConstant: 52),
// profileNameLabel:
profileNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
profileNameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8),
profileNameLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -16),
// userHandel:
userHandel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor, constant: 8),
userHandel.leftAnchor.constraint(equalTo: profileNameLabel.leftAnchor),
userHandel.rightAnchor.constraint(equalTo: profileNameLabel.rightAnchor),
// postTextLabel:
postTextLabel.topAnchor.constraint(equalTo: userHandel.bottomAnchor, constant: 8),
postTextLabel.leftAnchor.constraint(equalTo: userHandel.leftAnchor),
postTextLabel.rightAnchor.constraint(equalTo: userHandel.rightAnchor),
postTextLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
])
}

Programmatically set constraints for uitableviewcell with uilabel and uiimageview

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:
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.
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):
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
}
}

UITableView Cell with dynamic height and width not getting reused properly

I have a UITableView cell with dynamic height and width. Initially, it works properly, but when reusing an old cell the constraints are not set correctly. I am deactivating all the old constrains and activating them again. I have also called setNeedsLayout() and layoutIfNeeded(). But it's not helping.
Automatic height setup: (I think this is causing an issue)
discussionTableView.rowHeight = UITableViewAutomaticDimension
discussionTableView.estimatedRowHeight = 10
My table view cell:
class DiscussionChatMessageCell: UITableViewCell {
private let messageLabel: UILabel
private let senderNameLabel: UILabel
private let messageBubble: UIView
let screenWidth: CGFloat
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
messageLabel = UILabel()
senderNameLabel = UILabel()
screenWidth = UIScreen.main.bounds.size.width
messageBubble = UIView()
super.init(style: style, reuseIdentifier: reuseIdentifier)
// self.contentView.backgroundColor = .clear
self.contentView.addSubview(messageBubble)
messageBubble.translatesAutoresizingMaskIntoConstraints = false
messageBubble.addSubview(senderNameLabel)
senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
senderNameLabel.numberOfLines = 0
senderNameLabel.lineBreakMode = .byCharWrapping
senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
senderNameLabel.textColor = .white
messageBubble.addSubview(messageLabel)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
messageLabel.numberOfLines = 0
messageLabel.lineBreakMode = .byWordWrapping
messageLabel.font = UIFont.systemFont(ofSize: 13)
messageLabel.textColor = .white
NSLayoutConstraint.activate([
messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
messageBubble.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
messageBubble.widthAnchor.constraint(lessThanOrEqualToConstant: screenWidth - 100),
senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(message: String, isSender: Bool) {
senderNameLabel.text = "Default Sender"
messageLabel.text = message
for constraint in messageBubble.constraints {
// messageBubble.removeConstraint(constraint)
constraint.isActive = false
}
for constraint in messageLabel.constraints {
// messageLabel.removeConstraint(constraint)
constraint.isActive = false
}
for constraint in senderNameLabel.constraints {
//senderNameLabel.removeConstraint(constraint)
constraint.isActive = false
}
NSLayoutConstraint.deactivate([
messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10),
messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
])
NSLayoutConstraint.activate([
messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
messageBubble.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
messageBubble.widthAnchor.constraint(lessThanOrEqualToConstant: screenWidth - 100),
senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
])
messageBubble.backgroundColor = isSender ? accentColor : .gray
if isSender {
NSLayoutConstraint.activate([
messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
])
// let corners: UIRectCorner = [.topLeft, .topRight, .bottomLeft]
// roundCorners(corners: corners, isSender: isSender)
} else {
NSLayoutConstraint.activate([
messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
])
// let corners: UIRectCorner = [.topLeft, .topRight, .bottomRight]
// roundCorners(corners: corners, isSender: isSender)
}
}
Reusing cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let discussionChatMessageCell = tableView.dequeueReusableCell(withIdentifier: discussionChatId, for: indexPath) as? DiscussionChatMessageCell else { return UITableViewCell()}
discussionChatMessageCell.configureCell(message: messages[indexPath.row], isSender: isSender[indexPath.row])
discussionChatMessageCell.setNeedsLayout()
discussionChatMessageCell.layoutIfNeeded()
return discussionChatMessageCell
}
Before reusing cell:
After reusing cell:
Edit
When using UITextView instead of UILabel for messageLabel, the constraints work very differently and the table view takes 2-3 seconds to load.
Changed settings for textView
// messageLabel.numberOfLines = 0
// messageLabel.lineBreakMode = .byWordWrapping
messageLabel.isEditable = false
messageLabel.dataDetectorTypes = .all
messageLabel.textContainer.lineBreakMode = .byWordWrapping
messageLabel.setContentCompressionResistancePriority(.required, for: .vertical)
messageLabel.setContentHuggingPriority(.required, for: .vertical)
Output:
Here's the code for the updated cell, where I have also added a time label. So what is needed is
UILable, UITextView, UILabel. And right now this is UILabel, UILabel, UILabel.
class DiscussionChatMessageCell: UITableViewCell {
private let messageLabel: UILabel
private let senderNameLabel: UILabel
private let messageSentTimeLabel: UILabel
private let messageBubble: UIView
private var bubbleLeadingConstraint: NSLayoutConstraint!
private var bubbleTrailingConstraint: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
messageLabel = UILabel()
senderNameLabel = UILabel()
messageSentTimeLabel = UILabel()
messageBubble = UIView()
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(messageBubble)
messageBubble.translatesAutoresizingMaskIntoConstraints = false
messageBubble.addSubview(senderNameLabel)
senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
senderNameLabel.numberOfLines = 0
senderNameLabel.lineBreakMode = .byCharWrapping
senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
senderNameLabel.textColor = .white
messageBubble.addSubview(messageLabel)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
// messageLabel.isEditable = false
// messageLabel.dataDetectorTypes = .all
// messageLabel.textContainer.lineBreakMode = .byWordWrapping
messageLabel.numberOfLines = 0
messageLabel.lineBreakMode = .byWordWrapping
messageLabel.font = UIFont(name: "Helvetica Neue", size: 13)!
messageBubble.addSubview(messageSentTimeLabel)
messageSentTimeLabel.translatesAutoresizingMaskIntoConstraints = false
messageSentTimeLabel.lineBreakMode = .byCharWrapping
messageSentTimeLabel.numberOfLines = 0
messageSentTimeLabel.font = UIFont(name: "HelveticaNeue-Italic", size: 11)!
// set hugging and compression resistance for Name label
senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
senderNameLabel.setContentHuggingPriority(.required, for: .vertical)
// messageLabel.setContentCompressionResistancePriority(.required, for: .vertical)
// messageLabel.setContentHuggingPriority(.required, for: .vertical)
// create bubble Leading and Trailing constraints
bubbleLeadingConstraint = messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
bubbleTrailingConstraint = messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
// priority will be changed in configureCell()
bubbleLeadingConstraint.priority = .defaultHigh
bubbleTrailingConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
bubbleLeadingConstraint,
bubbleTrailingConstraint,
messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
messageBubble.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
messageBubble.widthAnchor.constraint(lessThanOrEqualTo: self.contentView.widthAnchor, constant: -100),
senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.bottomAnchor.constraint(equalTo: messageSentTimeLabel.topAnchor, constant: -10),
messageSentTimeLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
messageSentTimeLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageSentTimeLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
])
// corners will have radius: 10
messageBubble.layer.cornerRadius = 10
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(message: DiscussionMessage, isSender: Bool) {
senderNameLabel.text = message.userName + " " + message.userCountryEmoji
let date = Date(timeIntervalSince1970: message.messageTimestamp)
let dayTimePeriodFormatter = DateFormatter()
dayTimePeriodFormatter.timeZone = .current
dayTimePeriodFormatter.dateFormat = "hh:mm a"
let dateString = dayTimePeriodFormatter.string(from: date)
messageLabel.text = message.message
messageSentTimeLabel.text = dateString
messageLabel.textColor = isSender ? .black : .white
senderNameLabel.textColor = isSender ? .black : .white
messageSentTimeLabel.textColor = isSender ? .black : .white
messageSentTimeLabel.textAlignment = isSender ? .right : .left
bubbleLeadingConstraint.priority = isSender ? .defaultLow : .defaultHigh
bubbleTrailingConstraint.priority = isSender ? .defaultHigh : .defaultLow
messageBubble.backgroundColor = isSender ? accentColor : .gray
let senderCorners: CACornerMask = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner]
let nonSenderCorners: CACornerMask = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
if #available(iOS 11.0, *) {
messageBubble.layer.maskedCorners = isSender ?
// topLeft, topRight, bottomRight
senderCorners
:
// topLeft, topRight, bottomLeft
nonSenderCorners
} else {
// Fallback on earlier versions
// All corners will be rounded
}
}
}
Current output with the time label added to sender name label and message label:
You are modifying constraints way more than you need to.
A better approach would be to create both Leading and Trailing constraints for your "bubble" --- and change their Priority to determine which one is used.
So, if it's a "Received" message, we set the Leading constraint Priority to High, and the Trailing constraint Priority to Low. If it's a "Sent" message, we do the opposite.
Give this a try:
class DiscussionChatMessageCell: UITableViewCell {
let accentColor: UIColor = .systemYellow
private let messageLabel: UILabel
private let senderNameLabel: UILabel
private let messageBubble: UIView
private var bubbleLeadingConstraint: NSLayoutConstraint!
private var bubbleTrailingConstraint: NSLayoutConstraint!
// not needed
//let screenWidth: CGFloat
// wrong signature
//override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
messageLabel = UILabel()
senderNameLabel = UILabel()
messageBubble = UIView()
super.init(style: style, reuseIdentifier: reuseIdentifier)
// self.contentView.backgroundColor = .clear
self.contentView.addSubview(messageBubble)
messageBubble.translatesAutoresizingMaskIntoConstraints = false
messageBubble.addSubview(senderNameLabel)
senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
senderNameLabel.numberOfLines = 0
senderNameLabel.lineBreakMode = .byCharWrapping
senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
senderNameLabel.textColor = .white
messageBubble.addSubview(messageLabel)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
messageLabel.numberOfLines = 0
messageLabel.lineBreakMode = .byWordWrapping
messageLabel.font = UIFont.systemFont(ofSize: 13)
messageLabel.textColor = .white
// set hugging and compression resistance for Name label
senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
senderNameLabel.setContentHuggingPriority(.required, for: .vertical)
// create bubble Leading and Trailing constraints
bubbleLeadingConstraint = messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
bubbleTrailingConstraint = messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
// priority will be changed in configureCell()
bubbleLeadingConstraint.priority = .defaultHigh
bubbleTrailingConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
bubbleLeadingConstraint,
bubbleTrailingConstraint,
messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
messageBubble.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
messageBubble.widthAnchor.constraint(lessThanOrEqualTo: self.contentView.widthAnchor, constant: -100),
senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
])
// corners will have radius: 10
messageBubble.layer.cornerRadius = 10
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(message: String, isSender: Bool) {
senderNameLabel.text = "Default Sender"
messageLabel.text = message
bubbleLeadingConstraint.priority = isSender ? .defaultHigh : .defaultLow
bubbleTrailingConstraint.priority = isSender ? .defaultLow : .defaultHigh
messageBubble.backgroundColor = isSender ? accentColor : .gray
messageBubble.layer.maskedCorners = isSender ?
// topLeft, topRight, bottomRight
[.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner]
:
// topLeft, topRight, bottomLeft
[.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner]
}
}
Side Note: neither of these lines is needed in cellForRowAt:
//discussionChatMessageCell.setNeedsLayout()
//discussionChatMessageCell.layoutIfNeeded()
Edit - if you really want to support iOS prior to 11...
I suggest you subclass your "BubbleView" like this:
class BubbleView: UIView {
var radius: CGFloat = 0
var corners: UIRectCorner = []
var color: UIColor = .clear
lazy var shapeLayer: CAShapeLayer = self.layer as! CAShapeLayer
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override func layoutSubviews() {
super.layoutSubviews()
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
shapeLayer.path = path.cgPath
shapeLayer.fillColor = color.cgColor
}
}
and then use it like this:
class DiscussionChatMessageCell: UITableViewCell {
let accentColor: UIColor = .systemYellow
private let messageLabel: UILabel
private let senderNameLabel: UILabel
// use custom BubbleView class instead of standard UIView
private let messageBubble: BubbleView
private var bubbleLeadingConstraint: NSLayoutConstraint!
private var bubbleTrailingConstraint: NSLayoutConstraint!
// wrong signature - I beliee as of Swift 4.2
// 'UITableViewCellStyle' has been renamed to 'UITableViewCell.CellStyle'
//override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
messageLabel = UILabel()
senderNameLabel = UILabel()
messageBubble = BubbleView()
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(messageBubble)
messageBubble.translatesAutoresizingMaskIntoConstraints = false
messageBubble.addSubview(senderNameLabel)
senderNameLabel.translatesAutoresizingMaskIntoConstraints = false
senderNameLabel.numberOfLines = 0
senderNameLabel.lineBreakMode = .byCharWrapping
senderNameLabel.font = UIFont.boldSystemFont(ofSize: 15)
senderNameLabel.textColor = .white
messageBubble.addSubview(messageLabel)
messageLabel.translatesAutoresizingMaskIntoConstraints = false
messageLabel.numberOfLines = 0
messageLabel.lineBreakMode = .byWordWrapping
messageLabel.font = UIFont.systemFont(ofSize: 13)
messageLabel.textColor = .white
// set hugging and compression resistance for Name label
senderNameLabel.setContentCompressionResistancePriority(.required, for: .vertical)
senderNameLabel.setContentHuggingPriority(.required, for: .vertical)
// create bubble Leading and Trailing constraints
bubbleLeadingConstraint = messageBubble.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 10)
bubbleTrailingConstraint = messageBubble.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -10)
// priority will be changed in configureCell()
bubbleLeadingConstraint.priority = .defaultHigh
bubbleTrailingConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
bubbleLeadingConstraint,
bubbleTrailingConstraint,
messageBubble.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
messageBubble.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
messageBubble.widthAnchor.constraint(lessThanOrEqualTo: self.contentView.widthAnchor, constant: -100),
senderNameLabel.topAnchor.constraint(equalTo: messageBubble.topAnchor, constant: 10),
senderNameLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
senderNameLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.topAnchor.constraint(equalTo: senderNameLabel.bottomAnchor, constant: 10),
messageLabel.leadingAnchor.constraint(equalTo: messageBubble.leadingAnchor, constant: 10),
messageLabel.trailingAnchor.constraint(equalTo: messageBubble.trailingAnchor, constant: -10),
messageLabel.bottomAnchor.constraint(equalTo: messageBubble.bottomAnchor, constant: -10),
])
// corners will have radius: 10
messageBubble.radius = 10
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(message: String, isSender: Bool) {
senderNameLabel.text = "Default Sender"
messageLabel.text = message
bubbleLeadingConstraint.priority = isSender ? .defaultHigh : .defaultLow
bubbleTrailingConstraint.priority = isSender ? .defaultLow : .defaultHigh
messageBubble.color = isSender ? accentColor : .gray
let senderCorners: UIRectCorner = [.topLeft, .topRight, .bottomRight]
let nonSenderCorners: UIRectCorner = [.topLeft, .topRight, .bottomLeft]
messageBubble.corners = isSender ? senderCorners : nonSenderCorners
}
}
That will keep the "bubble" view's shape and size, even when the cell changes (such as when rotating the device).
I changed your code setting messageBuble constraint relative to the cell instead of the content view:
messageBubble.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
messageBubble.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10)
the just calling layoutIfNeeded():
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let discussionChatMessageCell = tableView.dequeueReusableCell(withIdentifier: discussionChatId, for: indexPath) as? DiscussionChatMessageCell else { return UITableViewCell()}
discussionChatMessageCell.configureCell(message: messages[indexPath.row], isSender: isSender[indexPath.row])
discussionChatMessageCell.layoutIfNeeded()
return discussionChatMessageCell
}
You add subviews a lot of times, but it's REUSABLE. Don't forget about it.
Add next code before .addSubview(....
contentView.subviews.forEach { $0.removeFromSuperview() }
Or change views values only, don't add it each time

Why ImageView has was wrong location when I use anchors programmatically?

everyone. I have problems with the collection view cell which I build programmatically using anchors.
Cell
private lazy var iconView: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.clipsToBounds = true
return image
}()
private lazy var label: UILabel = {
let label = UILabel()
label.numberOfLines = 2
label.font = NeoSansPro.regular.of(size: 12)
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
label.sizeToFit()
return label
}()
Config anchor
override func layoutSubviews() {
super.layoutSubviews()
setupUI()
}
func setupUI() {
NSLayoutConstraint.activate( [
iconView.heightAnchor.constraint(equalToConstant: 32),
iconView.widthAnchor.constraint(equalToConstant: 32),
iconView.centerXAnchor.constraint(equalTo: centerXAnchor, constant: 0),
iconView.centerYAnchor.constraint(equalTo: centerYAnchor, constant: -16),
label.topAnchor.constraint(equalTo: iconView.bottomAnchor, constant: 8),
label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 4),
label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4),
label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 4),
])
}
as the result 1 and 9 cells has wrong image location and label width biggest that cell width.
Add views first then add constraints it will work.
private lazy var iconView: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.image = UIImage(named: "1")
return image
}()
override func awakeFromNib() {
super.awakeFromNib()
backView.layer.cornerRadius = 5
self.contentView.addSubview(iconView)
setUpView()
}
func setUpView() {
NSLayoutConstraint.activate( [
iconView.heightAnchor.constraint(equalToConstant: 40),
iconView.widthAnchor.constraint(equalToConstant: 40),
iconView.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor, constant: 0),
iconView.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor, constant: -16)
])
}

UITableViewCell's content view height is not adjusting for the dynamic font (NSLayoutConstraints)

I am having difficulties adjusting table view cells to automatically resize for dynamic type fonts. Note that I am using layout constraints in code, not in storyboard.
Here is my table view setup code:
tableView = UITableView(frame: .zero, style: .grouped)
tableView.register(ChatAnalyticsCell.self, forCellReuseIdentifier: ChatAnalyticsCell.reuseIdentifier)
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
Here are my 2 labels and image view setup in ChatAnalyticsCell class
private var iconImageView: UIImageView = {
let imageView = UIImageView(frame: CGRect(origin: .zero,
size: CGSize(width: 20, height: 20)))
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.tintColor = UIColor(red: 0.25, green: 0.3, blue: 0.72, alpha: 1)
return imageView
}()
private var segmentLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor.gray
label.font = UIFont.preferredFont(forTextStyle: .footnote)
label.adjustsFontForContentSizeCategory = true
return label
}()
private var segmentDataLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor(red: 0.13, green: 0.15, blue: 0.19, alpha: 0.9)
label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
return label
}()
Here is the function where I add subviews and setup constraints, to resize properly for the dynamic-type font.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubviews()
}
private func setupSubviews() {
addSubview(iconImageView)
iconImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15.0).isActive = true
iconImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
iconImageView.widthAnchor.constraint(equalToConstant: 20.0).isActive = true
iconImageView.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
// Analytics segment label
addSubview(segmentLabel)
segmentLabel.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: 20).isActive = true
segmentLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
segmentLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20).isActive = true
// Segment data label
addSubview(segmentDataLabel)
segmentDataLabel.leadingAnchor.constraint(equalTo: segmentLabel.leadingAnchor).isActive = true
segmentDataLabel.trailingAnchor.constraint(equalTo: segmentLabel.trailingAnchor).isActive = true
segmentDataLabel.topAnchor.constraint(equalTo: segmentLabel.bottomAnchor, constant: 5).isActive = true
segmentDataLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 20).isActive = true
contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
}
Note that when I run the code, there are no constraints breaking, but the layout is not properly updated. Please see the screenshot:
Add all the views to the contentView!
contentView.addSubview(yourView)
One more thing - you might want to change(why?) this constraint,
segmentDataLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 20).isActive = true
to,
segmentDataLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
The rest of the constraints look ok to calculate the height of the contentView.

Resources