There's a simple UITableView in my app, and there's a custom view for the tableView.tableHeaderView property. When this property is set, the view has the correct size (full width, about 45px high).
[_resultHeaderView sizeToFit]; // the view as the correct frame
[_resultTableView setTableHeaderView:_resultHeaderView];
In iOS 9 and previous versions, the header displays correctly, but in iOS 10, the cells start at the same Y coordinate as my header view, so my header view appears over the first cell.
Setting these properties also have no effect:
self.edgesForExtendedLayout = UIRectEdgeNone;
self.automaticallyAdjustsScrollViewInsets = NO;
Has something changed in iOS 10 that could explain this different behavior? What would be a good solution?
Thanks
The bug was in the fact that the view was being resized automatically, so its frame height really was 0 when it was being attached to the tableView, which explains the behavior.
Setting the autoresizingMask to none fixed this bug.
_headerView.autoresizingMask = UIViewAutoresizingNone
Again, it wasn't necessary in iOS 9 and below. Hope this helps someone else.
Here is a more Swift approach
tableView.tableHeaderView?.translatesAutoresizingMaskIntoConstraints = false
Or you can do it directly in your header
myHeaderView.translatesAutoresizingMaskIntoConstraints = false
Also you must call these two delegate methods.
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return yourHeaderView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30 // Your preferred height
}
Just set headerView.frame before tableView.tableHeaderView = headerView
Related
I am running into an issue with automatic/dynamic UITableView section header views that contain a UILabel that wraps (numberOfLines = 0). The height is not being calculated properly, especially once you scroll the table and the views are reused. Sometimes the UILabel wraps, sometimes it is truncated, sometimes one or more of the labels are not visible, and sometimes there is extra spacing between the labels. The custom view contains a vertical UIStackView with three UILabels, once of which wraps.
A complete sample app demonstrating the issue can be found at https://github.com/outerstorm/tableviewHeaderTest.
The section header heights are set to automatic in viewDidLoad with the following:
tableView.sectionHeaderHeight = UITableViewAutomaticDimension
tableView.estimatedSectionHeaderHeight = 30.0
and also have implemented the following heightForHeaderInSection just to try to get it to work:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
I have also tried calling setNeedsLayout() and layoutIfNeeded() at various points to no avail. Any suggestions would be greatly appreciated.
Below is a screenshot of the behavior seen in the app. The first section is cutoff and the second section is too tall:
I have faced this kind of issue recently.
I solved it by setting preferredMaxLayoutWidth of multiline labels, i.e.
Before setting the value in labels, set their preferredMaxLayoutWidth as:
self.label1.preferredMaxLayoutWidth = self.label1.frame.size.width
self.label2.preferredMaxLayoutWidth = self.label2.frame.size.width
self.label3.preferredMaxLayoutWidth = self.label3.frame.size.width
Just add estimatedHeightForHeaderInSection function and return your estimated height. It will resolve your issue. Download your modified project from here
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat
{
return 30;
}
In general
heightForHeaderInSection
always called before
viewForHeaderInSection
when using UITableViewAutomaticDimension, sectionheader height will only calculate once unless called tableview.reloadData() manually.
In the code, you change sectionheader text everytime. the height was calculate at the first time, and doesnt change automatic.
you can change setup func to:
func setup(someNum: Int) {
let text = String(repeating: "This is where the really long text goes so that it will wrap lines appropriately", count: someNum)
mainLabel.text = text
}
and pass the section index to the function
A workaround can be hold the header views in array and then return the height of view from estimated height method
for _ in 0 ..< data.count {
let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomHeaderView") as! CustomHeaderView
view.setup()
headerViews.append(view)
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat
{
let view = headerViews[section] as? UIView
return (view?.frame.size.height)!
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return headerViews[section] as? UIView
}
From the code's documentation:
// tableView:titleForHeaderInSection: or tableView:titleForFooterInSection: if the title is not nil.
In other words. UITableViewAutomaticDimension is only intended for use if you are providing a section title using titleForHeaderInSection or titleForFooterInSection
Add
self.label1.preferredMaxLayoutWidth = self.label1.frame.size.width
self.label1.numberoflines = 0;
self.label1.linebreakmode = NSLineBreakByWordWrapping;
in awakeFromNib method
or
Kindly check this once
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 want to make a custom footer for my UITableView.
For that, I will need the position of the last cell in the UITableView and set my UIView under the TableView.
But I don't know how to get the position of the last cell.
Is it even possible to make this kind of custom Tableview-footer?
You can add a footer directly in the storyboard or xib--just drag your view so that it's inside your tableView, but after all of the tableView's cells.
You don't need to make custom footer or to know the position of your last cell. It is already available on UITableViewDelegate.
You need to make a custom UIView then on the callback of func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? return the UIView.
Example:
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView.init(frame: CGRectMake(0, 0, tableView.frame.size.width, tableView.frame.size.height))
view.backgroundColor = UIColor.greenColor()
return view
}
The above code will give you a green UIView for the UITableView. Just make sure you do not set the Footer height to 0 on Storyboard or on code.
My target is to change the UITableView section header height at runtime. Let me be more specific. Default height is 44 but on scroll before touching the top, height will be 64. I have created a subclass of UITableViewHeaderFooterView and it's uses autolayout.
I tried
var frame = sectionHeader.frame
frame.size.height = 64
sectionHeader.frame = frame
and also
tableView.sectionHeaderHeight = 64
but nothing work for me. Can anyone put some light on this problem.
when using autolayout change frame directly won't work
to change tableview section header you need implement delegate method and reload data after change
optional func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
You don't need to subclass the footer to change I'ts height.
Check you are only setting I'ts height at: (no delegate methods)
tableView.sectionHeaderHeight = 64
Check you call or "reloadData()" on the tableView after. or
[UIView setAnimationsEnabled:NO];
[tableView beginUpdates];
[tableView endUpdates];
[UIView setAnimationsEnabled:YES];
To only change height without reloading the tableView.
I think you have to reload the entire section of the table view, since there is no public API for only updating the section header.
self.isOnTop = true // or false
tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .None)
And change a property that modifies the return value from the aproppriate delegate method:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
isOnTop ? 44 : 64
}
You can also try the following methods, that can update the layout without reloading:
tableView.beginUpdates()
tableView.endUpdates()
Or move to a collection view solution, that supports the tableview's behaviour: https://github.com/jamztang/CSStickyHeaderFlowLayout
I am trying to resize a tableviewcell based on the size of a UILabel that can be one or more lines. I need autolayout turned ON for the rest of the app, so I don't want to turn it off to get this working. Target is iOS 7 and iOS 8.
I have tried several solutions, but for some reason I can't seem to get the correct height of the label to automatically adjust or to adjust the cell height.
The problem is occurring in this method: calculateHeightForConfiguredSizingCell. I have put a sample project on Git, so you can see what I am seeing.
https://github.com/mdaymond/cellResizer
This example is based on this article: http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout
Update
I checked in an update to the code. It's ALMOST working the way I want and calculating programatically, but for some reason the label height isn't quite sized correctly - it's not getting the full height required. Problem with the original code was that the label needed an explicit width.
Here we go, if you are supporting IOS 7 then you need to implement UITableViewDelegate protocol in your class and then override:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
}
for more information about Dynamic custom UITableViewCell's height based on label text length (check this out) and you can place the code in the heightForRowAtIndexPath function:
height based on label text length
Also override :
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// example to return the estimated height
return 280
}
Note : by supporting IOS 7 you should handle it manually. there is no such easier way as IOS 8.
But if you are only supporting IOS 8 and later then you can do it simply in the following two lines :
self.tableView.estimatedRowHeight = 280
self.tableView.rowHeight = UITableViewAutomaticDimension
Actually i have download your source code and doing following changes. and UILabel is now support multiline.
Step 1 : Removed programmatically calculate row height. that means commented heightForRowAtIndexPath in your demo.
Step 2: Set following layout constraints on UILabel in your UITableViewCell.
Step 3 : Set number of line to 0 in your xib.
Output :
Here is my code.
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
self.scoutPropertyTable.rowHeight = UITableViewAutomaticDimension + 35
return self.scoutPropertyTable.rowHeight
}