How to refresh a dynamic TableView Footer (Not Section Footer) using autolayout - ios

I have a TableView footerView with two labels that can hold multiple lines.
I'm using this code in viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
label1.preferredMaxLayoutWidth = tableView.frame.size.width - Constant.footerMarginSpace
label2.preferredMaxLayoutWidth = tableView.frame.size.width - Constant.footerMarginSpace
footerView.setNeedsLayout()
footerView.layoutIfNeeded()
footerView.frame.size.height = footerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
tableView.tableFooterView = footerView
}
This is working fine (the labels will eventually spread over multiples lines and the size of the view is adjusting) when the labels value are set before the view is shown, but If I update their content after, the view won't update its layout, even when using tableView.reloadData().
If I push another view on top of my view, and dismiss it, the layout will be correct.
What is the proper way to update the layout and size of my TableView footer ?

I think it might be easier to calculate how many lines you need based on your data and return the correct height using
tableView(_:heightForFooterInSection:)
This method will be called to layout your tableview when the reloadData is called.

Related

How to change the size of the view based off how many cells are in a UICollectionView

I am creating a collection view full of menu items. The collection view is supposed to be tall enough so that it fits perfectly in the size of view.
I can set the UICollectionView with a fixed height constraint, but if there are 20 items then that height might be too small or if there are 5 cells the height would be too big.
Is there a way to change the frames of the main view and the Collection view based off how many cells the view will have? Possibly in the viewDidLoad() function?
set the height constraint of the collection view in storyboard with any value , and hook it as IBOutlet say collectionHCon
override func viewDidLayoutSubviews() {
self.collectionHCon.constant = numberOfItems * itemHeight
super.viewDidLayoutSubviews()
}

Self sizing tableview

First of all this is not a question about how to automatically size the cells inside the tableview, moreover how to automatically resize the entire tableview.
So I have a scrollview which has a tableview and 2 other views as its subviews. The tableview's cells already automatically resize itself, again this question is not about the individual cells. However, the tableview does not resize at all.
What I have done:
1) Set up the tableview to have a top, bottom, leading and trailing constraint
2) Set the cells up to have auto layout enabled
3) * I do not know the cell size at build time
4) I have disabled scrolling mode on tableview
So long story short, how can I go along to get the tableview to resize itself?
Edit
The cells contain a label which can have various lines of text, so therefore the cells, which use auto layout, should then determine the height of the table view.
The following images show how the view is set up:
As you can see the tableview is only a small part of the view and since the scrollview from the tableview is deactivated there should, and aren't, any scrolling problems.
EDIT 2
This is actually how it should end however, i am calculating this on my own and everytime I want to make a small change to the cells the whole code, which calculates the height of the cell, needs to be rewritten and it is quite difficult for me to get the height just right.
Edit 3
Until now I had a height constraint on the tableview which I calculated manually, however removing this constraint and trying to let auto layout handle the tableview height size creates the following error:
Scroll View
Need constraint for: Y position or height
I can conclude therefore that the tableview does not know how to automatically calculate the height based on its cells with autolayout.
You don't need to create a height constraint or set frame whatsoever. Create a subclass of UITableView and recalculate its intrinsicContentSize every time its contentSize changes aka new data added or removed. Here is all you needed:
class SelfSizingTableView: UITableView {
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
setNeedsLayout()
}
}
override var intrinsicContentSize: CGSize {
let height = min(.infinity, contentSize.height)
return CGSize(width: contentSize.width, height: height)
}
}
You can change your UITableView's frame by using tableview.frame = CGRect(x: <some_x>, y: <some_y>, width: <some_width>, height: <some_height>)
If your UITableViewCells use auto layout then they should resize when the UITableView's frame changes.

How to make uitableview to fill all my view?

I am very new to iOS. Can anyone tell me how to fix this, to fill all my view with UITableView:
I am trying to do it with auto layout and constraints but I don't know why isn't working.
What you want to do is to have the cells at equal height so that all cells together fit exactly the height of the screen.
You cannot do that with auto layout. You have to tell the table view the height you'd like the rows to have like so:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let heightOfVisibleTableViewArea = view.bounds.height - topLayoutGuide.length - bottomLayoutGuide.length
let numberOfRows = tableView.numberOfRowsInSection(0)
tableView.rowHeight = heightOfVisibleTableViewArea / CGFloat(numberOfRows)
}
Note that this code assumes that you don't implement the method tableView(_:heightForRowAtIndexPath:).
To fill the whole view, with a table view, I use four constraints:
One. tableView centre = view centre.
Two. tableView width = view width.
Three. tableView vertical distance to top layout guide = 0
Four. tableView vertical distance to bottom layout guide = 0.
To fill everything with the same color, I set the backgroundColor of the tableView, then set the backgroundColor of the cells to [UIColor clearColor].

How to resize a UITableview to fit the dynamic number of rows

