UITableView automatic row height (based upon content) - still the same error - uitableview

I want the rows in my tableview to scale dynamicly based upon the content (2 labels, total of 3 lines of text). I keep getting this error (and my cells are set to standard size):
[Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a table view cell's content view. We're considering the collapse unintentional and using standard height instead. Cell: <ProjectSammy.AnnotationCell: 0x106061d80; baseClass = UITableViewCell; frame = (0 647.5; 414 70); autoresize = W; layer = <CALayer: 0x282d09ee0>>
I think I've cohered to all of the requirements (as mentioned in this post:https://stackoverflow.com/questions/25902288/detected-a-case-where-constraints-ambiguously-suggest-a-height-of-zero):
I fully constrain (top and bottom anchors) the labels in my custom cell:
private func configureContents() {
backgroundColor = .clear
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
contentView.addSubviews(titleLabel, detailsLabel)
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor,constant: 5),
titleLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
titleLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
titleLabel.bottomAnchor.constraint(equalTo: detailsLabel.topAnchor),
detailsLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
detailsLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
detailsLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
detailsLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
I set the rows on the TableView to automic dimension:
private func configureTableView() {
tableView = UITableView(frame: view.bounds, style: .grouped)
view.addSubview(tableView)
tableView.delegate = self
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableView.backgroundColor = .clear
tableView.register(AnnotationCell.self, forCellReuseIdentifier: AnnotationCell.reuseIdentifier)
tableView.register(AnnotationHeaderCell.self, forHeaderFooterViewReuseIdentifier: AnnotationHeaderCell.reuseIdentifier)
tableView.sectionHeaderHeight = 40
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 70
}
And I even supply an estimated row heigth in the delegate method: -NOT NEEDED1- can be done on tableview directly!
Where do I go wrong ?

The estimated row height can not be set to automatic dimension. You have to set it to a specific number try setting it to 44 which is by default the row height.

Related

Change the layout margins of a UITableViewController programmatically

The first image is how my cells are currently and the second image is how I want them to be. I have a UITableViewController and want to programmatically change the layout margins however it does not work
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(named: "Gray")
tableView.backgroundColor = UIColor(named: "Gray")
tableView.contentInset.top = .padding
tableView.separatorStyle = .none
tableView.register(TaskCell.self, forCellReuseIdentifier: "taskCell")
tableView.layoutMargins = .init(top: 0, left: 20, bottom: 0, right: 20) // Does not work
}
Put a UIView inside the cell as background view and give margins to that view by adjusting its size in the storyboard. Then change the colour opacity of the cell's content view to 0

Using constraints with tableHeaderView of UITableView, tableHeaderView appears on top of cells

I'm creating a custom tableHeaderView using constraints and autolayout. The problem is that my tableHeaderView appears on top of the cells.
Here's my viewDidLoad:
let tableView = UITableView()
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//Adding subviews and setting constraints
let headerContainer = UIView()
let myView = UIView()
headerContainer.addSubview(myView)
//Setup constraints...
let myLabel = UILabel()
headerContainer.addSubview(myLabel)
//Adding many more views...
tableView.setAndLayoutTableHeaderView(header: headerContainer)
Reading this post, I've copied the suggested extension to UITableView: Is it possible to use AutoLayout with UITableView's tableHeaderView?.
extension UITableView {
//set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
func setAndLayoutTableHeaderView(header: UIView) {
self.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
print(header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height) //Always 0???
header.frame.size = header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
self.tableHeaderView = header
}
}
The problem is that the header view appears on top of the cells. On printing the size of the headerContainer after layoutIfNeeded and setNeedsLayout, the height is 0...
When you are maintaining the frame yourself, it could be simplified to just this.
extension UITableView {
func setAndLayoutTableHeaderView(header: UIView) {
header.frame.size = header.systemLayoutSizeFitting(size: CGSize(width: self.frame.size.with, height: .greatestFiniteMagnitude))
self.tableHeaderView = header
}
}

How to set left and right margin for UITableView in Swift

