Dynamic height for UITableView header view - Problem with huge margins - ios

In my application I have a UITableViewController that allows preview of a photo and description to that photo, which are both contained in the UITableView's header. The description label needs to be multiline-compatible, so the lines amount is set to 0, obviously the next problem is to make the UITableView header dynamically resizable to adjust it's size to the amount of text in the description label.
The description label is set to fit it's content with self.descriptionLabel.sizeToFit()
The UITableView header view is also set to dynamically resize to fit it's contents via the widely accepted answer from this post:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
var headerFrame = headerView.frame
//Comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
This solution resizes the UITableView header view, but for some reason gives my description label absolutely huge margins stretching up and down from the text. I highlighted my description label with self.descriptionLabel.backgroundColor = .red to be sure it's the description label that stretches and that indeed seems to be the case. I tried a multitude of other solutions, but this is the only one that actually at least resizes the UITableView header view, so I'm stuck with this at the moment.
The margins, however, are absolutely enormous and I can't find a solution to make them fit the description label text size. Any help will be greatly appreciated, because I can't get my head around it.
I added screenshots displaying the problem at the end of this post.
This is the top of the UITableView header, with the red displaying the UILabel beginning to stretch down
This is the actual text of the UILabel
And finally this is where the UILabel stretching finishes with the UIButton at the bottom. I am at a complete loss of how to fix this.
This is my header view UI in Interface Builder
And these are the detailed constraints of my description label in Interface Builder

I believe since the translatesAutoresizingMaskIntoConstraints ARE set to false for the headerView the layout is not auto adjusted once it's calculated in layoutSubviews method.
Try setting the translatesAutoresizingMaskIntoConstraints to true to set the headerView height properly as below:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
var headerFrame = headerView.frame
//Comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
translatesAutoresizingMaskIntoConstraints = true
}

Related

Autolayout working wrong

I'm working on profile screen, there is a UITableView inside the ViewController and I placed all user info inside the UITableView Header. To make screen more accurate due to different sizes of "About" label I use Autolayout with this code:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
func sizeHeaderToFit() {
let headerView = tableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
var frame = headerView.frame
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
Everything works fine, but the last string of About label is not showing, text interrupts (I set word wrap):
Add a height constraint on tableview header and try to change height constraint where you are changing frame. As you are using autolayout so you should play with the constraints rather than playing directly with the frames
you can follow this tutorial
https://useyourloaf.com/blog/variable-height-table-view-header/
Hope it will help you.

Cant get width of table view or table view header until after viewDidAppear

func setTableHeader(){
let headerWidth = Double(tableHeader.frame.width)
// also tried let headerWidth = Double(tableView.frame.width)
print("header width is \(headerWidth)")
}
func viewWillAppear(){ output --> 351
setTableHeader()
//also tried in View Did Appear
}
func viewDidDisappear(){ output --> 296
setTableHeader()
}
The outputs refer to what occurs when run on Iphone SE. I designed my tableview and table header with constraints in Storyboard with the Iphone 6 display. I was able to get the result I wanted by changing setTableHeader to reflect view.frame.width - (left and right constraints from storyboard), however I was wondering why I couldnt get this to work, and what the less hacky work around would be? Also I am in actuality setting the dimensions of table header's subviews inside setTableHeader, but these subviews have no constraints (programatically nor in storyboard), in case that is relevant.
Thank you.
Try this , The solution was to override UIViewController().viewDidLayoutSubviews(), get the proper size of the header view based on it’s constraints, set the frame on the header, and reset it as the table header view
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the header view
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
Please try to add this code to viewDidLayoutSubviews method.
All views got their actual sizes only alter layout. Method viewDidLayoutSubviews called before viewDidAppear and after viewWillAppear.

Auto size view on tableViewController

I am having a view at the top of my tableViewController. The view is called viewBackground. It is there to show the post the user clicked on and the table cells will show the comments.
What I want to do is to resize the viewBackground depending on the size of the label theLabel. I have done this in the cells by setting the label in the cells to 0 lines and by implementing this little code :
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 205
How do I do the same with my viewBackground?
This is a picture of the tableView:
You can create table view with two sections. First section will contain selected post; second section - comments. If you need, i can provide some code.
Added this code and it helped!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
//Comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
I suggest to make a header for your current tableView -instead of adding an external view above it-, and then you can follow this answer, it should helps you of how you can calculate the label's string height and returning it in the tableView(_:heightForRowAt:) method.
Hope this helped.

Swift - How dynamically fixed UITableViewHeaderFooterView height size?

I have just tried below lines of code, but I doesn't work correctly. I wonder that how can I provide this case for header or footer programmatically, may be using autolayout I don't know exactly which one solved my problem. I'm using xib file both of UITableViewHeaderFooterView.
If someone explain I would be great.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the footer view
if let footerView = tableView.tableFooterView {
let height = footerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var footerFrame = footerView.frame
if height != footerFrame.size.height {
footerFrame.size.height = height
footerView.frame = footerFrame
tableView.tableFooterView = footerView
}
}
}
The UITableView calculates the HeaderFooter before they displayed, you can't update that height without calling reloadData() again.
Have you tried to use AutoLayout on those HeaderFooterViews?
I would set up the view from xib with the right constraint, and then:
self.tableView.estimatedSectionHeaderHeight = 100
self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension

UITableView tableHeaderView using autolayout height too small

I'm trying to create a UITableView with a header view using autolayout using storyboards. It looks fine in Xcode, but when I run the app, it does not look the same.
In Xcode:
In the app:
The image has a constraint for 150x150, and there are 8-high constraints between the top-image, image-middle label, middle description-label and description label-bottom.
Both labels have numberOfRows set to 0 and lineBreakMode set to ByWordWrapping.
I have tried settings the frame via:
if let headerView = self.tableView.tableHeaderView {
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
println("Setting height to \(height)")
var headerFrame = headerView.frame
headerFrame.size.height = height
headerView.frame = headerFrame
self.tableView.tableHeaderView = headerView
}
One of the original issues was that I had some erroneous constraints (that for some reason Xcode only started complaining about today), so I have removed those and set a contentHuggingPriority of 252 (higher than all others) on the app name label. When I resize the header view manually in the storyboard the image and app name label stay the same height, and the description label grows. It would appear that the apps uses the size of the header in the storyboard at run time, and doesn't get the height from its children.
Answering my own question here:
There are 2 steps that seem to get this to work. The first is in ViewDidLoad, and the second is in viewDidLayoutSubviews:
var headerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
if let currentTableHeaderView = self.tableView.tableHeaderView {
currentTableHeaderView.removeFromSuperview()
}
// Setting the table header view with a height of 0.01 fixes a bug that adds a gap between the
// tableHeaderView (once added) and the top row. See: http://stackoverflow.com/a/18938763/657676
self.tableView.tableHeaderView = UIView(frame: CGRectMake(0, 0, CGRectGetWidth(self.tableView.frame), 0.01))
self.headerView = AboutTableViewHeaderView(frame: CGRectZero)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let tableHeaderView = self.headerView {
var frame = CGRectZero
frame.size.width = self.tableView.bounds.size.width
frame.size.height = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
if self.tableView.tableHeaderView == nil || !CGRectEqualToRect(frame, tableHeaderView.frame) {
tableHeaderView.frame = frame
tableHeaderView.layoutIfNeeded()
self.tableView.tableHeaderView = tableHeaderView
}
}
}
Hopefully that all makes sense. The viewDidLayoutSubview code is via http://osdir.com/ml/general/2014-06/msg19399.html

Resources