How to dynamically increase height of scrollview based on uitableview - ios

Currently, I've developed project that UIScrollView, UITableView, another 3 more UIView inputs and UIButton at the last. In that page, UIScrollView height will be dynamically increased based on height of UITableView.
For UITableView there is no more scrolling. Its height will be increased as well based on how many rows are added based on JSON data loaded by Async as follow.
productHeight = 44;
productHeight *= _nsOrderItems.count;
productHeight = productHeight + 100;
if (isHeaderTap) {
self.productTableHeight.constant = 50;
} else {
self.productTableHeight.constant = productHeight;
}
//then the tableView must be redrawn
[self.productTable setNeedsDisplay];
My Problem is I want to increase height of UIScrollView based on height of UITableView.
- (void)viewDidLayoutSubviews {
[self.scrollView setContentSize:CGSizeMake(_scrollView.frame.size.width, _btnEdit.frame.origin.y + _btnEdit.frame.size.height)];
}

You can definitely do that,
First make sure your constraint of cells subView must set to top to bottom in order to calculate the height required for the cell.
Make sure your delegated are set as below
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 44;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Set height constraint of your tableView and make outlet of that constraint.
Add below method to your class where you want to resize your tableView dynamically.
- (void)adjustHeightOfTableview
{
CGFloat height = self.tableView.contentSize.height;
//CGFloat maxHeight = self.tableView.superview.frame.size.height - self.tableView.frame.origin.y;
/*
Here you have to take care of two things, if there is only tableView on the screen then you have to see is your tableView going below screen using maxHeight and your screen height,
Or you can add your tableView inside scrollView so that your tableView can increase its height as much it requires based on the number of cell (with different height based on content) it has to display.
*/
// now set the height constraint accordingly
self.constraintHeightTableView.constant = height;
//If you want to increase tableView height with animation you can do that as below.
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
}
Call this method when you are ready with the dataSource for the table, and call the method as
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
//In my case i had to call this method after some delay, because (i think) it will allow tableView to reload completely and then calculate the height required for itself. (This might be a workaround, but it worked for me)
[self performSelector:#selector(adjustHeightOfTableview) withObject:nil afterDelay:0.3];
});

// Get the supposed to be height of tableview
CGFloat height = self.tableViewMenu.contentSize.height;
// get the height constraint of tableview and give it you respected height
self.constraintHeightTableView.constant = height;
// set the scroll
NOTE:- don't forget to disenable the scrolling of table from storyboard or programmatically
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.viewUserLogin.frame.size.height + height);
[self.scrollView layoutIfNeeded];

Related

How to create dynamic tableview cell with dynamic tableview height in iOS

I want to increase tableview cell and tableview height based on content.
Suppose tableview contain 2 record and his 1st cell height is 100 and 2nd cell height is 25 then tableview height should be 100+25=125.
How to achieve this functionality?
Thanks in advance.
You can definitely do that,
First make sure your constraint of cells subView must set to top to bottom in order to calculate the height required for the cell.
Make sure your delegated are set as below
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 44;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Set height constraint of your tableView and make outlet of that constraint.
Add below method to your class where you want to resize your tableView dynamically.
- (void)adjustHeightOfTableview
{
CGFloat height = self.tableView.contentSize.height;
//CGFloat maxHeight = self.tableView.superview.frame.size.height - self.tableView.frame.origin.y;
/*
Here you have to take care of two things, if there is only tableView on the screen then you have to see is your tableView going below screen using maxHeight and your screen height,
Or you can add your tableView inside scrollView so that your tableView can increase its height as much it requires based on the number of cell (with different height based on content) it has to display.
*/
// now set the height constraint accordingly
self.constraintHeightTableView.constant = height;
//If you want to increase tableView height with animation you can do that as below.
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
}
Call this method when you are ready with the dataSource for the table, and call the method as
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
//In my case i had to call this method after some delay, because (i think) it will allow tableView to reload completely and then calculate the height required for itself. (This might be a workaround, but it worked for me)
[self performSelector:#selector(adjustHeightOfTableview) withObject:nil afterDelay:0.3];
});
If you are running iOS 8+,
You can use:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 80 // your desired or expected height
properties.
for this to take effect you should not have any height set in heightForRowAtIndexpath
You should set the cell constraints i.e., constraints for the elements present inside cell, so the set constraints are enough for the tableviewcell to calculate it's height in run time
Solution in swift 3
Step 1.
Set the top, bottom, leading and trailing constraints (do not make it constant height, but do set a constant height constraint as of now).
Now we gonna change this height dynamically based on the number of cells and its height.
Step 2.
Drag an outlet of that height constraint to the class, and copy this function anywhere in the class.
func adjustHeightOfTableview() {
let height: CGFloat = tableview.contentSize.height
self.outLetOfTheHeightConstraint.constant = height
}
Step 3.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
DispatchQueue.main.async {
self.tableview.reloadData()
self.perform(#selector(self.adjustHeightOfTableview))
}
}
self.tableView.frame = CGRectMake(self.tableView.origin.x, self.tableView.origin.y, self.tableView.frame.size.width, 125);

