Dynamically change cell height according to label text - swift

I tried to add Q&A part for my app and that tableview cell have to expand according to text height, cell height have to change with this text. In this point I did some thing to solve problem which are numberOfline = 0 and UITableView.automaticDimension code can't solve my problem. Here my code from project :
Problem is cell height didn't change this code just write label under the questions and for longer answers under the next question. Means cell height didn't change.
let soruLabel: UILabel = {
let sorulabel = UILabel()
sorulabel.textColor = .darkGray
sorulabel.numberOfLines = 0
sorulabel.font = UIFont.boldSystemFont(ofSize: 17)
return sorulabel
}()
let cevapLabel: UILabel = {
let cevaplabel = UILabel()
cevaplabel.textColor = .black
cevaplabel.numberOfLines = 0
cevaplabel.font = UIFont(name: "HelveticaNeue", size: 18)
return cevaplabel
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
func set(content: expandingTableCellForsss) {
self.soruLabel.text = content.soru
self.cevapLabel.text = content.expanded ? content.cevap : ""
}
func setup() {
backgroundColor = UIColor.init(red: 245, green: 245, blue: 245, alpha: 1)
addSubview(soruLabel)
addSubview(cevapLabel)
soruLabel.translatesAutoresizingMaskIntoConstraints = false
soruLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
soruLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
soruLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = false
soruLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
cevapLabel.translatesAutoresizingMaskIntoConstraints = false
cevapLabel.topAnchor.constraint(equalTo: soruLabel.bottomAnchor).isActive = true
cevapLabel.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
cevapLabel.bottomAnchor.constraint(equalTo: soruLabel.bottomAnchor).isActive = false
cevapLabel.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

The bottom anchor needs to be set:
cevapLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

Please check :
UITableViewCell auto height based on amount of UILabel text ,
Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
To use dynamic cell height:
1) give a minimum row height for cell in viewDidLoad
tableView.estimatedRowHeight = 300
2) set rowHeight property of tableview as automatic in viewDidLoad
tableView.rowHeight = UITableViewAutomaticDimension
3) in storyboard select numberOfLines = 0
4) if you have provided custom height to cell in storyBoard, make sure its equal to estimated row height.

Related

Swift: Encapsulated Layout Height always .333 larger than view

