Self sizing tableview - ios

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.

Related

Resize ScrollView based on TableView size

I'm trying to create layout that it structured like this:
- View
-- ScrollView
--- ContentView
---- CustomView
---- CustomView
---- TableView
---- CustomView
The tableView itself is auto-resizable using "invalidateIntrinsicContentSize" and when I add items - the height of the tableview changes, pushing the custom view below it further down.
Once enough items are added I the bottom custom view is hidden and the scroll doesn't work.
important fact - the bottom custom view doesn't have a bottom constraint. It is pushed down by the it's top constraint to the tableView.
If I do set a bottom constraint - the table view will no longer be dynamically resized.
The intended behaviour:
When a user adds items to the list and the list gets too big the ContentView will be scrollable so the user can scroll to see the bottom view.
The actual behaviour:
When a user adds items to the list and the list gets too big, the bottom view is pushed down and outside of sight and content is not scrollable.
What is happening and how can I fix it?
Below is what I think what is happening.
Since you are using UITableView, it has its own scroll view. So when the UITableView list gets too big, UITableView itself becomes scrollable rather than ScrollView's contentView becoming scrollable.
To achieve what you need, you would have to make the UITableView not scrollable and use the intrinsicHeight of the UITableView to get the actual height of UITableView along with all the items. If you have items with varying heights, it will be a problem because you won't know the height before rendering. With same height for all the rows, you can get the total height of the UITableView and set the height constraint to that value. This will increase the contentSize of the outer ScrollView, making it scrollable.
Apart from UITableView, you can also use UIStackView. This is because you are not using the reusing capabilities of UITableView anyways. Managing the datasource and delegates should not be a big problem.
You can create a constraint for tableview height, And take its reference to your swift file, by dragging it as you take other views. Now in your code, Just do this
tableViewHeightConstraint.constant = tableViewNoOfItems * tableViewCellHeight;
if you have set other constraints perfectly inside scrollview, It should work perfectly. Means TableView should have top, bottom, left, right margined constraints from the ScrollView.
try this code
tblViewHeight.constant = CGFloat( tableview row count * 45 )
var size = contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
if size.height < scrollView.frame.size.height
{
size = scrollView.frame.size
}
contenViewHeight.constant = size.height - scrollView.frame.size.height
scrollView.contentSize.height = contenViewHeight.constant
What I think you could do is:
Disable tableView's scroll tableView.isScrollEnabled = false
Every time a user adds items to the list, reload the tableView
Also using UIStackView with vertical axis and .fillEqually distribution as a Content View would be much more convenient as you won't need to set any positional constraints to your views, but may need to set height constraints if intrinsic content size can't be determined by the engine

Resizing UIView causes child table UITableView to disappear

I have a UIView which contains a label, a button, and a UITableView which populates its data dynamically from a server. I am having trouble resizing the parent UIView to fit its content after the content has dynamically populated. For the purpose of demonstrating my issue, I have made the background of the containing UIView blue.
After populating the TableView with data, the UIView's height does not adjust causing the Tableview data to overflow, seen in the diagram below.
I have set the bottom, leading and trailing space constraints of the TableView to the superview, and top space constraint to the button. The UIView itself has no height constraints set.
I implemented a function to manually recalculate the height of the UIView after populating the content of the TableView. Code for the function below:
func resizeToFitSubviews()
{
var w: CGFloat = self.frame.size.width,
h: CGFloat = 0
for view in subviews {
if view.frame.origin.y + view.frame.height > h { h = view.frame.origin.y + view.frame.height }
}
self.frame.size = CGSize(width: w, height: h)
}
This function works. The UIView resizes to what seems to be the right size, but the TableView disappears after doing so:
Completely lost as to why this occurs. The label and button seem unaffected. I either need to make it so autolayout automatically adjusts the height of the UIView, or make it so that resizing the UIView does not cause the TableView to disappear.
In the View Debugger, the TableView is returning a height of 0 (while the rows are returning 130 as expected given that is what I return in my heightForRowAt function).
Thanks
Don't adjust the view frame if you are using Auto Layout.
Make a height constraint and adjust that to your calculated value.
-
Also it might be easier to manually calculate this height.
height = numberOfRows * heightPerRow

Dynamic height for UITableView ( Not Cell )

