UITableViewCell constraints not seen in iOS 8. How do I mitigate this? - ios

I'm trying to understand why the constraints I have put in place on the Storyboard for my different labels in my cell aren't being done. This is important because my height is dynamic.
I have had this problem for the past 2 days and it's driving me up the wall. No, UITableViewAutomaticDimension is not how I want to do this.
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var cell = self.feed.dequeueReusableCellWithIdentifier("post") as PostCell
var post = self.posts[indexPath.row]
cell.name.text = post.name
cell.timestamp.text = post.timestamp
cell.postBody.text = post.body
println("\(cell.name.constraints())")
println("\(cell.postBody.constraints())")
cell.contentView.setTranslatesAutoresizingMaskIntoConstraints(false)
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(feed.bounds), CGRectGetHeight(cell.bounds))
cell.setNeedsLayout()
cell.layoutIfNeeded()
var height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height
height += 1.0
return height
}
When I print the constraints set for cell.postBody I get []. However, I have 5 constraints. A trailing space to Superview, leading space to Superview, Bottom space to Superview Equals 4, and 2 Top Spaces to 2 different labels Equals 8.
If it isn't possible for my code to see the constraints via the Storyboard, how do I programmatically set these 5 constraints
Updated way I'm doing it:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.feed.dequeueReusableCellWithIdentifier("post") as PostCell
var post = self.posts[indexPath.row]
cell.name.text = post.name
cell.timestamp.text = post.timestamp
cell.postBody.text = post.body
if cachedHeights[post.id] == nil && cell.bounds.height != 0.0 {
cachedHeights[post.id] = cell.bounds.height
}
return cell
}
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
var post = posts[indexPath.row]
if cachedHeights[post.id] != nil {
return cachedHeights[post.id]!
} else {
return 70
}
}
The problem is, I'm not sure cell.bounds.height is completely accurate. In that I mean, I think it is using the height of a previous large cell sometimes (perhaps from the cell it dequeued for the new one.)

The constraints aren't on the labels. That's why their constraints property returns an empty array.
You'll find the constraints on the labels' superview, cell.contentsView.
You can skip the calls to setNeedsUpdateConstraints and updateConstraintsIfNeeded as layoutSubviews will call updateConstraintsIfNeeded.
The old approach I used before switching to self-sizing cells:
I don't have any swift code, but here's some old code from an app before I switched over to self-sizing cells. It (implicitly) uses the constraints placed on the labels in the storyboard cell. The only thing different I did is caching the sizing cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
static LeftDetailTableViewCell *cell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
});
// configure the cell for this indexPath
[self configureCell:cell atIndexPath:indexPath];
// Set the sizing cell's width to the tableview's width
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
[cell setNeedsLayout];
[cell layoutIfNeeded];
// get the fitting size
CGSize s = [cell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];
return s.height + 1.0;
}
Update:
Here's the code I'm using now, for self-sized cells in iOS 8.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
BIBLETableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
[cell adjustSizeToMatchWidth:CGRectGetWidth(self.tableView.frame)];
[self configureAccessoryTypeForCell:cell forRowAtIndexPath:indexPath];
[cell adjustConstraintsToMatchSeparatorInset:self.tableView.separatorInset];
[self configureCell:cell forRowAtIndexPath:indexPath];
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
return cell;
}
My subclassed cell has:
- (void)adjustSizeToMatchWidth:(CGFloat)width
{
// Workaround for visible cells not laid out properly since their layout was
// based on a different (initial) width from the tableView.
CGRect rect = self.frame;
rect.size.width = width;
self.frame = rect;
// Workaround for initial cell height less than auto layout required height.
rect = self.contentView.bounds;
rect.size.height = 99999.0;
rect.size.width = 99999.0;
self.contentView.bounds = rect;
}
- (void)adjustConstraintsToMatchSeparatorInset:(UIEdgeInsets)inset
{
if (self.leadingMargins)
{
for (NSLayoutConstraint *constraint in self.leadingMargins)
{
constraint.constant = inset.left;
}
}
if (self.trailingMargins)
{
for (NSLayoutConstraint *constraint in self.trailingMargins)
{
constraint.constant = self.accessoryType == UITableViewCellAccessoryDisclosureIndicator ? 0.0f : inset.left;
}
}
}

Related

indexPathsForVisibleRows alternatives [duplicate]