I am building a TableView filled with a variable number of cells. One type of those cells should have a fixed height of 69.0, but xcode keeps setting its encapsulated layout height .333 larger than what I set the height to, meaning it breaks the constraints because the encapsulated layout height is 69.333. If I change my Cell's size to 70 for example, the constraint is set to 70.333. I don't understand what causes this.
Here is the Cell:
class AnnotationCell: UITableViewCell {
//Set identifier to be able to call it later on
static let identifier = "AnnotationCell"
var annotation: Annotation!
//MARK: - Configure
public func configure(annotation: Annotation) {
self.annotation = annotation
}
//MARK: - Cell Style
//Add all subviews in here
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
//Customize Cell
contentView.backgroundColor = colors.darkGray
contentView.layer.borderWidth = 0
//Favorite
let favoriteView = UIView()
favoriteView.backgroundColor = colors.gray
favoriteView.addBorders(edges: [.top], color: colors.lightGray, width: 1)
favoriteView.translatesAutoresizingMaskIntoConstraints = false
let favIcon = UIImageView()
let myEmoji = "👀".textToImage() //Test
favIcon.image = myEmoji
favIcon.contentMode = .scaleAspectFill
favIcon.translatesAutoresizingMaskIntoConstraints = false
favoriteView.addSubview(favIcon)
let stringStack = UIStackView()
stringStack.axis = .vertical
stringStack.spacing = 6
stringStack.translatesAutoresizingMaskIntoConstraints = false
let titleString = UILabel()
titleString.text = "Test"
titleString.textColor = colors.justWhite
titleString.font = UIFont(name: "montserrat", size: 17)
titleString.translatesAutoresizingMaskIntoConstraints = false
stringStack.addArrangedSubview(titleString)
let addressString = UILabel()
addressString.text = "Test 2"
addressString.textColor = colors.lightGray
addressString.font = UIFont(name: "montserrat", size: 14)
addressString.translatesAutoresizingMaskIntoConstraints = false
stringStack.addArrangedSubview(addressString)
favoriteView.addSubview(stringStack)
let editButton = UIButton()
editButton.tintColor = colors.lightGray
let mediumConfig = UIImage.SymbolConfiguration(pointSize: 20, weight: .semibold, scale: .medium)
let mediumEditButton = UIImage(systemName: "chevron.right", withConfiguration: mediumConfig)
editButton.setImage(mediumEditButton, for: .normal)
editButton.translatesAutoresizingMaskIntoConstraints = false
favoriteView.addSubview(editButton)
contentView.addSubview(favoriteView)
//Define Constraints
NSLayoutConstraint.activate([
favoriteView.heightAnchor.constraint(equalToConstant: 69),
favoriteView.topAnchor.constraint(equalTo: contentView.topAnchor),
favoriteView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
favoriteView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
favoriteView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
favIcon.leadingAnchor.constraint(equalTo: favoriteView.leadingAnchor, constant: 15),
favIcon.centerYAnchor.constraint(equalTo: favoriteView.centerYAnchor),
favIcon.heightAnchor.constraint(equalToConstant: 50),
favIcon.widthAnchor.constraint(equalToConstant: 50),
stringStack.leadingAnchor.constraint(equalTo: favIcon.trailingAnchor, constant: 20),
stringStack.centerYAnchor.constraint(equalTo: favoriteView.centerYAnchor),
editButton.trailingAnchor.constraint(equalTo: favoriteView.trailingAnchor, constant: -15),
editButton.centerYAnchor.constraint(equalTo: favoriteView.centerYAnchor),
])
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
I am not setting the Cell's height in the TableView at all - I've tried that and that did not make things better. The Cell is getting displayed correctly, but console throws:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
(
"<NSLayoutConstraint:0x281d21ae0 UIView:0x15cca3490.height == 69 (active)>",
"<NSLayoutConstraint:0x281d21bd0 V:|-(0)-[UIView:0x15cca3490] (active, names: '|':UITableViewCellContentView:0x15cca42a0 )>",
"<NSLayoutConstraint:0x281d22120 UIView:0x15cca3490.bottom == UITableViewCellContentView:0x15cca42a0.bottom (active)>",
"<NSLayoutConstraint:0x281d28820 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x15cca42a0.height == 69.3333 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x281d21ae0 UIView:0x15cca3490.height == 69 (active)>
I have tried giving every subview a fixed height (which does not make sense with a stackview in this layout) but that did not work either. I had the same layout in a view (not TableViewCell) without any issues, so I am guessing that it has to do something with the TableView.
Here are my constraints as seen from the view hierarchy, in case someone has an eye for it. I have opened up most constraints to make it as clear as possible. As can be seen next to the red arrow, the constraint of 69.0 seems to be there and I don't see anything different. The only thing I could see causing this issue would be the "UISysmenBackGroundView" which has a view inside it without any constraints.
1/3 pt sounds like a hairline thickness on a 3x device. Could it perhaps be the separator? If they’re enabled, try turning them off to see if that fixes the issue.

Adjusting UILabels size (i.e., width and height) inside a UITableViewCell Class?

I created a custom UITableViewCell Class as illustrated below, whereby i defined the items I would like to be displayed inside my custom cell (specifically UILabel items):
import UIKit
import ChameleonFramework
class IsectionsCustomTableViewCell: UITableViewCell {
let sectionDesignationLabelTopPadding: CGFloat = 10
let sectionDesignationLabelLeftPadding: CGFloat = 10
let sectionDesignationLabelRightPadding: CGFloat = 10
let depthOfSectionLabelTopPadding: CGFloat = 0
let depthOfSectionLabelLeftPadding: CGFloat = 10
let depthOfSectionLabelRightPadding: CGFloat = 10
let webThicknessLabelTopPadding: CGFloat = 10
let webThicknessLabelLeftPadding: CGFloat = 10
let webThicknessLabelRightPadding: CGFloat = 10
var sectionDesignationLabel: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.yellow
label.textAlignment = .left
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor(hexString: "#F27E63")
return label
}()
var depthOfSectionLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
label.backgroundColor = UIColor.blue
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor(hexString: "#F27E63")
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(sectionDesignationLabel)
addSubview(depthOfSectionLabel)
applyAppropriateSizeAndConstraintsForCellItems()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func applyAppropriateSizeAndConstraintsForCellItems() {
sectionDesignationLabel.frame.size.width = 200
NSLayoutConstraint.activate([
sectionDesignationLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: sectionDesignationLabelLeftPadding),
sectionDesignationLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -1*sectionDesignationLabelRightPadding),
sectionDesignationLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: sectionDesignationLabelTopPadding),
depthOfSectionLabel.topAnchor.constraint(equalTo: sectionDesignationLabel.bottomAnchor, constant: depthOfSectionLabelTopPadding),
depthOfSectionLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: depthOfSectionLabelLeftPadding),
depthOfSectionLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -1 * (((self.frame.size.width)/2) + depthOfSectionLabelRightPadding)),
])
}
}
As it can be seen from the declaration of depthOfSectionLabel, whereby I stated the width and height of its frame. However, these values are not reflected when my table gets displayed (please refer to attached image). The width is displayed much more greater than the 50 I specified for this specific label?
My question is how can I adjust the widths of the various labels to be displayed inside my tableView custom Cell?