I'm building a View Controller with some elements of UIView and one UITableView. This view will be larger than an iPhone screen than I put all those elements in a UIScrollView and configured auto layout.
The number of rows in a UITableView are dynamic, and I need that all content be visible in a single screen. I don't want to enable UITableView scroll property because will be complete messy working with master UIScrollView and the UITableview scroll
I have been researching for days on StackOverflow for posts about this problem and couldn't find anything like my problem.
This is the code trying to resize UITableView and UIScrollView
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Configuring dataSource an delegate
self.tableView.dataSource = self
self.tableView.delegate = self
// Sizing all the elements inside scrollView
var contentRect = CGRectZero
for view:UIView in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
print(contentRect)
// Trying to resize the tableView and scrollView to fit more contents
self.tableView.contentSize = CGSizeMake(scrollView.frame.width, CGFloat(441))
scrollView.contentSize = CGSizeMake(scrollView.frame.width, CGFloat(877))
scrollView.delaysContentTouches = true
scrollView.canCancelContentTouches = false
// Looking for a new size and nothing change
var contentRect2 = CGRectZero
for view:UIView in self.scrollView.subviews {
contentRect2 = CGRectUnion(contentRect2, view.frame)
}
print(contentRect2)
print("Tableview width \(self.tableView.frame.width)")
print("Tableview height \(self.tableView.frame.height)")
}
The sample project on GitHub
I appreciate any help
Layout of ViewControl:
The short, but wrong answer to this question is to add an autolayout constraint to the tableview constraining the height to a constant size. You can set it to your best guess, but at runtime set it equal to the tableview's content size's height.
The problem with this approach is that it completely eliminates the benefit of tableview's and reusable cells. Table views are expressly designed to minimize the memory footprint of your view. It takes a lot more memory to store the view (cell) that represents your data than it does to keep track with data you're filling these cells in with.
So instead, the better solution is to use multiple sections within a table view. Using the example from your screenshot, your parent scroll view will be a tableview instead of a scroll view. It would have four sections. In the first section, we return a single row with the yellow view which is configured as a table view cell, and we do the same in the third & fourth sections. In the second section, we return however many rows you were using in the table view.
Alternatively, the yellow view could be thought as the table header, and the blue & red views become the table footer, with the rows in the middle.

custom tableView's header view overlap the table view cells

I designed a custom view as my UITableView's header view. just like this
(I just put image link here instead of image since I don't have 10 reputations.)
http://i.stack.imgur.com/KhNbE.png
Then in my UITableViewController I use this view as tableHeaderView
override func viewDidLoad() {
tableView.tableHeaderView = headerView!
//...other things
}
I got text from a JSON to fulfill the ContentLabel. If the text is long, the headerView will overlap cells just like below image.(short text is OK)
http://i.stack.imgur.com/gtO2g.png
Section is visible but two lines of cell have been overlapped by the headerView.I'm not sure if I did wrong constraints or code on ContentLabel. Below is the code I configured the contentLabel in TopicHeaderView.swift
var content: String? {
didSet {
self.contentLabel.text = content!
self.setNeedsUpdateConstraints()
self.updateConstraintsIfNeeded()
self.setNeedsLayout()
self.layoutIfNeeded()
}
}
func setFrameHeight(height: CGFloat) {
var frame = self.frame
frame.size.height = height
self.frame = frame
}
override func layoutSubviews() {
super.layoutSubviews()
self.contentLabel.preferredMaxLayoutWidth = self.contentLabel.alignmentRectForFrame(contentLabel.frame).width
self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.bounds.size.width
self.authorLabel.preferredMaxLayoutWidth = self.authorLabel.bounds.size.width
self.setFrameHeight(CGRectGetMaxY(contentLabel.frame) + 8)
}
I browsed similar questions in SO but seems I can't find a solution to fix my problem. Can anyone help on this?
EDITED:
I logged the origin CGPoint of my first tableView cell and headerView's height. It shows the right number which means the first cell is right next to the header view vertically. There is a 22 points gap because of the height of section of course.
headerheight:600.0
first cell's y: 622.0
So maybe it's the label problem that its height is too big to exceed the bounds of TableView headerView? I'm not sure.
EDITED:
Strange things happen. I logged the y value of headerView's bottom,contentLabel's bottom and first UITableViewCell's origin. Please see the image from the link in the question comment below(still need 10 reputation)
As you can see, from the value in console, the view sequence from top should be "contentLabel's bottom(value:224) - headerView's bottom bounds(value: 232) - first cell's origin(value:254)". But in simulator, the sequence is totally messed up.It turns "headerView's bottom bounds - first cell's origin - contentLabel's bottom"
I really appreciate if anyone can help on this.
Problem is, that UITableView does not automatically change positions of cells when its headerView's height changes. Thus you need to reload UITableView every time TopicHeaderView.content changes.
Select that header view, or imageView what you have there, and check Clip Subviews in Attributes Inspector tab.
This worked for me.

Resources