I have a UITableView with cells of different heights and I need to know when they are completely visible or not.
At the moment I am looping through each cell in the list of visible cells to check if it is completely visible every time the view is scrolled . Is this the best approach?
Here's my code:
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
NSArray* cells = myTableView.visibleCells;
for (MyCustomUITableViewCell* cell in cells) {
if (cell.frame.origin.y > offset.y &&
cell.frame.origin.y + cell.frame.size.height < offset.y + bounds.size.height) {
[cell notifyCompletelyVisible];
}
else {
[cell notifyNotCompletelyVisible];
}
}
}
Edit:
Please note that *- (NSArray )visibleCells returns visible cells which are both completely visible and partly visible.
Edit 2:
This is the revised code after combining solutions from both lnafziger and Vadim Yelagin:
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
NSArray* cells = myTableView.visibleCells;
NSArray* indexPaths = myTableView.indexPathsForVisibleRows;
NSUInteger cellCount = [cells count];
if (cellCount == 0) return;
// Check the visibility of the first cell
[self checkVisibilityOfCell:[cells objectAtIndex:0] forIndexPath:[indexPaths objectAtIndex:0]];
if (cellCount == 1) return;
// Check the visibility of the last cell
[self checkVisibilityOfCell:[cells lastObject] forIndexPath:[indexPaths lastObject]];
if (cellCount == 2) return;
// All of the rest of the cells are visible: Loop through the 2nd through n-1 cells
for (NSUInteger i = 1; i < cellCount - 1; i++)
[[cells objectAtIndex:i] notifyCellVisibleWithIsCompletelyVisible:YES];
}
- (void)checkVisibilityOfCell:(MultiQuestionTableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
CGRect cellRect = [myTableView rectForRowAtIndexPath:indexPath];
cellRect = [myTableView convertRect:cellRect toView:myTableView.superview];
BOOL completelyVisible = CGRectContainsRect(myTableView.frame, cellRect);
[cell notifyCellVisibleWithIsCompletelyVisible:completelyVisible];
}
You can get the rect of a cell with rectForRowAtIndexPath: method and compare it with tableview's bounds rect using CGRectContainsRect function.
Note that this will not instantiate the cell if it is not visible, and thus will be rather fast.
Swift
let cellRect = tableView.rectForRowAtIndexPath(indexPath)
let completelyVisible = tableView.bounds.contains(cellRect)
Obj-C
CGRect cellRect = [tableView rectForRowAtIndexPath:indexPath];
BOOL completelyVisible = CGRectContainsRect(tableView.bounds, cellRect);
Of course this will not regard the table view being clipped by a superview or obscured by another view.
I would change it like this:
- (void)checkVisibilityOfCell:(MyCustomUITableViewCell *)cell inScrollView:(UIScrollView *)aScrollView {
CGRect cellRect = [aScrollView convertRect:cell.frame toView:aScrollView.superview];
if (CGRectContainsRect(aScrollView.frame, cellRect))
[cell notifyCompletelyVisible];
else
[cell notifyNotCompletelyVisible];
}
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
NSArray* cells = myTableView.visibleCells;
NSUInteger cellCount = [cells count];
if (cellCount == 0)
return;
// Check the visibility of the first cell
[self checkVisibilityOfCell:[cells firstObject] inScrollView:aScrollView];
if (cellCount == 1)
return;
// Check the visibility of the last cell
[self checkVisibilityOfCell:[cells lastObject] inScrollView:aScrollView];
if (cellCount == 2)
return;
// All of the rest of the cells are visible: Loop through the 2nd through n-1 cells
for (NSUInteger i = 1; i < cellCount - 1; i++)
[[cells objectAtIndex:i] notifyCompletelyVisible];
}
You can try something like this to see how much percentage is visible:
-(void)scrollViewDidScroll:(UIScrollView *)sender
{
[self checkWhichVideoToEnable];
}
-(void)checkWhichVideoToEnable
{
for(UITableViewCell *cell in [tblMessages visibleCells])
{
if([cell isKindOfClass:[VideoMessageCell class]])
{
NSIndexPath *indexPath = [tblMessages indexPathForCell:cell];
CGRect cellRect = [tblMessages rectForRowAtIndexPath:indexPath];
UIView *superview = tblMessages.superview;
CGRect convertedRect=[tblMessages convertRect:cellRect toView:superview];
CGRect intersect = CGRectIntersection(tblMessages.frame, convertedRect);
float visibleHeight = CGRectGetHeight(intersect);
if(visibleHeight>VIDEO_CELL_SIZE*0.6) // only if 60% of the cell is visible
{
// unmute the video if we can see at least half of the cell
[((VideoMessageCell*)cell) muteVideo:!btnMuteVideos.selected];
}
else
{
// mute the other video cells that are not visible
[((VideoMessageCell*)cell) muteVideo:YES];
}
}
}
}
If you also want to take the contentInset into account, and don't want to rely on a superview (the table view frame in superview could be something else than 0,0), here's my solution:
extension UITableView {
public var boundsWithoutInset: CGRect {
var boundsWithoutInset = bounds
boundsWithoutInset.origin.y += contentInset.top
boundsWithoutInset.size.height -= contentInset.top + contentInset.bottom
return boundsWithoutInset
}
public func isRowCompletelyVisible(at indexPath: IndexPath) -> Bool {
let rect = rectForRow(at: indexPath)
return boundsWithoutInset.contains(rect)
}
}
From the docs:
visibleCells Returns the table cells that are visible in the receiver.
- (NSArray *)visibleCells
Return Value An array containing UITableViewCell objects, each
representing a visible cell in the
receiving table view.
Availability Available in iOS 2.0 and later.
See Also –
indexPathsForVisibleRows
The code below will let you check if a collection view cell is completely visible through the layout attributes of the collection view.
guard let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame else { return }
let isCellCompletelyVisible = collectionView.bounds.contains(cellRect)
Swift 5+
we can use
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
...
}
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
...
}
let cellRect = tableView.rectForRow(at: indexPath)
let completelyVisible = tableView.bounds.contains(cellRect)
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
CGRect frame = cell.frame;
if (CGRectContainsRect(CGRectOffset(self.collectionView.frame, self.collectionView.contentOffset.x, self.collectionView.contentOffset.y), frame))
{
// is on screen
}
Even if you said you want to check it every time you scrolled, you can also use
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
CGRect cellRect = [tableView convertRect:cell.frame toView:tableView.superview];
if (CGRectContainsRect(tableView.frame, cellRect)){
//Do things in case cell is fully displayed
}
}
- (BOOL)checkVisibilityOfCell{
if (tableView.contentSize.height <= tableView.frame.size.height) {
return YES;
} else{
return NO;
}
}
Maybe for this issue better used next function from UITableViewDelegate
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath)