UIStackView with multiline label in a UITableViewCell incorrect height

I have a table view where each cell displays an optional title and a multiline subtitle. I want the cell to be self-sizing (i.e grow along with the subtitle but stay as compact as possible). For that I use tableView.rowHeight = UITableView.automaticDimension
The problem I have is that there is quite a lot of vertical spacing around the subtitle in the cells that do have a title.
Cells without title are compressed correctly. Also when I reload the table view, all the layout becomes correct.
Expected behaviour:
Actual behaviour:
The cell has basically a UIStackView pinned to the cell's contentView.
import UIKit
public class TableViewCellSubtitle: UITableViewCell {
private lazy var labelStack: UIStackView = {
let labelStack = UIStackView()
labelStack.alignment = .fill
labelStack.distribution = .fillProportionally
labelStack.axis = .vertical
return labelStack
}()
private lazy var titleLabel: UILabel = {
let titleLabel = UILabel()
titleLabel.backgroundColor = UIColor.blue.withAlphaComponent(0.3)
return titleLabel
}()
private lazy var subtitleLabel: UILabel = {
let subtitleLabel = UILabel()
subtitleLabel.numberOfLines = 0
subtitleLabel.backgroundColor = UIColor.green.withAlphaComponent(0.3)
return subtitleLabel
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
contentView.addSubview(labelStack)
labelStack.translatesAutoresizingMaskIntoConstraints = false
labelStack.addArrangedSubview(titleLabel)
labelStack.addArrangedSubview(subtitleLabel)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
labelStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
labelStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
contentView.bottomAnchor.constraint(equalTo: labelStack.bottomAnchor, constant: 12),
contentView.trailingAnchor.constraint(equalTo: labelStack.trailingAnchor, constant: 16)
])
}
public func setup(title: String?, subtitle: String) {
titleLabel.text = title
if title == nil {
labelStack.removeArrangedSubview(titleLabel)
titleLabel.removeFromSuperview()
}
subtitleLabel.text = subtitle
}
}
I tried setting the content hugging on the subtitle
subtitleLabel.setContentHuggingPriority(.required, for: .vertical)
but that makes the title to grow:
If I set it to both:
titleLabel.setContentHuggingPriority(.required, for: .vertical)
subtitleLabel.setContentHuggingPriority(.required, for: .vertical)
it becomes
In all the cases, if I reload the cell or the table view, the layout becomes correct (here on cell tap):
I'm aware that I could layout the cell without using the stack view but my real implementation is a bit more complex
Try with fill instead of fill proportional
Or try to set label.sizeToFit() to collapse label to its content size

