I am trying to adjust the cell height for one of the cells on my table view. I am adjusting the size from the "row height" setting inside the "size inspector" of the cell in question. When I run the app on my iPhone the cell has the default size set from the "row size" in the table view.
If I change the "row size" of the table view then the size of all cells changes. I do not want to do that as I want a custom size only for one cell. I have seen a lot of posts that have a programmatic solution to the problem, but I would prefer to do it through storyboard, if that is possible.
On dynamic cells, rowHeight
set on the UITableView always overrides the individual cells' rowHeight.
But on static cells, rowHeight
set on individual cells can override UITableView's.
Not sure if it's a bug, Apple might be intentionally doing this?
If you use UITableViewController, implement this method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
In the function of a row you can choose Height. For example,
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
return 100;
}
else {
return 60;
}
}
In this exemple, the first row height is 100 pixels, and the others are 60 pixels.
I hope this one can help you.
For dynamic cells, rowHeight
set on the UITableView
always overrides the individual cells' rowHeight
.
This behavior is, IMO, a bug. Anytime you have to manage your UI in two places it is prone to error. For example, if you change your cell size in the storyboard, you have to remember to change them in the heightForRowAtIndexPath:
as well. Until Apple fixes the bug, the current best workaround is to override heightForRowAtIndexPath:
, but use the actual prototype cells from the storyboard to determine the height rather than using magic numbers. Here's an example:
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
/* In this example, there is a different cell for
the top, middle and bottom rows of the tableView.
Each type of cell has a different height.
self.model contains the data for the tableview
*/
static NSString *CellIdentifier;
if (indexPath.row == 0)
CellIdentifier = @"CellTop";
else if (indexPath.row + 1 == [self.model count] )
CellIdentifier = @"CellBottom";
else
CellIdentifier = @"CellMiddle";
UITableViewCell *cell =
[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
return cell.bounds.size.height;
}
This will ensure any changes to your prototype cell heights will automatically be picked up at runtime and you only need to manage your UI in one place: the storyboard.
I've built the code the various answers/comments hint at so that this works for storyboards that use prototype cells.
This code:
Does not require the cell height to be set anywhere other than the obvious place in the storyboard
Caches the height for performance reasons
Uses a common function to get the cell identifier for an index path to avoid duplicated logic
Thanks to Answerbot, Brennan and lensovet.
- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = nil;
switch (indexPath.section)
{
case 0:
cellIdentifier = @"ArtworkCell";
break;
<... and so on ...>
}
return cellIdentifier;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
static NSMutableDictionary *heightCache;
if (!heightCache)
heightCache = [[NSMutableDictionary alloc] init];
NSNumber *cachedHeight = heightCache[cellIdentifier];
if (cachedHeight)
return cachedHeight.floatValue;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
CGFloat height = cell.bounds.size.height;
heightCache[cellIdentifier] = @(height);
return height;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
<... configure cell as usual...>
There are actually two places where you need to change to row height, first the cell (you already did change that) and now select the Table View and check the Size Inspector
I think it is a bug.
Try adjust height not by Utility inspector but by mouse drag on the storyboard directly.
I solved this problem with this method.
If you're using swift , use like this. Don't Use storyboard to select row height. Programmatically set table row height like this,
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 || indexPath.row == 1{
let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
self.tableView.rowHeight = 150
cell.label1.text = "hiiiiii"
cell.label2.text = "Huiiilllllll"
return cell
} else {
let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
self.tableView.rowHeight = 60
cell.label3.text = "llll"
return cell
}
}
cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
You can get height of UITableviewCell (in UITableviewController - static cells) from storyboard with help of following lines.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];
return height;
}
Open the storyboard in the XML view and try to edit the rowHeight
attribute of the wanted element.
It worked for me when I tried to set custom rowHeight for my prototyped row. It's not working via inspector, but via XML it works.
You can use prototype cells
with a custom height
, and then invoke cellForRowAtIndexPath:
and return its frame.height
.:.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView
cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}
If you want to set a static row height, you can do something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 120;
}
I have recently been wrestling with this. My issue was the solutions posted above using the heightForRowAtIndexPath:
method would work for iOS 7.1 in the Simulator but then have completely screwed up results by simply switching to iOS 8.1.
I began reading more about self-sizing cells (introduced in iOS 8, read here). It was apparent that the use of UITableViewAutomaticDimension
would help in iOS 8. I tried using that technique and deleted the use of heightForRowAtIndexPath:
and voila, it was working perfect in iOS 8 now. But then iOS 7 wasn't. What was I to do? I needed heightForRowAtIndexPath:
for iOS 7 and not for iOS 8.
Here is my solution (trimmed up for brevity's sake) which borrow's from the answer @JosephH posted above:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = 50.;
self.tableView.rowHeight = UITableViewAutomaticDimension;
// ...
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
return UITableViewAutomaticDimension;
} else {
NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath:indexPath];
static NSMutableDictionary *heightCache;
if (!heightCache)
heightCache = [[NSMutableDictionary alloc] init];
NSNumber *cachedHeight = heightCache[cellIdentifier];
if (cachedHeight)
return cachedHeight.floatValue;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
CGFloat height = cell.bounds.size.height;
heightCache[cellIdentifier] = @(height);
return height;
}
}
- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
NSString * reuseIdentifier;
switch (indexPath.row) {
case 0:
reuseIdentifier = EventTitleCellIdentifier;
break;
case 2:
reuseIdentifier = EventDateTimeCellIdentifier;
break;
case 4:
reuseIdentifier = EventContactsCellIdentifier;
break;
case 6:
reuseIdentifier = EventLocationCellIdentifier;
break;
case 8:
reuseIdentifier = NotesCellIdentifier;
break;
default:
reuseIdentifier = SeparatorCellIdentifier;
break;
}
return reuseIdentifier;
}
SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0") is actually from a set of macro definitions I am using which I found somewhere (very helpful). They are defined as:
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
The same problem occurred when working on XCode 9 using Swift 4.
Add AutoLayout for the UI elements inside the Cell and custom cell row height will work accordingly as specified.
For dynamic cells, rowHeight set on the UITableView always overrides the individual cells' rowHeight. Just calculate the dynamic height if the content inside the row.
The only real solution I could find is this
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
return cell.frame.size.height;
}
This works and allows not to have an horrible switch/if duplicating the logic already in the StoryBoard. Not sure about performance but I guess when arriving in cellForRow:
the cell being already initalized it's as fast. Of course there are probably collateral damages here, but it looks like it works fine for me here.
I also posted this here: https://devforums.apple.com/message/772464
EDIT: Ortwin Gentz reminded me that heightForRowAtIndexPath:
will be called for all cells of the TableView, not only the visible ones. Sounds logical since iOS needs to know the total height to be able to show the right scrollbars. It means it's probably fine on small TableViews (like 20 Cells) but forget about it on a 1000 Cell TableView.
Also, the previous trick with XML: Same as first comment for me. The correct value was already there.
Added as a comment, but posting as an answer for visibility:
There seems to also be an issue if you initially setup a table view as "static cells" and then change it to "dynamic prototypes." I had an issue where even the delegate method for heightForRowAtIndexPath
was being ignored. I first resolved it by directly editing the storyboard XML, then finally just rebuilt the storyboard scene from scratch, using dynamic prototypes from the start.
Given that I did not find any solution to this problem through Interface Builder, I decided to post a programmatic solution to the problem in Swift using two dynamic cells, even though the initial question asked for a solution through Interface Builder. Regardless I think it could be helpful for the Stack Overflow community:
import UIKit
enum SignInUpMenuTableViewControllerCellIdentifier: String {
case BigButtonCell = "BigButtonCell"
case LabelCell = "LabelCell"
}
class SignInUpMenuTableViewController: UITableViewController {
let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]
private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
if indexPath.row == 2 {
return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
} else {
return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
}
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
}
...
}
One other thing you can do is to go to your Document Outline, select the table view that your prototype cell is nested. Then on the Size Inspector, change your table view Row Height to your desired value and uncheck the Automatic box.
Success story sharing
UITableView
content fromDynamic Prototypes
toStatic Cells
, I did that, and my whole project blew up.. almost got killed myself.