UITableView self sizing cell scrolling issues with big difference of cell height

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let json = JSON(data: activityTableView.onlineFeedsData)[indexPath.row] // new
if(json["topic"]["reply_count"].int > 0){
if let cell: FeedQuestionAnswerTableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier_reply) as? FeedQuestionAnswerTableViewCell{
cell.selectionStyle = .None
cell.tag = indexPath.row
cell.relatedTableView = self.activityTableView
cell.configureFeedWithReplyCell(cell, indexPath: indexPath, rect: view.frame,key: "activityTableView")
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.mainScreen().scale
return cell
}
}else{
if let cell: FeedQuestionTableViewCell = tableView.dequeueReusableCellWithIdentifier(kCellIdentifier) as? FeedQuestionTableViewCell{
cell.selectionStyle = .None
cell.tag = indexPath.row
cell.relatedTableView = self.activityTableView
cell.configureFeedCell(cell, indexPath: indexPath, rect: view.frame)
// Make sure the constraints have been added to this cell, since it may have just been created from scratch
cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.mainScreen().scale
return cell
}
}
return UITableViewCell()
}
There is difference of 200-400 in height in each cell,So tried implementing the height calculation in estimatedHeightForRowAtIndexPath .I cant the exact estimate height in this method as bcz of calculation
and caching the height in willDisplayCell method but still the scroll jumps like is it taking time to rendering .
The cell is like the Facebook card type layout with lots of dynamic data with variable height text and images.Can someone help me in this .Thanks
If you're able to calculate each cells height, just use the tableView(_:heightForRowAtIndexPath) instead of estimatedRowHeightAtIndexPath property. estimatedRowHeightAtIndexPath is mostly used in smthg like selfsizing cells etc.
Try this one. I hope this could help you. It's worked for me. Actually this calculates the cell height before it displayed.
Thanks.
NSMutableDictionary *cellHeightsDictionary;
// Put this in viewDidLoad
cellHeightsDictionary = #{}.mutableCopy;
// UITableview delegate Methods
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
NS_AVAILABLE_IOS(7_0)
{
NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
if (height) return height.doubleValue;
return UITableViewAutomaticDimension;
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cellHeightsDictionary setObject:#(cell.frame.size.height) forKey:indexPath];
}

Size Class Customization in UITableViewCell

