Update constraints of other elements when UITableView height change - ios

I have a UITableView of which I am defining height as 200 in autolayout. Based on that I am laying out other elements below it like UITextField etc. After that in run time I am fetching data from server and populating in UITableView due to which i am updating UITableView's height based on its content size. Following code I am using for it
self.myTableView.frame = CGRectMake(0 , 0, self.myTableView.frame.width, self.myTableView.contentSize.height)
But due to this, all the elements placed below UITableView still appear at same location which they were while laying out in Autolayout. Means change in height of UITableView makes no difference to them. Following image depicts this problem. What could be possible solution for this?
Here you can see, text fields are getting overlapped on tableview at run time. I am using Swift 2 in Xcode 7.2

If you have all required constraints to your table view and other view.
Don't change the frame of TableView to change height of it.
Instead create IBOutlet of height constraint of your tablview.
e.g. say IBOutlet name is constraintTableViewHeight,
then you can the the hight easily.
constraintTableViewHeight.constant = yourNewHeightValue
//update all constraint of your view and its inner view
self.view.layoutIfNeeded();
Refer Image to create IBOutlet for your height Constraint.

Take IBOutlet of NSLayoutConstraint for tableView Height and set its value not set tableview frame it's not working if you are already given constraints for tableview so change tableview hight constant
like if you take tblHeight for tableView height then set tblHeight.constant = self.myTableView.contentSize.height

You have to create an outlet connection for your table view height constraint:
#IBOutlet var heightConstraint: NSLayoutConstraint!
and when you want to change table view height, you can change the constraint value like this:
heightConstraint.constant = newHeightValue
self.view.layoutIfNeeded()

Related

Change frame of UITableView as per number of rows in cellForRowAtIndexPath in Objective-c

I am resizing my UITableView as per its number of rows in cellForRowAtIndexPath:, that's why the next part of the UITableView was not visible, and for the part of the UITableView which is not visible, cellForRowAtIndexPath: is never called.
But I need to manage TableView frame according to number of rows, please suggest how can i implement this.
Thanks
If you are using autoLayout
To your UITableView add a leading, trailing, top and bottom constraint >=0. Also add a fixed height constraint with a priority of 750. Create an IBOutlet for this height constraint to your UIViewController. Now you can calculate your height using. This will allow your UITableView to only become scrollable once the total height exceeds your screen bounds.
//dataArray = array of your data source
tableViewHeightConstraint.constant = dataArray.count * rowHeight;
Now this height constraint will break as soon as the UITableView becomes scrollable so if you are adding the ability to delete the rows, you will have to create a new height constraint to the UITableViewas soon the number of rows of doesn't take up the entire screen bounds and update the new height constraint appropriately.
You can do using UITableView's content size property .
[tableView reloadData]; // you need to reload data first before set frame
tableView.frame =CGRectMake(tableView.frame.origin.x,tableView.frame.origin.x, tableView.frame.size.width, tableView.contentSize.height);

UITableView dynamic content size height inside UIScrollView with AutoLayout

I have a UIScrollView, inside this UIScrollView I have a UIView and inside the UIView I have some UITextField's, some UILabel's and at the bottom there is a UITableView.
I want the UITableView to fit it's content height, the UIView to fit it's content height and the UIScrollView to fit it's content height.
The UITableView height might change as I add / remove cells from it during usage.
What is the best way to handle it using AutoLayout?
- UIViewController
-- UIView
--- UIScrollView
---- UIView
----- UILabel
----- UITextField
----- UITextView
----- UITableView (at the bottom of the superview)
or as in the xib:
The best way to implement this is to scrap the hierarchy that you have now and do the following...
- UIViewController (or UITableViewController)
--- UITableView
----- UIView (as the tableView.tableHeaderView)
------- UILabel
------- UITextField
------- UITextView
----- Rest of the cells for the table view.
The tableView.tableHeaderView is a single (not reusable, concrete) view that is placed at the top of the content of the table view and scrolls with the content of the table view. It doesn't stick to the top of the screen like a section header view does.
This will allow you to delete the scroll view and place everything inside the table view and still doesn't change the methods that you are using to populate the cells as that remains untouched.
#Jan Greve is correct. But if you still want to do it
Set Bottom Space to Container constraint between the Table View and its super view to 0
Increase/decrease the height constraint of the Table View with each insert/delete (You will have to do this programmatically)
Set the content offset of the scrollview to the new view height
The best part of AutoLayout is now you don't have to worry about UIScrollView content size.
If you set all constraints properly(without any warnings) AutoLayout manage content size for scrollView itself.
Set all subview's constraints but don't add height and width constraints.
And for contentSize add bottom constraint of inner view (subview of scrollview) to UIScrollView. This will increaser scrollView content size and height as per inner view expand or shrink.
For more details: I asked question for same, you can check it. You can find code in question itself.
You can set a fixed height to the tableView and link the constraint to an outlet. Then you'd need to override viewDidLayoutSubviews() in order to set the height dynamically based on the contentSize of the table:
#IBOutlet weak var dynamicTVHeight: NSLayoutConstraint!
internal override func viewDidLayoutSubviews() {
let height = min(self.view.bounds.size.height, self.tableView.contentSize.height)
self.dynamicTVHeight.constant = height
self.view.layoutIfNeeded()
}

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.

