I am having UITableView in my one of screens. It shows section headers and rows with dynamic height.
There are two issues with this set-up when I moved from Xcode8.3 to Xcode-9 beta.
1 Header view height broken
2 Dynamic height of rows broken
I have set-up my table view as:
Then I have custom cell
Here is code:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return //dynamically calculated height according to msg text
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 30
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerHeight:CGFloat = tableView.sectionHeaderHeight
let headerView = HeaderView(frame: CGRect(x: 0, y: 0, width: tableView.width, height: headerHeight), withHeaderLable: "Date/Day of msg")
return headerView
}
This works perfect in Xcode8.3 even with multiple lines of msgs, but broken in Xcode9 beta as:
When I run with Swift3.2 settings in Xcode9 beta:
When I run with Swift4 settings in Xcode9 beta:
What would be the reason of this behavior?
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 30.0
Add this in your viewDidLoad and no need of tableview delegate method estimatedHeightForRowAt.
Related
I have a tableView cell with constrains that works nice in Xcode 9, and iOS 10, after upgrate to iOS 12 and Xcode 10 the self sizing stop working, tableView was unable to determinate the cell size.
The cell designing is this with all constraints:
So setting by code the automatic dimension
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 196
}
The result is that I have to use the height for row with a number instead of automaticDimension to see the cell in tableView. So, I'm missing a constrain here or what.
I'm new to IOS development with swift and I'm having a problem. I need to create a tableview and it looks almost the way I wanted, except for the space at the top of the first section of the table. It has no name but I would like to reduce the space between the top and the first item. What I was able to do is according to the code and image below:
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch (section) {
case 0:
return ""
default:
return self.nameSection2
}
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
headerView.backgroundColor = UIColor.white
let headerLabel = UILabel(frame: CGRect(x: 15, y: 8, width:
tableView.bounds.size.width, height: tableView.bounds.size.height))
headerLabel.font = UIFont(name: "Verdana", size: 16)
headerLabel.textColor = UIColor.lightGray
headerLabel.text = self.tableView(self.tableView, titleForHeaderInSection: section)
headerLabel.sizeToFit()
headerView.addSubview(headerLabel)
return headerView
}
The 'margin' you see is because the height for both the section headers is the same. The second one looks less-empty as it actually has a title.
You can modify the height for the headers to reduce the space:
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
switch section {
case 0:
return 0
case 1:
return 44 //Required height value here
default:
return defaultValue //Any default value
}
}
You need to implement the heightForHeaderInSection so you can collapse that header. See below:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if section == 0 {
return 1.0
} else {
return 32.0
}
}
You can set the appropriate value for the else condition for your needs but this gives you the idea.
Update #1: I found this link in searching that may help as well: http://stackoverflow.com/a/23955420/3965
It recommends using GLFloat's minimum value instead:
if section == 0 {
return CGFloat.leastNormalMagnitude
}
return tableView.sectionHeaderHeight
Implement heightForHeaderInSection and return the height you want for the first section.
Also, you wouldn't normally implement titleForHeaderInSection and viewForHeaderInSection. Just put your switch statement in viewForHeaderInSection to set the text for your label.
And you don't need to put your UILabel into headerView, just return the label. Or instead of UIView, use UITableViewHeaderFooterView.
In iOS 11, my section headers always appear, regardless of whether the items are 0 or more.
On iOS 10 devices, my code works and sections disappear when item count is 0. But on iOS 11, the same code has no affect.
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if sections[section].items.count > 0{
return sections[section].title
}else{
return nil
}
}
In iOS 11 if you implement only titleForHeaderInSection and return nil, you will not see a header view. But if you also implement viewForHeaderInSection, regardless of what you return, there will be a section.
This alone will not show a section header:
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return nil
}
This will show a section header with no title:
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return nil
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
So both methods may return nil and the header will be visible. If only titleForHeaderInSection is implemented, no header shows up. That does seem to be a case only in iOS 11. Not sure if it's a bug or a way to force developers chose one method of the two. But the docs confirm this behaviour about titleForHeaderInSection:
"Return Value: A string to use as the title of the section header. If you return nil , the section will have no title."
So nothing about showing or not showing, this method only returns the string for the title. Which makes sense. But what does look like a bug is that returning nil in viewForHeaderInSection will show the section header.
To hide a section header for, say, section 0, implement the following method like so:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if (section == 0) {
return CGFloat.leastNormalMagnitude //Now section 0's header is hidden regardless of the new behaviour in iOS11.
}
return tableView.sectionHeaderHeight
}
This solution also works for grouped UITableViews, as discussed below.
Update: If you execute reloadSections(..), this solution causes an
NSException of type 'CALayerInvalidGeometry'
If you return 0 in the if statement however, this crash doesn't occur! :)
Therefore, I would say the best solution I have found (atleast for plain UITableViews) is:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if (section == 0) {
return 0
}
return tableView.sectionHeaderHeight
}
Implement tableView(_:heightForHeaderInSection:) to return UITableViewAutomaticDimension.
This will suppress the section header in exactly the case where titleForHeaderInSection returns nil (and otherwise it will use the default header height from the table).
If you explicitly tell iOS 11 to use a height of 0 in heightForHeaderInSection it will hide the section header. You can still use automatic header sizing by returning UITableViewAutomaticDimension for non-zero height headers. Here's an example of a solution to workaround the iOS 11 bug:
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
guard shouldShowSectionHeader(section: section) else { return 0 }
return UITableViewAutomaticDimension
}
You'll need to implement shouldShowSectionHeader to determine whether or not to show the section header.
iOS 11.3, works in production
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return sections[section].headerViewModel?.makeView(bindImmediately: true)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return sections[section].headerViewModel == nil ? 0 : UITableViewAutomaticDimension
}
return empty view from viewForHeaderInSection
swift 3, 4 and 4.2
To hide your tableView header
tableView.tableHeaderView?.frame = CGRect.zero
and to show it back
tableView.tableHeaderView?.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: 66)
What worked for me:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.01
}
Coding in Swift 3. Have a tableView with custom cells and header.
I have a tableView with custom cells and headers. The headers have two (2) labels in them and have dynamic cell heights since the labels may be long. My problem is the first time the tableView and sections are configured the label appears as it should, HOWEVER, after scrolling down and then back up the headers' layout somehow breaks.
As you can see below, after I scroll down then back up to the cells, the label is getting cutoff.
After printing out what methods are being called I found that the first time scrolling down the tableView the following two (2) override functions are called.
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
print("\(section) heightForHeaderInSection")
print("\(section) returning AUTO for header")
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
print("\(section) viewForHeaderInSection")
let header = tableView.dequeueReusableCell(withIdentifier: "QuestionHeader") as! QuestionHeader
header.delegate = self
header.contentView.backgroundColor = UIColor.groupTableViewBackground
header.questionTextLabel.text = String(questionStringArray[section])
header.questionNumberLabel.text = (String(section + 1) + ")")
return header.contentView
}
But when i scroll back up ONLY the viewForHeader function is called and I think because the height is no longer being set to UITableViewAutomaticDimension the labels get cutoff?
Any ideas?
You should return header instead of header.contentView from tableView: viewForHeaderInSection: method:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableCell(...
...
return header
}
I had a custom cell being used as a tableview section header. I updated to swift 3 and now it is not showing anymore
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableCell(withIdentifier: "tableHeader") as! tableHeader
then i put in some constraints and return header
Your custom class should extend from UITableViewHeaderFooterView and it will work.
I know it's a late answer but for some who did not find the correct answer till. You can use UITableViewSectionHeight. Below is an example:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 80 // this is your row height
}