I am using UITableView inside my controller that displays cells with some informations (title, subtitle and image). I would like to add some padding to the tableView, but I can't find the right solution. This is my code for table view:
private let tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.allowsSelection = false
tableView.showsVerticalScrollIndicator = false
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableView.automaticDimension
return tableView
}()
Inside viewDidLoad() I setup constraints for that tableView
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
Right now my view looks like this:
Now when I try to configure contentInset property inside my tableView closure by adding this:
tableView.contentInset = .init(top: 0, left: 23.5, bottom: 0, right: -23.5)
As you can see it only added space on the left side, but looking at the debug view hierarchy
I can say that it moved that contentView to the right side (It's still have the same width as its superview)
Info from view debugger.
TableView:
<UITableView: 0x7fba82016000; frame = (0 0; 390 844); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x60000023de90>; layer = <CALayer: 0x600000c4b9e0>; contentOffset: {-23.333333333333332, -47}; contentSize: {390, 635.66666666666663}; adjustedContentInset: {47, 23.5, 34, -23.5}; dataSource: <Myapp.WelcomeViewController: 0x7fba81c08f60>>
and First cell:
<UITableViewCellContentView: 0x7fba84021250; frame = (0 0; 390 87); gestureRecognizers = <NSArray: 0x6000002cf900>; layer = <CALayer: 0x600000c2b900>>
TableView has the same width as cell (390 vs 390). How can I add my paddings to the left and right to the tableView, without making constraints on that tableView (I want that area to also be scrollable)
You can do this either by setting the .layoutMargins on the cell's contentView (either in the cell class itself or in cellForRowAt:
cell.contentView.layoutMargins = .init(top: 0.0, left: 23.5, bottom: 0.0, right: 23.5)
or on the table view itself:
override func viewDidLoad() {
super.viewDidLoad()
tableView.layoutMargins = .init(top: 0.0, left: 23.5, bottom: 0.0, right: 23.5)
// if you want the separator lines to follow the content width
tableView.separatorInset = tableView.layoutMargins
}
You may try this:
Add the constant while adding the constraints
NSLayoutConstraint.activate([
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20),
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
Try this
override var frame: CGRect {
get {
return super.frame
}
set (newFrame) {
var frame = newFrame
frame.origin.x += 10
frame.size.width -= 2 * 10
super.frame = frame
}
}

Adding a separator line under custom tableview cell in UITableViewCell

I am trying to add a separator to my uitableview cell. i tried this.
let separator = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 8.0))
cell.contentView.addSubview(separator)
But this adds the separator view on top of the cell, i need it to the bottom.
I also tried this way.
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(view)
view.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor).isActive = true
view.heightAnchor.constraint(equalToConstant: 8.0).isActive = true
view.topAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
But this gives me no common ancestor error. and i don't want to use storyboard. i need it because i am using same cell at different places, somewhere i need the separator somewhere not. what should i do?
Change constraint for this
view.topAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
to
view.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
its better to create the line inside init of the custom cell and make it a property
let view = UIView()
then mange its state from cellForRowAt
view.isHidden = true/false
Here is a better way to use separators:
First enable separators in your UITableView by:
myTableView.separatorStyle = .singleLine
Then at your cellForRowAt function:
// Create your cell
// if you want to show the separator then
cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
// if you want to hide the separator then
self.separatorInset = UIEdgeInsets(top: 0, left: UIScreen.main.bounds.width, bottom: 0, right: 0)
This would work for cells in the same UITableView as well. Because by adding a left inset of screen width then it won't show on screen and if you set it to 0 it'll be displyed from left edge to right edge of the screen.
Also you can change the color or the insets of the separator by using other properties without using storyboards or xibs.
you can just try below line under viewDidLoad() that include the tableview
override func viewDidLoad() {
tableView.separatorStyle = .singleLine
}

Swift - UITableViewCell height according to UITextView layout constraint

In my table view cell are 3 objects; an imageview, label and a textview.
This is how I set up my textView and its constraints:
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textColor = UIColor.blue
textView.font = UIFont.systemFont(ofSize:15.0)
textView.textAlignment = NSTextAlignment.left
textView.isScrollEnabled = false
**note that self is a UIView that contains the textView. The table view cell's content view will contain this UIView
self.addConstraints([
textView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 10.0),
textView.leftAnchor.constraint(equalTo: imageView.rightAnchor, constant: 10.0),
textView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10.0),
textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 20.0)
])
I made isScrollEnabled = false so that the textView's height is calculated according to its content
For my UITableView I did this:
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
With the above code my tableviewcell does not show the correct height. I believe its because my textview height constraint is giving the tableviewcell content view a wrong height.
How can I fix this?
Setting isScrollEnabled = false will not affect calculating textView's height.
In order to resize cell according to textView's content you should add bottom(=) constraint to your textView in addition to top(=) and optional height(>=).
Have you tried using tableView(heightForRowAt:) delegate method?
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return textView.frame.height // Assuming you have a reference to the textview
}

Resources