Make UILabel Fit Height in a Dynamic Sized UITableViewCell - ios

I have the following setup in my app:
I have a UITableView which has animated height change, when a cell is selected. In the cell is I have a label, which text I want to be on one line with truncation, when the cell is not selected and then when it is selected (the cell height get's bigger) I want the text (UILabel) to be on multiple lines, based on the text.
This is my code at the moment:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(self.selectedIndex != nil && [self.selectedIndex isEqual:indexPath]) {
return kCellHeight * 3.9;
}
return kCellHeight;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
ProductCell *cell = (ProductCell *) [tableView cellForRowAtIndexPath:indexPath];
if (![indexPath isEqual:self.selectedIndex]) {
cell.addButton.hidden = NO;
cell.descLabel.numberOfLines = 0;
cell.descLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.descLabel sizeToFit];
tableView.scrollEnabled = NO;
self.selectedIndex = indexPath;
}else {
self.selectedIndex = nil;
cell.descLabel.lineBreakMode = NSLineBreakByTruncatingTail;
tableView.scrollEnabled = YES;
cell.addButton.hidden = YES;
}
[tableView beginUpdates];
[tableView endUpdates];
}
The first part (when the cell is selected, expanded and the height gets bigger) the text fits as I want, but the second part (when the cell is contracted, and the cell height get back to the normal height) the text does not fit to the new height and is truncated as I want it to, it just stays multi lined. How can I fix this? So when the cell is selected the text gets multilined and when the cell is deselected the text gets back to its normal truncated state?

EDIT:
Override
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
and add below line in this function
cell.descLabel.numberOfLines = 1;

Related

Hiding a UILabel from a UITableViewCell doesn't resize the contentView