Adjusting UILabel height depending on contents?

I have a custom CollectionViewCell class, which contains a label and an image. Basically, the image will be constrained to the left, top and right of the cell. However, I want its height to vary depending on the height of the UILabel, which in turns is going to be dependent on the contents inside it. Below is my attempt at it:
import UIKit
class CustomCollectionViewCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .yellow
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init(cellTextTile text: String) {
self.init()
}
func setupCustomCellElements(cellImageName image: String, cellTitleTextColour textColour: UIColor, cellTitleTextSize textSize: CGFloat, cellTitleFontType fontType: String, cellTitle title: String) {
let cellImage: UIImageView = {
let imageView = UIImageView()
imageView.backgroundColor = .clear
imageView.image = UIImage(named: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
let cellTitle: UILabel = {
let label = UILabel()
label.textColor = textColour
label.font = UIFont(name: fontType, size: textSize)
label.text = title
label.textAlignment = .center
label.frame.size = CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude)
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.sizeToFit()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
addSubview(cellImage)
addSubview(cellTitle)
NSLayoutConstraint.activate([
cellTitle.bottomAnchor.constraint(equalTo: bottomAnchor),
cellTitle.leftAnchor.constraint(equalTo: leftAnchor),
cellTitle.rightAnchor.constraint(equalTo: rightAnchor),
cellImage.bottomAnchor.constraint(equalTo: cellTitle.topAnchor),
cellImage.topAnchor.constraint(equalTo: topAnchor),
cellImage.leftAnchor.constraint(equalTo: leftAnchor),
cellImage.rightAnchor.constraint(equalTo: rightAnchor)
])
}
}
However, with the above code I am not getting the kind of behaviour I am looking for. I want the height of the UIlabel to change based on its contents. And in turn the height of the image to adjust accordingly?
Regards,
Shadi.
You need a height constraint for the imageView
cellImage.heightAnchor.constraint(equalToConstant: 50), // or any value
Also don't add the properties inside the method setupCustomCellElements make them an instance variables , and add them inside the init function
It's better also to add the views to
contentView.addSubview(cellImage)
contentView.addSubview(cellTitle)
and constrcut the constraints with it

Swift: tableview cell change width and postion

I create chatroom
I want user 1 chats position in right and user 2 chats position in left like below image
My code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! MyChatCell
var frame = cell.frame
let newWidth = frame.width * 0.50
let space = (frame.width - newWidth) / 2
frame.size.width = newWidth
frame.origin.x += space
cell.frame = frame
return cell
}
But my code not work
Please consider using Anchor for your MyChatCellLeft and MyChatCellRight and anchor each example:
cell.messageLabel = "My Message"
cell.floatPosition = true // true left / false right
I must mention I haven't tested this. but you can try it and take it as a blueprint for what you are doing.. you also need to add some kind of logic of who is who... example user1 left user2 right. that depends on your logic...
class ChatBubbleCell: UITableViewCell{
var position: Bool = false
let messageLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var floatPosition = Bool() {
didSet {
if(floatPosition == true){
position = true
} else {
position = false
}
}
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews(){
addSubview(messageLabel)
// lef position
if position == true {
let constrains = [messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 15),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15),
messageLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 20)
]
NSLayoutConstraint.activate(constrains)
} else {
let constrains = [messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 15),
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15),
messageLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -30)
]
NSLayoutConstraint.activate(constrains)
}
}
}
Consider the cell to be the whole row and use a subview to update frames.
Update the frames on the method of will display cell for row at index path