Is it possible to subclass UITableView and make it have an intrinsic content size for its height based on the number of cells?

I want my UITableView to only be as tall as it needs to be, as it floats above the main UIViewController's view. If I hardcode a height of 200 and theres' only one cell in the table, it looks silly.
I'm aware in my view controller I could monitor the table view and define the height of it based on the number of cells it has, but the height is a property of the view, and for MVC it doesn't make much sense for the controller to be actively managing a view's height.
Is it possible to have a UITableView subclass, and have it define an intrinsic height based on the number of cells it holds? So with Auto Layout I could add the subclass to my view, specify its width, center it vertically, and perhaps define a "less than or equal" height constraint saying to keep it smaller than 200pts. But for the most part have the intrinsic content size of the view define the height of the view automatically?
This would be just like a UILabel being able to be centered horizontally and vertically with some distance from the left and right, and have it grow and shrink vertically automatically.
Could I feasibly do this with a UITableView subclass?
You can do this easily by having the table view use its contentSize property to "know" how tall it needs to be. This value could be somewhat inaccurate if you're using estimated row heights, but it should be good enough. In this example, I gave the table view a height constraint (as well as width and centerY), and made an IBOutlet to it (heightCon). The only code needed was this,
#interface RDTableView ()
#property (weak,nonatomic) IBOutlet NSLayoutConstraint *heightCon;
#end
#implementation RDTableView
-(void)reloadData {
[super reloadData];
self.heightCon.constant = MIN(200, self.contentSize.height);
}
You would also have to override reloadRowsAtIndexPaths:withRowAnimation: and
reloadSections:withRowAnimation: if you're updating your table with either of those as well.
This feels like a chicken/egg problem you have with designing your UI. Sounds like you want to set the height of the floating tableview based on how many rows it contains * height per row, but the tableview doesn't know any of this information until its been drawn, ie [tableView reloadData] and the corresponding height for row delegate methods are called.
I'd suggest rendering the tableview offscreen somewhere, draw all the rows, sum up all the heights for each row, then present the view to the user with the appropriate CGRect.
When you (re)load data in your TableView, you can do this. The TableView will take only the height it needs
CGRect frame = youTableView;
frame.size.height = CELL_HEIGHT*[yourArray count];
if(frame.size.height > MAX_TABLEVIEW_HEIGHT)
{
frame.size.height = MAX_TABLEVIEW_HEIGHT
}
youTableView.frame = frame;

Scrolling ViewController Content

What I have in the view controller view are :
An image of fixed height
Few labels
Table view with n rows.
Once rendered I want everything here to be inside the scroll the view so the user can scroll the entire screen as needed. Note that the scrollView needs to expand to the entire size of the tableView to show its full contents. I have tried different ways of doing this but unable to do it. I would appreciate any pointers or code segment to get this done.
There are essentially two ways to do so.
tableHeaderView
The first way involves the tableHeaderView property of the UITableView instance you have. You can simply add the UITableView with the constraints/frame/autoresizingMask that allows you to put it full-screen. Done that, you simply do (i.e. in your viewDidLoad):
UIView *headerView = [UIView new];
// Here I am supposing that you have a 200pt high view and a `self.tableView` UITableView
headerView.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 200.0);
UIImageView *fixedImageView = [UIImageView new];
// configure your imageView..
[headerView addSubview:fixedImageView];
// configure labels as you want and add them to headerView as subviews
// Now set `UITableView` headerView
self.tableView.tableHeaderView = headerView;
If you want to use AutoLayout for your tableHeaderView, I suggest you to take a look at this question
Dynamic scrollView
Another way to do this is to to create an UIScrollView, put everything inside, and let it scroll. The downside of this method is that if you are using floating section headers for your UITableView, they will not float due to the fact that the tableView is going to stay fixed, while the parent scrollView is going to scroll.
On the other side, this approach is more AutoLayout friendly due to the fact you can use constraints easily.
To do so, you start adding an UIScrollView to your view, and placing all your other views inside it.
Be sure to add a Vertical Spacing constraint between the first view inside your scrollView (I suppose the UIImageView) and the scrollView top, and between the last view (I suppose the UITableView) and the scrollView bottom, to avoid an ambiguous content size.
You should have something like that (I omitted the labels for the sake of brevity):
Note that every view is inside a parent UIScrollView
After that, add an Height constraint to the tableView, and add an IBOutlet to your view controller subclass, i.e. like this:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *tableViewHeightConstraint;
Now you only need to configure this constraint to reflect the tableView natural height, given by its rows, etc. To do so, you simply calculate the height in this way:
// Resize TableView
CGFloat height = self.tableView.contentSize.height;
self.tableViewHeightConstraint.constant = height;
Now the tableView will resize, and due to its constraints it will adapt the parent scrollView contentSize.
Just be sure to refresh this height constraint anytime you reload the UITableView dataSource.

Resources