I'm trying to create a dynamic UITableView where a cell can expand/collapse as the user selects the cell.
- (void)setUpCell:(DynamicTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
cell.label.text = [self.dataSource objectAtIndex:indexPath.row];
cell.secondLabel.text = [self.dataSource objectAtIndex:self.dataSource.count - indexPath.row - 1];
if ([self.isVisible[indexPath.row] isEqual:#NO]) {
cell.secondLabel.hidden = YES;
} else {
cell.secondLabel.hidden = NO;
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DynamicTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
[self setUpCell:cell atIndexPath:indexPath];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DynamicTableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([self.isVisible[indexPath.row] isEqual: #YES]) {
self.isVisible[indexPath.row] = #NO;
cell.secondLabel.hidden = YES;
} else {
self.isVisible[indexPath.row] = #YES;
cell.secondLabel.hidden = NO;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static DynamicTableViewCell *cell = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
});
[self setUpCell:cell atIndexPath:indexPath];
return [self calculateHeightForConfiguredSizingCell:cell];
}
- (CGFloat)calculateHeightForConfiguredSizingCell:(DynamicTableViewCell *)sizingCell {
[sizingCell layoutIfNeeded];
CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height;
}
I forked this project and have the test code here.
Once the cell has been sized it does not change when selecting the cell, only hides/shows the content of the cell. I've tried replacing the explicit size calculation with UITableViewAutomaticDimension. Also tried reloading the cell. Seems once the cell size has been calculated, it does not change.
Any suggestions as to what to try would be greatly appreciated!
In iOS development a view never collapses if you set the hidden property to true.
Instead you should use autolayout. Assuming your view has two labels vertically stacked on top of each other, pin the first label to the cells contentView's top, give it a height constraint, pin the second label's top to the first labels bottom, pin the second labels bottom to the cell's contentView bottom. Set the height of the second label, and save this constraint in a variable, lets called it secondLabelHeightConstraint, now you can collapse and expand the cell by setting the value of secondLabelHeightConstraint to 0 or what ever value you would like.

UITableviewCell height not getting reset on scroll

I have a tableview which can be expanded on selecting the cell and collapses on selecting again. When you select, the cell should expand and display a label and when you select again it collapses and hides the label . The expanding and collapsing works fine, but if i scroll the tableview after expanding a cell it behaves weird. Once it goes out of the view and comes back , the cell will have the expanded cell's height but the label which is supposed to be shown in expanded cell is hidden.If i select the cell again it collapses and displays the label. I use ,
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:t cellForRowAtIndexPath:indexPath];
if([self cellIsSelected:indexPath])
return cell.frame.size.height+35;
return cell.frame.size.height;
}
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
NSNumber *selectedIndex = [self.selectedIndexes objectForKey:indexPath];
return selectedIndex == nil ? FALSE : [selectedIndex boolValue];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect cell
NSLog(#"Select cell:%#",indexPath);
[self.tableView deselectRowAtIndexPath:indexPath animated:TRUE];
if([self pickTaskForIndexPath:indexPath].productSpecialMessage){
BOOL isSelected = ![self cellIsSelected:indexPath];
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
if(cell.messageLabel.hidden==true){
cell.messageLabel.hidden = false;
} else {
cell.messageLabel.hidden = true;
}
NSLog(#"message:%#",cell.messageLabel.text);
[cell layoutIfNeeded];
}
self.tableView.rowHeight=UITableViewAutomaticDimension;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
indexPath is added to the selectedIndexes on didSelectRowAtIndexPath
Please help me
Cells should be configured only within cellForRowAtIndexPath. When a state change occurs that makes a cell need to look different, just reload that cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PickTaskTableviewCell *cell = (PickTaskTableviewCell *)[tableView dequeueReusableCellWithIdentifier:#"cell"];
// everything else you do to configure the cell goes here, then ...
// check the logic here, we want one condition that tells us whether to show the labels
if([[self cellIsSelected:indexPath] && self pickTaskForIndexPath:indexPath].productSpecialMessage){
// don't need these here
//NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
// [self.selectedIndexes setObject:selectedIndex forKey:indexPath];
// PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
cell.messageLabel.hidden=NO;
} else {
cell.message.hidden=YES;
cell.messageLabel.hidden=YES;
}
NSLog(#"message:%#",cell.messageLabel.text);
// don't need this here
// [cell layoutIfNeeded];
return cell;
}
Selection (and presumably deselection) cause the need to update the cell, so...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// don't deselect it here, just reload it
// more on this later...
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
// probably do the same in didDeselectRowAtIndexPath:
One last (optional) point. There's no need to maintain your own list of selected index paths, UITableView does that for you, so you could delete your selectedIndexes property and just use the table view methods, e.g....
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
return [[self.tableView indexPathsForSelectedRows] containsObject:indexPath];
}

UITableViewController - cells size separator and backgroundcolor

I'm trying to achieve airbnb look for my UITableViewController.
separators are not in the full width of the screen
each cell has a unique size and background color
I've managed to tackle #2 using static table and setting each cell size in IB but have the following problems:
setting a background color in IB didn't take (changed both background color as well as tint)
I wish to "delete" a cell programmatically when it has no content but the only function that returns cell height - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
is generic for all cells and is called prior to populating the cells with data, so at this method I can't decide if the cell should be deleted or not.
I think you should use the delegate method willDisplayCell: forRowAtIndexPath: of the table view. This delegate method is called just before the cell is ready to be displayed on the table view. In this delegate you will have the cell created, if you need some modifications you can do here, which will be reflected just before the cell is displayed.
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[cell setBackgroundColor:[UIColor redColor]];
// and all other cell customizations
}
You could set separatorInset and layoutMargins properties of table view before layout subviews and cell's separatorInset and layoutMargins properties before displaying cell.
For setting custom cell's height you may get needed cell by calling a table view data source method instead of call cellForRowAtIndexPath: method of table view.
Example:
// 1. Make full width separators:
- (void)viewWillLayoutSubviews {
if ([self.tableView respondsToSelector:#selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([self.tableView respondsToSelector:#selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsZero];
}
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if ([cell respondsToSelector:#selector(setSeparatorInset:)]) {
[cell setSeparatorInset:UIEdgeInsetsZero];
}
if ([cell respondsToSelector:#selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
}
#pragma mark - Table view data source
// 2.Customize cells background color and height
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *cellIdentifier = [NSString stringWithFormat:#"s%ld-r%ld", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
if (indexPath.row % 2) {
cell.backgroundColor = [UIColor redColor];
cell.contentView.backgroundColor = [UIColor whiteColor];
cell.textLabel.text = cellIdentifier;
} else {
cell.backgroundColor = [UIColor cyanColor];
cell.contentView.backgroundColor = [UIColor whiteColor];//background color of contentView overlaps cell's background color
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath]; //getting cell at indexPath
if ([self isCellEmpty:cell]) {
return 22;
}
return 44;
}
- (BOOL)isCellEmpty:(UITableViewCell *)cell {
return !cell.textLabel.text.length; //Place your code for checking cell's content
}

Expand Tableviewcell revealing labels at the bottom

When you select a tableviewcell it expands from 44 pixels to 100 pixels and when you deselect it will contract to 44 pixels again.
In the storyboard i've customized my cell to be 100pixels as standard and added 3 labels. one at the top and two at the bottom (the 2 labels at the bottom should not be visible and hide at the bottom).
When i run my app every cell will get the height of 44 pixels. The bottom two labels are visible at the correct x-coordinates but above every other cells.
like this:
in viewDidLoad:
self.expandedIndexPath = [NSIndexPath indexPathForRow:7 inSection:1];
And the methods:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Compares the index path for the current cell to the index path stored in the expanded
// index path variable. If the two match, return a height of 100 points, otherwise return
// a height of 44 points.
if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
return 100.0; // Expanded height
}
return 44.0; // Normal height
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UILabel *DateName = (UILabel *)[cell viewWithTag:100100];
DateName.text = _objects[indexPath.row];
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView beginUpdates];
if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
self.expandedIndexPath = nil;
} else {
self.expandedIndexPath = indexPath;
}
[tableView endUpdates];
}
How can i hide the labels until the cells are expanded to 100 pixels?
Set the cell to clipToBounds = YES;

uitableview cell does't update cell height after I change a subview's height constraint

i have a custom cell with a button and a blue blank view in it, when the button is tapped, the cell should change the blue view's cell height constraint. But now when the blue view's height is changed, the cell height doesn't change, i want the cell height to change the same amount as the blue view, but now it doesn't.
Here is the constraint.
the result is
what is want is update the cell height to accommodate the blue view.
here is the code.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TestZanTableViewCell";
TestZanTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[self configureCell:cell forIndexPath:indexPath];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static TestZanTableViewCell *cell;
if (cell == nil) {
cell = [tableView dequeueReusableCellWithIdentifier:#"TestZanTableViewCell"];
}
[self configureCell:cell forIndexPath:indexPath];
NSLog(#"%f", [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1.0f);
return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1.0f;
}
- (void)configureCell:(TestZanTableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
if (cell.tapped) {
cell.heightOfLikeView.constant = 50;
}
cell.testLabel.text = #"Helloworld";
[cell.zanButton addTarget:self action:#selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)didTapButton:(id)sender {
// Cast Sender to UIButton
UIButton *button = (UIButton *)sender;
// Find Point in Superview
CGPoint pointInSuperview = [button.superview convertPoint:button.center toView:self.tableView];
// Infer Index Path
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:pointInSuperview];
TestZanTableViewCell *cell = (TestZanTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.tapped = YES;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[cell.contentView setNeedsUpdateConstraints];
[cell.contentView layoutIfNeeded];
}
You need to refresh the table view and explicitly tell the tableview that, that particular cell has a different height.
You can refresh the table view as follows:
[tableview reloadData]
Or if you want to only refresh the one cell/ specific cells
[tableview reloadRowsAtIndexPaths:#[indexPathOfYourCell] withRowAnimation:UITableViewRowAnimationNone];
You will also need to add the height function to your TableView's delegate so that the table view can load with custom heights for each cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (hasTheTallBlueView) {
return largerValue;
}
return regularHeight;
}

Resources