Dynamically resize UICollectionView's supplementary view (containing multiline UILabel's)

Inside a UICollectionView's supplementary view (header), I have a multiline label that I want to truncate to 3 lines.
When the user taps anywhere on the header (supplementary) view, I want to switch the UILabel to 0 lines so all text displays, and grow the collectionView's supplementary view's height accordingly (preferably animated). Here's what happens after you tap the header:
Here's my code so far:
// MyHeaderReusableView.m
// my gesture recognizer's action
- (IBAction)onHeaderTap:(UITapGestureRecognizer *)sender
{
self.listIntro.numberOfLines = 0;
// force -layoutSubviews to run again
[self setNeedsLayout];
[self layoutIfNeeded];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.listTitle.preferredMaxLayoutWidth = self.listTitle.frame.size.width;
self.listIntro.preferredMaxLayoutWidth = self.listIntro.frame.size.width;
[self layoutIfNeeded];
CGFloat height = [self systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
self.frame = ({
CGRect headerFrame = self.frame;
headerFrame.size.height = height;
headerFrame;
});
NSLog(#"height: %#", #(height));
}
When I log height at the end of layoutSubviews, its value is 149 while the label is truncated and numberOfLines is set to 3. After tapping the headerView, setting numberOfLines to 0, and forcing a layout pass, height then gets recorded as 163.5. Great!
The only problem is that the entire headerView doesn't grow, and the cells don't get pushed down.
How can I dynamically change the height of my collectionView's supplementary view (preferably animated)?
I'm aware of UICollectionViewFlowLayout's headerReferenceSize and collectionView:layout:referenceSizeForHeaderInSection: but not quite sure how I'd use them in this situation.
I got something working, but I'll admit, it feels kludgy. I feel like this could be accomplished with the standard CollectionView (and associated elements) API + hooking into standard layout/display invalidation, but I just couldn't get it working.
The only thing that would resize my headerView was setting my collection view's flow layout's headerReferenceSize. Unfortunately, I can't access my collection view or it's flow layout from my instance of UICollectionReusableView, so I had to create a delegate method to pass the correct height back.
Here's what I have now:
// in MyHeaderReusableView.m
//
// my UITapGestureRecognizer's action
- (IBAction)onHeaderTap:(UITapGestureRecognizer *)sender
{
self.listIntro.numberOfLines = 0;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.listTitle.preferredMaxLayoutWidth = self.listTitle.frame.size.width;
self.listIntro.preferredMaxLayoutWidth = self.listIntro.frame.size.width;
CGFloat height = [self systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
self.frame = ({
CGRect headerFrame = self.frame;
headerFrame.size.height = height;
headerFrame;
});
if (self.resizeDelegate) {
[self.resizeDelegate wanderlistDetailHeaderDidResize:self.frame.size];
}
}
// in my viewController subclass which owns the UICollectionView:
- (void)wanderlistDetailHeaderDidResize:(CGSize)newSize
{
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
// this is the key line
flowLayout.headerReferenceSize = newSize;
// this doesn't look beautiful but it's the best i can do for now. I would love for just the bottom of the frame to animate down, but instead, all the contents in the header (the top labels) have a crossfade effect applied.
[UIView animateWithDuration:0.3 animations:^{
[self.collectionView layoutIfNeeded];
}];
}
Like I said, not the solution I was looking for, but a working solution nonetheless.
I ran into the same issue than you, so I was just wondering: did you ever get a solution without the crossfade effect that you mention in the code sample?. My approach was pretty much the same, so I get the same problem. One additional comment though: I managed to implement the solution without the need for delegation: What I did was from "MyHeaderReusableView.m" You can reference the UICollectionView (and therefore, the UICollectionViewLayout) by:
//from MyHeaderReusableView.m
if ([self.superview isKindOfClass:UICollectionView.class]) {
//get collectionView reference
UICollectionView * collectionView = (UICollectionView*)self.superview;
//layout
UICollectionViewFlowLayout * layout = (UICollectionViewFlowLayout *)collectionView.collectionViewLayout;
//... perform the header size change
}

Customize the UITableview with different height of cells like Facebook feeds in iOS

I have to show the list details in UITableview. But the listing is small for some cells and large/medium height for some other cells. (Like facebook feed) So Tableview have different height for each cell. How can i achieve this?
The list contains,
imageview,2 labels, textview (Label and textview lengths also increased based on its content. It will also affect the tableview cell height). So any help to achieve this would be greatful.
Thanks.
Create your cells with their own .xib. Add the labels, textview imageviews etc as you want. Make sure to pin all them with constraints horizontally and vertically to the leading/trailing/top and bottom of the cell.
Now the trick is to tell autolayout to layout the contentView of the cell, and return the minimum height to fit its content. Since heightForRow is executed before the cell is loaded in cellForRow, we need to use a fakeCell to calculate the height.
To do this lets say, in our controller, we have a tableView showing cells of class DetailsTableViewCell with a nib created for it DetailsTableViewCell.xib :
1) Create a #property in your controller for a fakeCell.
#property (nonatomic, strong) DetailsTableViewCell *fakeCell;
2) In viewDidLoad, initialize it from them nib:
self.fakeCell = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass:#"DetailsTableViewCell" owner:nil options:nil].firstObject;
3) In heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//grab fakeCell, style it with the data you want
[self.fakeCell styleWithDetails:self.details];
//and calculate the minimum height required to fit all content
return [self minimumCellHeightToSatisfyConstraints:self.fakeCell];
}
-(CGFloat)minimumCellHeightToSatisfyConstraints:(UITableViewCell *)cell {
//Makes sure cell's width is equal to tableView's width (can be wrong since they are in separated nibs)
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(cell.bounds));
//Force autolayout to recalculate all subview's frames
[cell layoutIfNeeded];
//Get the minimum height required for the cell to fit all its content
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;
}
Important: If you forget to pin all subviews in the .xib vertically from top to bottom to the cell, calling systemLayoutSizeFittingSize:UILayoutFittingCompressedSize will return 0 and won't work.
#define CELL_MARGIN 10.0f
-(CGFloat)heightForTableView:(NSString*)text widthOfTableview:(CGFloat)tableViewWidth{
CGSize constraint = CGSizeMake(tableViewWidth - (CELL_MARGIN * 2), 20000.0f);
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGFloat height = MAX(size.height, 44.0f);
return height + (CELL_MARGIN * 2);
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self heightForTableView:cell.titleLabel.text widthOfTableview:CGRectGetWidth(tableView.frame)];// cell.textlable.text is your lable.text
}
This code might helps you :)

