iOS: Dynamic Height For UITableViewCell - objective-c

I'm using iOS9 XCode7
I need to change the height of cell Dynamically according to labelText Height
I have used:
self.tableView.rowHeight=UITableViewAutomaticDimension;
But it is not working for custom made cell.
sizetoFit is removed in iOS9.
Please suggest something.
Thanks

Give your label constrains relative to the cell, top, bottom, left and right.
Than your cell size will grow with the content height.
also make your label multiline.(by setting Lines property to 0 in attribute inspector)
#pragma mark- Menu table Delegates
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}

I'm not quite sure what this sentence means (a screenshot would've helped):
"I need to change the height of cell Dynamically according to labelText Height"
So, you have a UITableViewCell, containing a UILabel, and want each cell in your table to have a height depending on that cell's label ?
Here's the code I use.
In my UITableViewCell class, I'll define a static function to measure, and return the height of my .xib file:
#implementation MikesTableViewCell
...
+(CGSize)preferredSize
{
static NSValue *sizeBox = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Assumption: The XIB file name matches this UIView subclass name.
NSString* nibName = NSStringFromClass(self);
UINib *nib = [UINib nibWithNibName:nibName bundle:nil];
// Assumption: The XIB file only contains a single root UIView.
UIView *rootView = [[nib instantiateWithOwner:nil options:nil] lastObject];
sizeBox = [NSValue valueWithCGSize:rootView.frame.size];
});
return [sizeBox CGSizeValue];
}
#end
Then, in my UIViewController class, I'll tell my UITableView to use this cell, find out it's height.
#property (nonatomic) float rowHeight;
UINib *cellNib = [UINib nibWithNibName:#"MikesTableViewCell" bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:#"CustomerCell"];
rowHeight = [MikesTableViewCell preferredSize].height;
...
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return (float)rowHeight;
}
Again, this might not be exactly what you're looking for, but I hope this helps.

