Swift UITableView auto height - ios

I have a design, where UITableView is in UIScrollView with other content and the content is scrolling with the table data. It means, that table has scrolling disabled and is stretched to full size.
I'm using autolayout and the table has height constraint, set to some default value.
Then I have overwritten viewWillLayoutSubviews method:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if (self.itemTableView.contentSize.height > 0) {
self.itemTableViewHeightConstraint.constant = self.itemTableView.contentSize.height
}
}
With this method in place, when I called self.itemTableView.reloadData() after loading the data from the REST, the height constraint was set to table content height, which was great.
This used to work for me in many projects, but for some reason it is not working in the latest one, written in swift 4. Does something changed? Why tableView reloadData() method does not invoke viewWillLayoutSubviews()?

The problem was that the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) was not calling self.view.setNeedsLayout() before returning row.

Related

Resize iOS tableviewcell contaning several views

I'm looking for a way to resize a tableView cell with a more complex layout.
To simplify the description of the problem the tableview cell consists of three views. One view that defines the "background" of the cell and two additional views (each of them with a height 45% of the background cell.
One of these additional views is tagged to the top of the background view the other one to be bottom.
If the user taps on the tableview cell it should shrink in a way that only the top view is visible and after an additional user tap it should resize to its full size again.
The user tap is handled by the function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
and the resizing is done by
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
Unfortunately, after shrinking the tableview cell, both addition views are displayed with half of their original hight.
var selectedCell = -1
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
tableView.deselectRow(at: indexPath, animated: true)
if (selectedCell != indexPath.row)
{
selectedCell = indexPath.row
}
else {
selectedCell = -1
}
tableView.beginUpdates()
tableView.endUpdates()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if (indexPath.row == selectedCell)
{
return 65
}
else {
return UITableView.automaticDimension
}
}
I'm now looking for a way to change the code in a way, that after shrinking only the upper view is visible.
Example picture of the fully visible cell
In the example picture, after shrinking the tableview cell should only display the red view.
Thanks in advance
Patrick
The reason is that when the cell is shrunk, your views (since their sizes are defined relative to parent's size) also gets shrunk and are visible with half size. They are not really gone/hidden from the view. What you need is to hide/remove them from the view.
It looks from your description that you are using plain constraints to achieve this. This can be done by just using constraints but its a lot more work. So I will mention two ways to get this done:
Using just constraints
When the user taps your cell, you need to make the height of the bottom view 0. Also, if you do not want the middle 10% part to show when the cell shrinks, you would need to create an additional constraint from the bottom of the top view to the bottom of the contentView of the cell. Similarly, your bottom view will also have two height constraints, one for 45% and one for 0.
The idea is to have both these constraints at the same time but with different priorities. The initial constraint will have higher priority and the other one will have a lower priority. Although these constraints will be contradictory, iOS will take the higher priority one to render the view.
Next, you would need to toggle the active property of the higher priority constraint on user tap which will in turn let iOS use the lower priority constraint to render the view.
Use stackview:
iOS 9 introduced stack view which is a helper view. It basically, handles constraints for you, at least some part of it. When you hide one view from the stack view's children, stack view will automatically make the height of that view to be 0. read up more about vertical stack view and you will get the idea.

Auto Fit Height of UITableView

I have multiple tableviews in my ViewController and each tableview has different number of rows, please see image below:
Is there any way to match the height of the tableview according to its number of rows?
In your view controller, put code similar to the following:
override func updateViewConstraints() {
super.updateViewConstraints()
tableViewHeightConstraint.constant = tableView.contentSize.height;
}
Where tableViewHeightConstraint is an #IBOutlet to NSLayoutConstraint. Obviously with multiple table views you will need to reference multiple height constraints, so you'd just have one line in updateViewConstraints for each table view you want.
The advantage of this technique is that it takes all of the table view's content into account. It handles grouping, automatic cell height, etc.
You have to determine how many cells you have, then override
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return CGRectGetHeight(self.tableView.bounds) / self.tableView.numberOfRowsInSection(indexPath.section)
}
Btw, I suppose making use of header cells would be a better idea than implementing multiple tableviews.

iOS Table View Cell Dynamic Size - LayoutSubviews

I'm trying to call [cell layoutIfNeeded] and [cell.contentView layoutIfNeeded] as I have a number of subviews that have custom constraint logic that I implement in the cell's layoutSubviews method.
However, despite calling both those methods in cellForRowAtIndexPath, when I check the frames of the subviews in the contentView, their frames are still incorrect. I've ensured to call layoutIfNeeded on each subview as well.
Any ideas on why this is happening? The frames are only correct after the cell is displayed, but the height isn't being calculated properly before that.
edit:
I am trying to use the new auto size feature in iOS 8. It's not resizing properly because I have some complicated logic in terms of where my labels go if one is larger than the other. It's not as simple as stacking the labels together - which is why I need the subviews to be laid out correctly before the height is calculated.
I'm not sure what is happening with the code without more information but you should consider using Auto resizing cells. This will resize your cells automatically without having to deal with layout subviews.
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 100
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
The layout subviews solution is still used in some edge cases but was mainly used before UITableViewAutomaticDimension came out.

Dynamically Changing all of the Uitableviewcells height to fill the uitableview in swift

I have been having an issue for quite some time with regards to an ios app I have been building in swift. I have a Uitableview ,with dynamic properties, embedded within my view controller. And what I want to do is size all of the cells to fill the table view. I have tried:
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
tableView.reloadData()
let height = tableView.contentSize.height
return height
}
but this just returned zero for the height of the uitableview.
Help would be very appreciated.
You don't want to call reloadData() from this delegate method, since this method is getting called because the data is being reloaded.
In fact, you don't want to use this method at all if all of your cells are going to be the same height. Just set the table view's rowHeight property.
Assuming this is happening from a view controller, I'd override viewDidLayoutSubviews() to get notified when the table view size changes and set the row height there:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.rowHeight = tableView.bounds.height
}
You may or may not need to add reloadData() there to get the table view to update. Try it without it first. Setting the row height may automatically trigger a reload.
Also notice that I don't use contentSize to get the height. That's the height of all of the cells, and it's determined by the row height you set. So again, using it gets a little circular.

iOS UITableViewCell asynchronous image download and change UITableView height accordingly with the use of auto layout

I'm writing an application where images are downloaded asynchronously and when the images are loaded its scaled but I just want to show as the same size within the UITableViewCell.
Since i'm using auto layout I have tried to change the height constraint through an IB but then I have to change the height of the cell and I though of reloading each cell after downloading each image but not sure if it's a good idea.
Its like the Facebook application where images are loaded and the uitableviewcell is dynamically changed.
Have you tried this delegate method?:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// return downloadedimage.bounds.height etc.
}
Also do not forget to reload data in the viewDidLoad method
tableView.reloadData()

Resources