I have a height constraint in UIImageView which is contained in UITableViewCell,
and I want it to be 180 for iPhone and 300 for iPad.
But it doesn't make any effect for iPad.
It is a table view with automatic dimension.
- (void)configureTableView {
self.tableView.allowsSelection = NO;
self.tableView.estimatedRowHeight = 30.f;
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
How can I customize cell's height for iPad?
update:
I fixed it by implementing delegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height = -1;
if (indexPath.section == kQuizControllerSection_Description && indexPath.row == kDescriptionQuizCell_PreviewImage) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
height = 400;
}
else {
height = 180;
}
}
return height;
}
because other cells are resized dynamically (cells with multiline labels) method returns negative number for every other cell. Is it correct approach?
I there is only UIImageView with the same height as cell ....then just pinned all edges of UIImageView and then there is not any need of height constraint of UIImageView...
You just need to set height of cell according to device in heightForRowAtIndexPath method
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if UIDevice.currentDevice().userInterfaceIdiom == .Pad{
return 300
}
else{
return 180
}
}
The other scenario where UIImageView is not the same height as cell....then make a one IBOutlet of height constraint of UIImageView and then change the constant as per your requirement...
if UIDevice.currentDevice().userInterfaceIdiom == .Pad{
heightconstraint.constant = 300
}
else{
heightconstraint.constant = 180
}
This will do it:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// default height is 180
int height = 180;
// if your device is an iPad then it's 300
if (UIDevice.currentDevice().model.rangeOfString("iPad") != nil)
height = 300;
// if you want the UIImageView to follow the height, just go with this:
yourImageView.frame = CGRectMake(0, 0, tableView.frame.size.width, height);
return height;
}

Custom Table View Cell with a big image and a label

I am trying to populate a Tableview with data from an array. I am trying to make a custom table view cell that includes a big square image, same width as the screen and the height should also be the same size as the screen's width. And under the image I want to add a text label. (You can think of it as a very bad copy of instagram)
However, the tableview only shows empty standard cells.
This is my custom cell code:
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
customImageView.contentMode = UIViewContentModeScaleAspectFit;
CGRect frame;
frame.origin.x=0;
frame.origin.y=0;
frame.size.width=self.frame.size.width;
frame.size.height=self.frame.size.width;
customImageView.frame=frame;
[customTextLabel sizeToFit];
frame.origin = CGPointMake(0, (self.frame.size.width+1));
customTextLabel.frame = frame;
customTextLabel.numberOfLines = 0;
customTextLabel.lineBreakMode = NSLineBreakByCharWrapping;
[self.contentView addSubview:customImageView];
[self.contentView addSubview:customTextLabel];
[self.contentView sizeToFit]; } return self;}
and the code from the tableview:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyCellIdentifier = #"MyCellIdentifier";
//UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:MyCellIdentifier];
FeedTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:MyCellIdentifier];
if(cell == nil) {
cell = [[FeedTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyCellIdentifier];
}
if(!emptyTable){
Log *log = [self.feedPosts objectAtIndex:indexPath.section];
cell.customImageView.image = [self loadImage:(int)log.logID];
cell.customTextLabel.text = log.logDescription;
}
return cell;}
Better solution is to use constraints
create constraints like on image below
Create outlets for your height width ( control drag your constraint to cell class)
Change it to screen size/ height on your
cell
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
cell.heightConstraint.constant = UIScreen .mainScreen().bounds.height;
cell.widthConstraint.constant = UIScreen.mainScreen().bounds.width;
return cell
}
Don't forget to set height for row
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UIScreen .mainScreen().bounds.height;
}
Also, you can recreate it on your code programmatically, if you want
Hope this helps
Where are you allocate memory for the cell.customImageView and cell.customTextLabel? This problem should be caused by not generating customeImageView and customeTextLabel. you should according to the following code to modify:
//Setter/Getter
//allocate memory and init customImageView/customTextLable
- (UIImageView *)customeImageView{
if(!_ customImageView)_customImageView = [UIImageView new];
return _customImageView;
}
- (UILable *)customTextLabel{
if(!_customTextLable) _customTextLabel = [UIlable new];
return _cusomTextLable;
}
Then invoke them when initWithStyle:reuseIdentifier:.The other thing should be noticed is that tableView should custome cell's height by implement tableView:heightForRowAtIndexPath:
I almost did what DarkHorse said.
I forgot to do:
customImageView = [[UIImageView alloc] init];
customTextLabel = [[UILabel alloc] init];
and then set the height in
tableView:heightForRowAtIndexPath

AutoLayout in UITableview with dynamic cell height

