I\'ve spent several days trying to figure this out, but there doesn\'t seem to be a solution. I have a very basic UITableView cell with two labels in it. One of them will be
I suggest you don't use stack view for this . Just take custom cell with Label according to your requirement and cell will resize automatically according to Label text size.
Check , here i have attached demo.
UITableview cell Autoresize According to textsize
Output :-
In viewdidload
super.viewDidLoad()
self.tblView.estimatedRowHeight = 100;
self.tblView.rowHeight = UITableViewAutomaticDimension;
self.tblView.setNeedsLayout()
self.tblView.layoutIfNeeded()
Dont forget to set Numberoflines=0
of UILabel property.
Edit :- If you need step by step guide regarding how to set constrain to UILabel ,
check this link ,
Adjust UILabel height depending on the text
Edit :- Here i have take 2 label in cell according to your above image. Just set constrain like this .
Label 1 :- Top , Leading , (Height and width According to your requirement)
Label 2 :- Top , Bottom , Leading from Label 1, Trailing from Superview
I've had very similar problems with autoLayout time and time again. I don't use stackViews much, I typically use them when I have a requirement to add / remove or show / hide a view. When not doing that I opt for just using constraints. I feel the stackviews are quite buggy in quite a few circumstances. Particularly in xib's. I don't know why that makes a difference, but it seems to always cause warnings that I can't remove, when using more than 1 label.
That being said, autoLayout isn't without its issues either, and they all mainly center around preferredMaxLayoutWidth
.
To explain, in order for AutouLayout to calculate the height of a label, it needs to know what width the label can be. When you have multiple labels together, particularly in a tableViewCell, this causes all manner of issues.
I asked and subsequently answered a related question here several days later.
What I did is added a subclass of UILabel that always ensures preferredMaxLayoutWidth
is updated whenever the label is modified. I've recently updated it for iOS 8, iOS 9 and a separate issue I found with modal views.
So the full steps are:
estimatedHeightForRowAtIndexPath
method to make a guess at what the height might be.heightForRowAtIndexPath
.To Calculate the height, you can use something like this (which I have abstracted away into a helper method that I can reuse more easily)
// Set the cell data first, i.e. the label text, any programmatic fonts, font sizes etc.
if let tempCell = cell as? UITableViewCell
{
tempCell.width = tableView.width
let size = tempCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
if tableView.separatorStyle != UITableViewCellSeparatorStyle.None
{
// +0.5 for seperator
return size.height+0.5
}
else
{
return size.height
}
}
I have combined this with a protocol that makes all cells take in a dictionary to set their data. Then I can wrap up this method to take in and pass in a dictionary and reuse the code over and over again.
This has been working seamlessly for me for a very long time.
What you want to achieve can be done by a simple implementation of heightForRowAtIndexPath from the tableView delegate.
If you're unsure what cell should have what height you can just calculate it on runtime.
For this you need to make use of Self sizing table view cell i.e. table view itself calculates height required for each cells to display it's content it can be achieved in two lines of code:
Step1: set estimate height for table view tableView.estimatedRowHeight = 33.0 step2: set tableView's row height to UITableViewAutomaticDimension tableView.rowHeight = UITableViewAutomaticDimension
Here is the tutorial for detail description http://www.appcoda.com/self-sizing-cells/
The approach i will be following:
I will create a UIView SubClass
as a container view
which will contain "word label" and "english word label". it's much easier, seperate code and maintanable.
I will create a cell subclass and use this container view as subView of cell'scontent view
.
I will then use this whole set up in cellForRowAtIndex
.
I will also make some code implementation in heightForRow
. As to make cell row dynamic height at run time u must implementing this method heightForRow
.
The sample Output will some thing like below, it has one UIImageView, and Two UILabel and custom gray seperator. This dynamic cell height is based on what ever image height and what ever string length to be shown in UILabel:
I will share some code snippet for you help. So here goes some conceptual code example for container view:
Most important constraint setup for container view say (VenueViewContainer:UIView) is as below, (for your case you can make labels horizontally, instead of vertical):
"V:|-0-[venueImage]-10-[venueName]-2-[sportName]-10-[lblSeperator(10)]",
"H:|-0-[venueImage]-0-|",
"H:|-20-[venueName]-20-|",
"H:|-20-[sportName]-20-|",
"H:|-0-[lblSeperator]-0-|",
Be sure to set these two properties for your labels:
[lbl setNumberOfLines:0];
[lbl setLineBreakMode:NSLineBreakByWordWrapping];
In your VenueViewContainer implementation file make a method:
/*!This method is responsible for getting view dynamic height .*/
-(float)getViewHeight
{
[self layoutIfNeeded];
CGFloat maxY = 0;
for (UIView *subview in self.subviews) {
maxY = MAX(CGRectGetMaxY(subview.frame),maxY);
}
return maxY;
}
/*!
This method is responsible for set up Venue.
@param venue object
*/
#pragma -mark data population
- (void)setupViewWithVenue:(Venue *)venue
{
self.venueLabel.text = @"my venue my venue my venue my venue my venue my venue my venue my venue my venue my venue my venue my venue my venue my venue last";
self.sportName.text = @"my sport my sport my sport my sport my sport my sport my sport my sport my sport my sport my sport my sport my sport my sport last";
[self.venueImage setImageWithURLString:venue.venueImageUrl defaultImage:[UIImage imageNamed:@"venueinfo_main"]];
}
Now lets talk about custom cell. the UITableViewCell subclass. The implementation is some thinh like this:
@implementation VenueCellTableViewCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self setUpContainer];
[self setUpConstraints];
}
return self;
}
/*!
This method is responsible for set up Container
*/
-(void)setUpContainer{
aViewContainer = [[VenueViewContainer alloc] initWithFrame:CGRectZero];
[aViewContainer setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.contentView addSubview:aViewContainer];
}
/*!constraints setup*/
-(void)setUpConstraints{
/*!
dictionary of views for autolayout
*/
NSMutableDictionary* views;
views = [NSMutableDictionary new];
UIView *parentView = self.contentView;
views[@"aViewContainer"] = aViewContainer;
views[@"parentView"] = parentView;
NSArray* constraints;
NSString* format;
/*!
layouting horizontal
*/
format = @"|-0-[aViewContainer]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[parentView addConstraints:constraints];
/*!
layouting vertical
*/
format = @"V:|-0-[aViewContainer]-0-|";
constraints = [NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:nil views:views];
[parentView addConstraints:constraints];
}
/*!
This method is responsible for set up venue
@param venue object
*/
- (void)setupCellWithVenue:(Venue *)venue
{
[aViewContainer setupViewWithVenue:venue];
}
/*!
This method is responsible to get cell height dynamically
@param venue object
*/
-(float)getCellHeight
{
return [aViewContainer getViewHeight];
}
Thats it related to customixation of your cell.
now talk about cellForRow
and heightForRow
:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
VenueCellTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kVenueCellIdentifier];
if (nil == cell) {
cell = [[VenueCellTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:kVenueCellIdentifier];
}
[cell setBackgroundColor:[[AWPViewFactory sharedInstance]getColor:@"colorWhite"]];
#pragma uncomment below line as per ur need
// Venue *venue = [_dataSourceArray objectAtIndex:indexPath.row];
[cell setupCellWithVenue:nil];
if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
return cell;
}
#pragma mark - UITableViewDelegate methods
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Use the dictionary of offscreen cells to get a cell for the reuse identifier, creating a cell and storing
// it in the dictionary if one hasn't already been added for the reuse identifier.
// WARNING: Don't call the table view's dequeueReusableCellWithIdentifier: method here because this will result
// in a memory leak as the cell is created but never returned from the tableView:cellForRowAtIndexPath: method!
VenueCellTableViewCell *cell = [self.offscreenCells objectForKey:kVenueCellIdentifier];
if (!cell) {
cell = [[VenueCellTableViewCell alloc] init];
[self.offscreenCells setObject:cell forKey:kVenueCellIdentifier];
}
// Configure the cell for this indexPath
#pragma uncomment below line as per ur need
// Venue *venue = [_dataSourceArray objectAtIndex:indexPath.row];
[cell setupCellWithVenue:nil];
return [cell getCellHeight];
}
couple of things i have to not use in above aproach:
1. No use of automatic row height calculation property. 2.No use of estimated height 3.No need of unnecessary updateConstraints. 4.No use of Automatic Preferred Max Layout Width. 5. No use of systemLayoutSizeFittingSize (should have use but not working for me, i dont know what it is doing internally), but instead my method -(float)getViewHeight working and i know what it's doing internally.
i have shared you most relevant code blocks, i have not still worked on swift. But basics remains same. Thanks
As others have mentioned, make sure you're letting the table view cells dynamically resize with:
tableView.estimatedRowHeight = <estimatedHeightValue>
tableView.rowHeight = UITableViewAutomaticDimension
But you've mentioned that you're already doing this.
The rest of the problem is a case of missing constraints. You've already told autolayout that you want your multi-line label to match the top and bottom of the stack view, as in the code below:
englishWordLabel.topAnchor.constraintEqualToAnchor(stackView.topAnchor).active = true
englishWordLabel.bottomAnchor.constraintEqualToAnchor(stackView.bottomAnchor).active = true
But you haven't mentioned to autolayout how this stack view relates to the cell's content view (its superview). This is necessary for dynamically-sized cells because according to Apple:
To define the cell’s height, you need an unbroken chain of constraints and views (with defined heights) to fill the area between the content view’s top edge and its bottom edge. If your views have intrinsic content heights, the system uses those values. If not, you must add the appropriate height constraints, either to the views or to the content view itself.
Your labels have intrinsic content, so that's no problem, but in order to complete the "unbroken chain," you need to tell autolayout that the stack view's top and bottom should be equal to the content view's top and bottom, like so:
// stackView.superview! is the table view cell's content view
stackView.topAnchor.constraintEqualToAnchor(stackView.superview!.topAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(stackView.superview!.bottomAnchor).active = true
Add these two lines after you've added the stack view to the content view, and I believe you'll be in business.
FYI, even if you're programmatically adding these views to your cells like you are, you can still visually debug layout issues quite well with Xcode's awesome Capture View Hierarchy ability. While your app is running from Xcode, click the menu item Debug > View Debugging > Capture View Hierarchy. It's very easy to see what's going on in your view hierarchy, what constraints are active, where your disappearing views went, etc..
2nd FYI, destroying views and then reinstantiating new ones every time a cell appears on screen significantly degrades performance while scrolling. As much as you can, you'll want to take existing views from dequeued cells and reassign their contents with the proper content for that row (and thus avoid view instantiation). You can still do this using your purely-programmatic way of doing things by dequeueing a cell and calling cell.viewWithTag
for each possible view. If the method returns nil
, you do a one-time instantiation of the view for the cell and give the view a tag (let's say 1 for wordLabel
and 2 for englishWordLabel
). Then assign the proper content for the row to the view.