try this.
it is so simple.
set this height in heightForRowAtIndexPath method
float vendorNameHeight = [vendorNameLbl.text heigthWithWidth:width andFont:[UIFont fontWithName:#"Signika-Light" size:21.0]];
then
UILabel*vendorNameLbl=[[UILabel alloc]initWithFrame:CGRectMake( 13, 11, 100, 22)];
vendorNameLbl.numberOfLines = 0;
[vendorNameLbl sizeToFitVertically];

Related

Couldn't set values in Custom Cell in UITableView from NSMutableArray

Help me to get rid of with this dilemma that occurred yet when I tried to dequeued the cell (Custom Cell).Below are some steps and Indents that I did with my Project.
The very first is I drag and drop a UITableView in my ViewController and add the ViewController.h doing after this
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
Then I made a Custom Cell with 3 UILabels and change the height of the Cell to 65.
After that I made a property in ViewController.m
#property (nonatomic,strong) NSMutableArray *myTodoTitles;
Then in method(ViewDidLoad) I did.
myTodoTitles = [NSMutableArray arrayWithCapacity:10];
[myTodoTitles addObject:#"Go for ride"];
[myTodoTitles addObject:#"Do University Assignments"];
[myTodoTitles addObject:#"Watch Show"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.myTodoTitles count]-1 inSection:1];
[self tableView:self.myTodoTable cellForRowAtIndexPath:indexPath];
After that I just did these things in my ViewController.m
#pragma mark - Table view data source
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *myIdentifier = #"TodoCell";
TodoCell *todoCell = (TodoCell*)[tableView dequeueReusableCellWithIdentifier:myIdentifier];
todoCell.todoTitleLabel.text = [self.myTodoTitles objectAtIndex:indexPath.row];
return todoCell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [myTodoTitles count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
But when I run the project it dequeued nothing.
Please help
Most likely that you have not connected your viewController to be the dataSource of your tableView. This could be done from Interface Builder or from the code. You can easily check it by adding self.myTodoTable.dataSource = self; at the very first of viewDidLoad method.
And also: what did you mean by `
[self tableView:self.myTodoTable cellForRowAtIndexPath:indexPath];`
in viewDidLoad ? Seems like you wanted to do
[self.myTodoTable reloadData];
There are to UITableView methods with similar name:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
and
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath
The first one will try to dequeue a reusable cell. If it returns nil you are responsible to create appropriate cell.
The latter one will always return a valid cell: you will have to register a certain class or NIB with that tableview beforehand though. Docs.
EDIT:
As ReDetection pointed out: first method will also return a valid cell as long as you had registered a proper class (or nib) with that tableview.
In your case that means that you should register TodoCell in viewDidLoad with your tableView.
If TodoCell is made without .xib:
[self.tableView registerClass:[ToDoCell class]
forCellReuseIdentifier:#"TodoCell"];
Or if it is made with .xib.
[self.tableView registerNib:[UINib nibWithNibName:#"TodoCell"
bundle:nil]
forCellReuseIdentifier:#"TodoCell"];
EDIT2:
Your code also seems to be missing the dataSource setting. Something like:
self.tableView.dataSource = self;
This will trigger initial reload.
You'd probably want to set a delegate (since your controller claims to adopt that protocol) in the same manner.

NSScrollView displaying problems in Xcode 5 after scrolling

NSScrollView displaying NSTableView based on custom view (Image, checkbox and text label). When I scroll - I have lag (bug?) with redrawing rows.
Normal:
Bugged after scroll:
Project (and .xib file) was updated from Xcode 4 to Xcode 5 format. I think this bug appeared after it, but I'm not sure.
Any suggestions how to fix it?
Realisation of NSTableViewDataSource, NSTableViewDelegate protocols:
- (NSInteger) numberOfRowsInTableView: (NSTableView *) aTableView {
return [arrayOfObjects count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
CMListItem *currentObject = [arrayOfObjects objectAtIndex: row];
NSString *currentName = [currentObject name];
BOOL currentState = [currentObject state];
NSImage *currentImage = [currentObject getArtwork];
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"MainCell"]) {
CMTableCellView *cellView = [tableView makeViewWithIdentifier: identifier owner: self];
cellView.textField.stringValue = currentName;
cellView.button.state = currentState;
cellView.imageView.image = currentImage;
return cellView;
}
return nil;
}
NSScrollView not modified.
My own table cell view - subclass of NSTableCellView with few additional outlets.
UPDATE: this thing helped me: set Can Draw Concurrently for all NSTableView at On state, then turn it to Off state.
I think I have seen this problem sometimes when "Copy On Scroll" is enabled. Try disabling this option in IB (in the attribute inspector, scrollview section, behavior).

UITableViewCell UIButton dynamic height

I have CustomCellView which is custom view for cell in tableview. I have UIButton in xib and set it's default frame in xib.
In MainViewController (viewDidLoad)
// Get nib for custom cell
UINib *nib = [UINib nibWithNibName:#"CustomFeedCell" bundle:nil];
[self.mainTableView registerNib:nib forCellReuseIdentifier:#"CustomFeedCell"];
In cellForRow...
CustomFeedCell *cell = [self.mainTableView dequeueReusableCellWithIdentifier:#"CustomFeedCell"];
// here i need to set UIButton frame (height) depending on image height.
// I have tried diferent ways and it always return height set in CustomCellView xib
If you want to create buttons, images etc in UIView dynamically, you have to use viewDidLoad: method and do not use xibs.
Ok so what I am understanding from your question is you are having problem setting the dynamic height of a button present in your tableview cell. It sometimes might be more then the height of the cell and sometimes quiet less then height of the cell. And you want to adjust it accordingly. Correct ?
If thats your problem then you can set height of your each tableview cell in its delegate
tableView:heightForRowAtIndexPath:
as
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [indexPath row] * 20; // your desired height
}
Hope this solves your problem.

UITableView is lagging a cell containing a UITextView is displayed the first time

I'm running into issues with a UITableView that lags while scrolling when specific cells will move to superview.
I've written my own IPFormKit for an easy way to create beautiful input forms with different kind of inputViews without having to re-code everything manually for each form field / cell.
I've got a UITableViewController that initializes my IPFormKit and its fields.
The - (UITableViewCell *) cellForRowAtIndexPath:(NSIndexPath)indexPath; loads the dequeued custom cells (called IPFormTableViewCell) and assigns the IPFormField to each cell.
The custom UITableViewCell (IPFormTableViewCell) creates all (possibly) required inputViews (UITextField, UITextView, CustomUILabel) with a CGRectZero on initialization.
The matching inputView depending on the IPFormField's type (that was already inited as an iVar of the cell) is resized and added as a subview to the cell.contentView within.
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath)indexPath
For UITextField and CustomUILabel this works flawlessly, but when the inputView is a UITextView, the scrolling of the UITableView lags (slightly) noticable when this cell will be displayed for the first time.
When the cell will be displayed again later after scrolling a bit (even if the cell was reused and thus the UITextView removed and readded), there is no lag and scrolling is super smooth for those cells.
I'm running out of ideas what the reason for this lag could be.
Any idea is appreciated.
PS: The lag is noticable on both, iPhone 4 and iPhone 4S and is of almost exactly the same duration (so it should not be CPU related)
UITableViewController.m:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"IPFormFieldCell";
// Get Form Field for indexPath
IPFormField *formField = [self.form fieldAtIndexPath:indexPath];
IPTableViewCell *cell = (IPTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[IPTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.backgroundView = nil;
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
cell.selectedBackgroundView = nil;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
[cell assignFormField:formField];
return cell;
}
IPFormTableViewCell.m:
- (void) assignFormField:(IPFormField *)field:
- (void) assignFormField:(IPFormField *)field {
if (formField != nil) {
formField.inputView = nil; // unlink old field
}
self.formField = field;
// Change Field Label
[fieldLabel setText:[field label]];
// Add an Input View to the Field
UIView *labelView = nil;
UIView *inputView = nil;
switch (formField.type) {
case IPFormFieldTypeTextField:
{
labelView = fieldLabel;
UITextField *textField = inputTextField;
textField.delegate = (IPFormTextField *)formField;
textField.inputAccessoryView = [formField.form inputAccessoryView];
textField.placeholder = [self.formField stringFromValue:self.formField.defaultValue];
textField.keyboardType = [(IPFormTextField *)formField keyboardType];
if (self.formField.value == nil || [[self.formField stringFromValue:self.formField.value] isEqualToString:[self.formField stringFromValue:self.formField.defaultValue]]) {
textField.clearsOnBeginEditing = YES;
} else {
textField.text = [self.formField stringFromValue:self.formField.value];
textField.clearsOnBeginEditing = NO;
}
inputView = textField;
break;
}
case IPFormFieldTypeTextArea:
{
UITextView *textView = inputTextView;
textView.delegate = (IPFormTextArea *)formField;
textView.inputAccessoryView = [formField.form inputAccessoryView];
if (self.formField.value == nil || ![[self.formField stringFromValue:self.formField.value] length] > 0) {
textView.text = [self.formField stringFromValue:self.formField.defaultValue];
} else {
textView.text = [self.formField stringFromValue:self.formField.value];
}
inputView = textView;
break;
}
default:
break;
}
self.leftItem = labelView;
self.rightItem = inputView;
if (leftItem != nil) {
[self.contentView addSubview:leftItem];
}
if (rightItem != nil) {
[self.contentView addSubview:rightItem];
}
formField.inputView = rightItem;
}
Apparently, cellForRowAtIndexPath: of my dataSource made use of a field's property, that was set as #property (nonatomic, copy) instead of #property (nonatomic, readonly).
Now that I've fixed it, the scrolling isn't lagging anymore.
As I guessed, your problem here is with your custom controls. Yes, you are reusing the cell, but this doesn't give anything in your case, as every time you request for the cell, you are creating new custom control for each cell. My advise, you can create and keep your custom controls as an instance variables, and when required return them without many if-elses, or, you could create custom cells for your two cases, and keep them dequeued with different cell identifiers and reuse them. Good Luck!

Setting static cells in uitableview programmatically

I am programmatically creating a tableview in objective c. How can I make the cells static programmatically?
Thanks
Making cells static programmatically doesn't really make sense. Static cells are basically only for Interface Builder and requires the entire TableView to be static. They allow you to drag UILables, UITextFields, UIImageViews, etc. right into cells and have it show up just how it looks in Xcode when the app is run.
However, your cells can be "static" programmatically by not using an outside data source and hardcoding everything, which is usually going to be kind of messy and generally a poor idea.
I suggest making a new UITableViewController with a .xib and customizing it from there if you want "static" cells. Otherwise, just hardcode all your values and it's basically the same thing, but is probably poor design if it can be avoided.
By using a distinct cell identifier for each one you will get it. You could use something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [NSString stringWithFormat:#"s%i-r%i", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
//you can customize your cell here because it will be used just for one row.
}
return cell;
}
You could also do it the old fashioned and just create the cell the way you want depending on the NSIndexPath, this works with Static Cell TVC's and regular table views (don't forget to return the proper number of sections and rows in their datasource methods):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch indexPath.row {
case 0:
// First cell, setup the way you want
case 1:
// Second cell, setup the way you want
}
// return the customized cell
return cell;
}
I you want to create cells structure for example for a settings screen or something like that and you maybe need just to modify some cells content but not their number or sections structure you can overload method of your UITableViewController subclass like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *aCell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
// Configure the cell...
if ([aCell.reuseIdentifier isEqualToString:#"someIdentifier"]){
//some configuration block
}
else if ([aCell.reuseIdentifier isEqualToString:#"someOtherIdentifier"]) {
//other configuration block
}
return aCell;
}
But you can make it in a better way with a little bit more code;
1) In the begining of your .m file add typedef:
typedef void(^IDPCellConfigurationBlock)(UITableViewCell *aCell);
2) add cellConfigurations property to your TablviewControllerSubclass extention:
#interface IPDSettingsTableViewController ()
#property (nonatomic, strong) NSDictionary *cellConfigurations;
#property (nonatomic) id dataModel;
#end
3) Modify your static cells of TableviewController subclass in storyboard or xib
and add unique cellReuseIdentifier for each cell you want to modify programmatically
4) In your viewDidLoad method setup cellsConfiguration blocks:
- (void)viewDidLoad
{
[super viewDidLoad];
[self SetupCellsConfigurationBlocks];
}
- (void)SetupCellsConfigurationBlocks
{
//Store configurations code for each cell reuse identifier
NSMutableDictionary *cellsConfigurationBlocks = [NSMutableDictionary new];
//store cells configurations for a different cells identifiers
cellsConfigurationBlocks[#"someCellIdentifier"] = ^(UITableViewCell *aCell){
aCell.backgroundColor = [UIColor orangeColor];
};
cellsConfigurationBlocks[#"otherCellIdentifier"] = ^(UITableViewCell *aCell){
aCell.imageView.image = [UIImage imageNamed:#"some image name"];
};
//use waek reference to self to avoid memory leaks
__weak typeof (self) weakSelf = self;
cellsConfigurationBlocks[#"nextCellIdentifier"] = ^(UITableViewCell *aCell){
//You can even use your data model to configure cell
aCell.textLabel.textColor = [[weakSelf.dataModel someProperty] isEqual:#YES] ? [UIColor purpleColor] : [UIColor yellowColor];
aCell.textLabel.text = [weakSelf.dataModel someOtherProperty];
};
weakSelf.cellConfigurations = [cellsConfigurationBlocks copy];
}
5) overload tableView:cellForRowAtIndexPath method like this:
#pragma mark - Table view data source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *aCell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
// configure cell
[self configureCell:aCell withConfigurationBlock:self.cellConfigurations[aCell.reuseIdentifier]];
return aCell;
}
- (void)configureCell:(UITableViewCell *)aCell withConfigurationBlock:(IDPCellConfigurationBlock)configureCellBlock
{
if (configureCellBlock){
configureCellBlock(aCell);
}
}
It is pretty common to want to build a simple table to use as a menu or form, but using the built in API with the datasource and delegate callbacks don't make it easy to write or maintain. You may need to dynamically add/remove/update some cells, so using Storyboards by itself won't work.
I put together MEDeclarativeTable to programmatically build small tables. It provides the datasource and delegate for UITableView. We end up with an API where we provide instances of sections and rows instead of implementing datasource and delegate methods.