The content in the cells is aligned to the center.
This caused the cells were separated by unwanted white space.
I have tried the following code
In viewDidLoad
tableView.layoutMargins = UIEdgeInsets.zero
tableView.separatorInset = UIEdgeInsets.zero
tableView.separatorStyle = .none
In cellForRowAt
cell.layoutMargins = UIEdgeInsets.zero
cell.separatorInset = .zero
I am looking for a utility method to customize the space between the cells.
Please find the image below
Cell row height
Cells have exactly 0 space between them, so the cells themselves have to have a top/bottom margins.
To simply test what I've just said, set background of the cell to .red. If I'm wrong, you will have white stripes between red cells.
To get it right:
Look for top/bottom margins/constraints inside the cell and remove them
Make sure that all constraints make the row height unambiguous and use automaticDimension or calculate row height by yourself and hardcode it.
Use tableView:heightForRowAtIndexPath:. This lets you set the size regardless of the content in your cell.
Depending on your cell configuration, maybe setting automaticDimension could resolve it:
self.tableView.rowHeight = UITableView.automaticDimension
Otherwise, you may want to decrease the cell height:
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return CELL_HEIGHT_VALUE
}
Hope this helps.
Related
Yesterday I took a look at a quite big project from someone else and I noticed that wherever the intention was not to have the separator generated by UITableView, it is done by setting tableView.separatorColor = .clear instead of tableView.separatorStyle = .none.
Now I wondered If there might be a reason for that.. Is the result any different or does changing the style property mess up the constraints somehow because 0.5p are missing?
Yes, you are thinking in right direction.
tableView.separatorColor = .clear // It clears the separator background color
tableView.separatorStyle = .none // It removes the separator from superview(UITableCell)
Case Study:
Consider height of UITableViewCell is set to 50.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
If there are a UILabel in UITableViewCell and you have provided the leading, trailing, top and bottom constraint then tableView.separatorStyle = .none will not cause any constraint-break as height of UILabel will be auto-increased.
But if above case if you have applied height-constraint as well, then there will be difference of 0.5 pixel in calculated height and constraint height.
It is not necessarily happen every-time but to prevent this condition, we should use tableView.separatorColor = .clear.
I need to set UITableViewCell dynamic height based on it's content.
Here is my demo image:
In this to label which have dynamic content so based on that label content my cell height should change..
I get the constraint error. In this cell two UILabel is available and both have dynamic content size. (I already make the demo with auto resizing cell with only one label.) But in this cell two label are there. So error is xcode suggest me to give Y POS or Height to label, but I already set top constraints. (4 side constraints with label line set to 0 Zero) But still it's fails.
I also try with this code
self.tableView.estimatedRowHeight = 250.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
Here is my demo code
Look at the screenshot of my output. For both the labels , you should give all the four constrains and you have to change the heights of both the labels priority to 750 & set the hugging and resistance priority to 1000. Then you have to change the number of lines to 0 in storyboard
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 250
}
Add this code into viewDidLoad() method:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44
In Swift 3, Use
func tableView(_ tableView: UITableView,estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return HEIGHT // Maximum cell height
}
In table view cell, Set dynamic height constraint to the text view (Greater than or equal) - Maximum text view height
I found the problem. After setting text to UILabel, it doesn't still set correct height frame to its content. In cellForRowAt, just add this:
//these make height frame both of labels became correct
cell.lbName.sizeToFit()
cell.lbAge.sizeToFit()
// update cell layout
cell.layoutIfNeeded()
Have a look at the UITableViewDelegate function tableView(_:heightForRowAt:). It’ll work in your case if you are able to calculate the height for a given index path.
I'm making a simple texting app for practice and the UITableView has lots of cells containing messages.
It works well with one-line messages but, as you can see on the bottom, with multi-line texts the size of the UITableViewCell starts getting in the way and cutting the message short. Just in case, here's the code for my cellForRowAtIndexPath method:
internal func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if usernames[indexPath.row] == PFUser.currentUser()?.username {
cell = tableView.dequeueReusableCellWithIdentifier("text")!
} else {
cell = tableView.dequeueReusableCellWithIdentifier("reply")!
(cell.viewWithTag(2) as! UILabel).text = usernames[indexPath.row]
}
(cell.viewWithTag(1) as! UILabel).text = messages[indexPath.row]
return cell
}
Basically I just need a solution that will enable this app to support multi-line messages. Thanks in advance for any help!
Two things you need to do:
1) In viewDidLoad, specify these:
tableView.rowHeight = UITableViewAutomaticDimension
# The estimated row height number is not that important, just approximate it
tableView.estimatedRowHeight = 140
2) Define all constraints for your custom cell. Make sure top and bottom constraints specifically are defined.
The second step is particularly important.
1) On Storyboard,
In Attribute Inspector section.
Set Lines = 0
2) In func viewDidLoad(),
tableView.estimatedRowHeight = 50.0
tableView.rowHeight = UITableViewAutomaticDimension
You can use self-sizing table view cells and UITableViewAutomaticDimension. This is quite good article about it.
The trick to getting Auto Layout to work on a UITableViewCell is to ensure you have constraints to pin each subview on all sides — that is, each subview should have leading, top, trailing and bottom constraints. Then, the intrinsic height of the subviews will be used to dictate the height of each cell. You’ll do this now.
Then setup rowHeight to UITableViewAutomaticDimension:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
Senario A:
If I set the label content in cellForRowAtIndexPath, the cell correctly get resized.
Senario B:
If I change the text content in custom action in cell, the cell sized does not get changed.(I do call setNeedsLayout + layoutIfNeeded)
How to fix this?
EDIT:
1) I have set,
myTableView.estimatedRowHeight = 71.0
myTableView.rowHeight = UITableViewAutomaticDimension
2) I have correctly added auto layout constraints.
I was running into this issue, and my problem was that I was constraining the content to self (the UITableViewCell) and not to self.contentView (the contentView OF the cell). Hope this helps someone else who has built their cells all in code!
In my case, the cell's custom size was enabled:
After you change the text of the cell, just reload that particular cell or simply call mainTableView.reloadData().
To reload that cell-
//indexPath is indexPath of cell you just changed label of
mainTableView.reloadRows(at: indexPath, with: .automatic)
In my case, in the same cell I had an imageView in the top left corner with a "center vertically in container" constraint, and a "top space container" constraint.
Obviously to satisfy this two constraint the cell must have an height equal to:
(height of the imageView / 2) + (length of the top space container constraint).
This height is not enough to fit the label text, so my label had only 1 line visible.
After I have deleted the imageView top constraint all went to the right place, in my case i wanted the image to be centered, if the image had to stay in the top left corner I had to take off the "center vertically in container" constraint.
I hope this can help someone.
First of all, I don't specifically know what was your action on UITableViewCell. So, I assume I do that in UITableViewCell selection.
The below answer only work on iOS 9 and above
But, for some reason, it failed to do it in iOS 8 until it scroll. So, I will update the answer for iOS 8.
I have seen you have used UITableView's estimatedRowHeight and rowHeight at your project. So,
Please check the following
Make sure UITableView's estimatedRowHeight and rowHeight include inside viewDidLoad()
Make sure your UILabel lines set to 0
Make sure there is no constraints about height for your UILabel and let the constraints be like that :
If there are other component also included, make sure only bottom and top space constraints included or top space to container margin and bottom space to container margin.
Every time that you want to update the cell, you have to reload tableView no matter what your current situation will be.
So, don't say anything yet before you try this sample project, #Rikh answer still work. May be you are going in wrong direction. Here's the solution. Please do as I said steps by steps and let me know if that didn't work out. You might need to share your sample project which is causing.
Sample Demo - DynamicCellDemo
UPDATE for iOS 8 : update the following code for iOS 8 users
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 9, *) {
// do nothing
} else {
tblDynamic.reloadData()
}
}
what you can do is set the AutoLayout constraints for the label in present in the cell from all the sides that is from Top, Bottom, Leading and Trailing. Then add the following UITableViewDelegate method to your class.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 500
}
This will do the job. As now content in table view cell automatically adjusts the height of the cell.
Try this if it works:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Add the following in your viewDidLoad()
-(void)viewDidLoad{
[super viewDidLoad];
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
}
I have a UITableView with custom UITableViewCells, each has a UIButton inside. I'm setting buttons' titles from an array, so the size of the buttons change according to the title. I need to return correct height based on the inner button's size in heightForRowAtIndexPath event.
Since I'm using auto layout, I've created an outlet for the button's height constraint and I'm updating it in the cell's layoutSubviews() event like this:
class CustomCell: UITableViewCell {
/* ... */
override func layoutSubviews() {
super.layoutSubviews()
self.myButton?.layoutIfNeeded()
self.heightConstraint?.constant = self.myButton!.titleLabel!.frame.size.height
}
}
Then I return the height based on the button height and top-bottom margins like so:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomCell
cell.myButton?.setTitle(self.data[indexPath.row], forState: UIControlState.Normal)
cell.bounds = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds))
cell.setNeedsLayout()
cell.layoutIfNeeded()
return cell.myButton!.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + (cell.topMarginConstraint!.constant * 2) /* top-bottom margins */ + 1 /* separator height */
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomCell
cell.myButton?.setTitle(self.data[indexPath.row], forState: UIControlState.Normal)
return cell
}
On the first launch, there seems to be no problem. However, after I begin scrolling, then the height of some rows seem to be mistaken. When I get back to the top, I see that previous cell heights get to be broken as well.
When I googled for similar problems, issue seems to be about reusable cells, though I was unable to find another way to calculate the height. What can be done to reuse cells correctly or getting the correct height, perhaps by another method?
More info and source code:
Constraints set by IB like this:
Here's the cells on the first launch:
After some scrolling:
Full code of the project can be found on Github.
According to this
Configure tableView as
func configureTableView() {
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 160.0
}
Call it on your viewDidLoad method
Than configure your uibutton height constraint to be greater then or equal.
Override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat where you can place your estimation height code
First off, it's better if you perform constraint updates in func updateConstraints() method of UIView. So instead of
override func layoutSubviews() {
super.layoutSubviews()
self.myButton?.layoutIfNeeded()
self.heightConstraint?.constant = self.myButton!.titleLabel!.frame.size.height
}
I would do
override func updateConstraints() {
self.myButton?.layoutIfNeeded()
self.heightConstraint?.constant = self.myButton!.titleLabel!.frame.size.height
super.updateConstraints()
}
Note that you should call the super implementation at the end, not at the start. Then you would call cell.setNeedsUpdateConstraints() to trigger a constraint update pass.
Also you should never directly manipulate the cell bounds the way you are doing in heightForRowAtIndePath: method, and even if you are completely sure that manipulating directly is what you want, you should manipulate cell.contentView's bounds, not the cell's bounds. If you are looking to adjust the cell height dynamically with respect to the dimensions of the content, you should use self sizing cells. If you need to support iOS 7, then this answer tells you how to achieve that behaviour with autolayout only (without touching the bounds etc).
To reiterate the answer, you should do:
func viewDidLoad() {
self.dummyCell = CustomCell.init()
// additional setup
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
self.dummyCell.myButton?.setTitle(self.data[indexPath.row], forState: UIControlState.Normal)
self.dummyCell.layoutIfNeeded() // or self.dummyCell.setNeedsUpdateConstraints() if and only if the button text is changing in the cell
return self.dummyCell.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
}
Please know that the answer I linked to outlines a strategy to get the cell height via autolayout, so only writing the code changes I proposed won't be enough unless you set your constraints in a way that makes this solution work. Please refer to that answer for more information.
Hope it helps!
First of all, remove the height constraint of button and bind it to top and bottom with cell.
Then, in your cell' height, calculate height of the text based on the width and font of button. This will make the cell's height dynamic and you wont need height constraint anymore.
Refer the link below to get the height of text:
Adjust UILabel height to text
Hope it helps. If you need help further or understanding anything, let me know.. :)