Explicitly set row height for some rows but use estimatedRowHeight for others

I have a UITableViewCell which contains a UIWebView as a subview. The web view fills the entire cell. I am using estimatedRowHeight to calculate the row height. However, at the time the table view is built, the cell has no height because the web view has not loaded it's content, therefore the cell has no content. Because of this, estimatedRowHeight returns 44 instead of the correct height of the web view content.
Does anyone know how I can correctly calculate the height of a row when the content is not immediately set? Is there a way to estimate the height of some cells and explicitly set the heigh of other cells, in the same table view?
This is how I am using estimatedRowHeight:
self.tableView.estimatedRowHeight = 200;
self.tableView.rowHeight = UITableViewAutomaticDimension;
I am not using the delegate method. I have tried it, but it does not change the result. The cell I am using has a xib which also uses Auto Layout constraints. To be clear, the cell does appear and the web view does get added to the cell. The problem is that the height of the cell is not big enough to show the entire web view. The web view loads an HTML embed code for an audio player.
I did some fiddling around and found out that you can use UITableViewAutomaticDimension in the following way:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableSection *tableSection = (self.tableModel.sections)[indexPath.section];
if (tableSection.sectionType == TableSectionTypeWebView)
{
return 120;
}
else
{
return UITableViewAutomaticDimension;
}
}
This basically says, use a height of 120 for any WebView sections but for everything else I want you to figure out the height. I am using a my own custom table model here (i.e. TableSection, sectionType, etc...)
I had to add self.tableView.estimatedRowHeight = 200; to my init method for that to work.
Now I can provide an estimated row height but also explicitly set a row height for some sections, or even some rows if I wanted.
I haven't seen any documentation for this, but I tested it with variable length strings and it held up just fine.
You make your class a UIWebViewDelegate and then in and set your class as a delegate to every single UIWebView in your UITableViewCell
-(void)webViewDidFinishLoad:(UIWebView *)aWebView {
CGRect frame = aWebView.frame;
frame.size.height = 1;
aWebView.frame = frame;
//Asks the view to calculate and return the size that best fits //its subviews.
CGSize fittingSize = [aWebView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
aWebView.frame = frame;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
Now you can get the height of the UIWebView and set it to the rows height, because the following method will be called again once a 'beginUpdates' is invoked
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath {
return _webView.frame.size.height;
}
Hope This helps

Resize UITableView with Auto Layout with programming

I have a UITableView dynamically loading data from a remote XML. It has 1 prototype cell of height = 70.
I try to set the height of the UITableView to:
Cell Height x Number of Rows
using [tbl setFrame:CGRectMake(0, 0, 320, 70 * contents.count)]; but the table size remains unchanged.
Since I'm using Auto Layout, some suggests that I should use:
[tbl setTranslatesAutoresizingMaskIntoConstraints:NO];
Added, but size remains unchanged.
How to change the size of the UITableView in order to fit exactly the size of data?
Assuming your tableView is anchored on top side, and it's height is unconstrained (use a placeholder intrinsicSize to allow this on the interface builder), you can extend UITableView into something like this and you won't need any code:
#implementation ExpandedTableView
- (void)reloadData
{
[super reloadData];
[self invalidateIntrinsicContentSize];
}
- (CGSize)intrinsicContentSize
{
return self.contentSize;
}
#end

Resources