For dynamic height of my table view cell I take reference from this link.
Using Auto Layout in UITableView for dynamic cell layouts & variable row heights
Here is my code of tableview data source and delegate methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
return arrTemp. count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier=#"AutoLAyoutCell";
AutoLayoutTableViewCell *cell=(AutoLayoutTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil) {
for (id currentObject in [[NSBundle mainBundle] loadNibNamed:#"AutoLayoutTableViewCell" owner:self options:nil]) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (AutoLayoutTableViewCell *)currentObject;
break;
}
}
}
cell.IBlblLineNo.text=[NSString stringWithFormat:#"Line:%i",indexPath.row];
cell.IBlblLineText.text=[arrTemp objectAtIndex:indexPath.row];
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
CGSize expectedlineLabelSize = [cell.IBlblLineText.text sizeWithFont:cell.IBlblLineText.font constrainedToSize:CGSizeMake(280, 1000) lineBreakMode:NSLineBreakByTruncatingTail];
cell.IBlblLineText.numberOfLines=expectedlineLabelSize.height/17;
CGRect frmlbl=cell.IBlblLineText.frame;
frmlbl.size.height=expectedlineLabelSize.height;
cell.IBlblLineText.frame=frmlbl;
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
AutoLayoutTableViewCell *cell = (AutoLayoutTableViewCell *)[IBtblAutoLayoutExample cellForRowAtIndexPath:indexPath];
cell.IBlblLineNo.text=[NSString stringWithFormat:#"Line:%i",indexPath.row];
cell.IBlblLineText.text=[arrTemp objectAtIndex:indexPath.row];
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
CGSize expectedlineLabelSize = [cell.lineLabel.text sizeWithFont:cell.lineLabel.font constrainedToSize:CGSizeMake(280, 1000) lineBreakMode:NSLineBreakByWordWrapping];
cell.IBlblLineText.numberOfLines=expectedlineLabelSize.height/17;
CGRect frmlbl=cell.IBlblLineText.frame;
frmlbl.size.height=expectedlineLabelSize.height;
cell.IBlblLineText.frame=frmlbl;
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
height += 1.0f;
return height;
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
AutoLayoutTableViewCell *cell = (AutoLayoutTableViewCell *)[IBtblAutoLayoutExample cellForRowAtIndexPath:indexPath];
CGSize expectedlineLabelSize = [cell.IBlblLineText.text sizeWithFont:cell.IBlblLineText.font constrainedToSize:CGSizeMake(280, 1000) lineBreakMode:NSLineBreakByTruncatingTail];
return expectedlineLabelSize.height;
}
I have 2 questions :
My issue is I get the error EXE_BAD_EXCESS near the line
AutoLayoutTableViewCell *cell = (AutoLayoutTableViewCell *)[IBtblAutoLayoutExample cellForRowAtIndexPath:indexPath];
in heightForRowAtIndexPath and estimatedHeightForRowAtIndexPath.
Why do I have to write label text in both cellForRowAtIndexPath and heightForRowAtIndexPath?
Also, am I missing anything needed to achieve dynamic height for the cell?
To set automatic dimension for row height & estimated row height, ensure following steps to make, auto dimension effective for cell/row height layout.
Assign and implement tableview dataSource and delegate
Assign UITableViewAutomaticDimension to rowHeight & estimatedRowHeight
Implement delegate/dataSource methods (i.e. heightForRowAt and return a value UITableViewAutomaticDimension to it)
-
Objective C:
// in ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property IBOutlet UITableView * table;
#end
// in ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.table.dataSource = self;
self.table.delegate = self;
self.table.rowHeight = UITableViewAutomaticDimension;
self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
Swift:
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Don't forget to set dataSource and delegate for table
table.dataSource = self
table.delegate = self
// Set automatic dimensions for row height
// Swift 4.2 onwards
table.rowHeight = UITableView.automaticDimension
table.estimatedRowHeight = UITableView.automaticDimension
// Swift 4.1 and below
table.rowHeight = UITableViewAutomaticDimension
table.estimatedRowHeight = UITableViewAutomaticDimension
}
// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// Swift 4.2 onwards
return UITableView.automaticDimension
// Swift 4.1 and below
return UITableViewAutomaticDimension
}
For label instance in UITableviewCell
Set number of lines = 0 (& line break mode = truncate tail)
Set all constraints (top, bottom, right left) with respect to its superview/ cell container.
Optional: Set minimum height for label, if you want minimum vertical area covered by label, even if there is no data.
Note: If you've more than one labels (UIElements) with dynamic length, which should be adjusted according to its content size: Adjust 'Content Hugging and Compression Resistance Priority` for labels which you want to expand/compress with higher priority.

Resources