I'm trying to have a UITableView that lists all the different HomeKit devices a user has available.
Obviously there is no way to know how many devices they have, so I need to have the UITableView's height in the storyboard change.
I've tried this, which I call in the viewDidLoad() function:
func adjustHeightOfTableView() {
//getting the height of the tableview
var tableHeight = self.tableView.contentSize.height
//the height of the content inside the view
var maxHeight = self.tableView.superview?.frame.size.height
//if the height of the content is bigger then the height of the tableview
if (maxHeight! > tableHeight) {
tableHeight = maxHeight!
//set the tableview height to be the content height
}
//trying to reload the tableview height?
self.view.layoutIfNeeded()
self.tableView.reloadData()
}
I am trying to have some UI Elements under the tableview, and I want them to be a set space from the bottom of the tableview, but also have the tableview be the height that it needs to be, for whatever amount of cells there is.
But it's just not working.
If I'm doing anything wrong, or if anyone knows how to make this work, please let me know.
Thanks!
Note: For this approach you need to have static cell height or figure out a way to know before hand whats the total contentsize height
Assuming you are using constraints, create following constraints on your UITableView (apart from leading and trailing!)
Add a height constraint with a priority of 750 and a bottom spacing constraint of 0 to your super view that will be >= 0 and have a priority of 1000. Create outlet for this height constraint that you created in your UIViewController
Now,
func adjustHeightOfTableView() {
//set the height to be equal to the number of elements multiplied by the height of each cell.
//or use some logic that allows you to know what content size or space the cells will occupy!
tableViewHeightConstraint.constant = dataArray.count * rowHeight
view.layoutIfNeeded()
}
Now if your UITableView height is less than super view, no problems! But if it is greater than screen bounds, it will break the height constraint and become full screen and display the content normally as you expect a UITableView to!
Edit:
Even if you are using UIAutomaticRowDimensions what you can do is add constraints programmatically to your UITableView. i.e
Of course all your other views will still have a bottom constraint to your UITableView.
Create a UITableView in your storyboard with normal leading, trailing, top and bottom to the super view. Fetch the data. Get the contentSize for your UITableView and then remove the bottom constraint. Now add a height constraint that will be the minimum value of your UIScreen.main().bounds.size.height and contentSize.
you can use Automatic Dimensions if you are using autolayouts
in view didload:
let nib = UINib(nibName: "YOURCELLNIB", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "REUSEIDENTIFIER")
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140
Remove the function
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath)
In your code, you have:
tableHeight = maxHeight!
//set the tableview height to be the content height
But this does not change the table height - it only changes some variable that previously was assigned the value of the old table content height. Nowhere in your code do you actually do anything to change the table height.
One way to change the table height directly is to assign it a completely new frame with values from the old frame, except for the frame's height, which you calculate however you like.
Try something like this (adding whatever other logic you need):
oldFrame = self.tableView.frame
newHeight = rowCount * rowHeight
self.tableView.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newHeight)
There is a workaround which can make it seems like the height changes according to the number of the cells.
set tableview height to a proper value when init.
UITableView.init(frame: CGRect.init(x: 0, y: 70, width: self.view.frame.width, height: self.view.frame.height - 350))
set the tableview background color white transparent.
pulldownTableView?.backgroundColor = UIColor.white.withAlphaComponent(0)
set tableFooterView.
pulldownTableView?.tableFooterView = UIView(frame: CGRect.zero)
Below is the result, there are two table in the img. I set the transparent for the front tableview, left img set the backgroundColor to white, right white transparent.
----------------------vs----------------

How to Add Margin Between UITableViewCells Without Being Hacky

A lot of the solutions I've seen here include changing the cell's background to an image and using sections for rows rather than just rows themselves. I'm looking to have only two sections and have each cell expand in height on tap, so neither of those solutions would work.
I saw one solution includes setting the frame of the cell in the layoutSubviews() function like so:
override func layoutSubviews() {
super.layoutSubviews()
self.frame = CGRectOffset(self.frame, 0, 10);
}
When I do this however, it only gives margin to one cell and that's only when I tap on the cell.
Is there a surefire way to add spacing in between UITableViewCells without being hacky and breaking the cell layouts in the process?
I did this yesterday pretty easily with auto layout.
I set the background of the cell and it's content view to clear, then I created a new view and setup constraints all around it and put my labels inside of it. The height changes dynamically based on the label so I needed to use UITableViewAutomaticDimension for the row height and give it an estimated row height as well.
I don't see why this wouldn't work for expanding it on a tap as well, you just might have to reload the cell.
make the cell and it's contentView transparent
contentView addSubview customContentView and layout your cell on customContentView
customContentView pin to contentView top leading trailing with offset 0 but pin to bottom with offset 10 //the margin height

How to manually change UICollectionView height in Swift

I'm hiding rows that are completed in a collectionView.
I call cell.hidden = isCellHidden in cellForItemAtIndexPath when needed.
After I hide 10 rows there is plenty of empty space left and I'd like to trim down the size of the collectionView to only fit the rows that are not hidden.
The collectionView's design is kind of like a tableView.
I know with the tableView all I had to do to achieve this is set:
func section1VisibilityButton(sender: UIButton){
isCellHidden = !isCellHidden
self.tableView.reloadData()
self.tableView.contentSize.height = CGFloat(500)
}
with a collectionView when I try this it will resize it correctly but as soon as I try to scroll down it resizes itself back to the original height including the cells hidden (the cells layer is still hidden but there's tons of empty space bellow the last visible row as if they were visible)
For your issue, there are two options to change the frame of your collectionView/tableView.
If you are using autolayout, you need to create IBOutlet of bottom constraint or IBOutlet of constant height constraint of your tableView (anyone of these constraints, which you are using).
After reload tableView data you need to update constraint by calculating its height.
Suppose you are using constant height constraint and your calculated height is 150(e.g. 3 rows and 50 height of each row).
constraintTableViewHeight.constant = 150;//this will change height
self.view.layoutIfneed(); // this will apply updated constraints to whole view
If you are not using autolayout, you can manually change the height by changing tableView.frame property.

Resources