UITableViewCell with a lot of subviews or with a single view - ios

I'm new in iOS programming that's why I'm looking for the most efficient solution to my problem.
What I want to achieve is to display in UITableViewCell with a name (some text) and under each name some filled little rectangles with a number inside, similar to badges.
My first idea is to create a UIView that will represent the badge and in a custom UITableViewCell I will add these rectangles as subviews.
The second idea is to create only one UIView that will draw all the little rectangles.
My question is, which is the better performing solution knowing that:
the number of cells will be max. 20 and the total number of rectangles no more than 50
The number of rectangles displayed in a cell is different
I want to reuse the cells, so I have to update/redraw the cell content for each row
I want to avoid the cell selection view problem that "hides" the subviews
Of course any other solution is appreciated.
Thanks in advance,
hxx

What i would suggest is to sub class the UITableViewCell and make the customization u need in it.The customized view can have a label and rectangles below it.
The rectangles can be small custom buttons with background images (if you have any or give it a background color) and title as your number.You would have to ,however calculate their width based on the width of your table to accomodate the maximum number of rectangles.
You can disable the selection of the table in the xib or you can do it programmatically like so cell.selectionStyle = UITableViewCellSelectionStyleNone; and do not implement didSelectRowAtIndexPath
I have followed the approach of subclassing the cell for my tables to customize their look and feel and it works good.I hope this helps.
A Good tutorial to begin with subclassing can be found here
http://howtomakeiphoneapps.com/how-to-design-a-custom-uitableviewcell-from-scratch/1292/

Why you are not creating cell in -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath Here you can defines your custom type cell which will also reuse and whenever you want you can add the different thing to cell like this.
static NSString *CellIdentifier = #"Cell";
UILabel *RequestSentTo;
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
cell.selectionStyle=UITableViewCellSelectionStyleNone;
RequestSentTo = [[UILabel alloc] initWithFrame:CGRectMake(11, 2, 286, 40)];
RequestSentTo.backgroundColor = [UIColor clearColor];
RequestSentTo.tag = 200;
RequestSentTo.numberOfLines = 3;
RequestSentTo.font=[UIFont boldSystemFontOfSize:15.0];
RequestSentTo.textColor = [UIColor blackColor];
RequestSentTo.lineBreakMode=UILineBreakModeWordWrap;
[cell.contentView addSubview:RequestSentTo];
} else {
RequestSentTo=(UILabel*)[cell.contentView viewWithTag:200];
}
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"Shift Request for "];
[string appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"%# by ",dateStr] attributes:nil]];
[string appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:#"Dr. %#",notificationsObj.doctorName] attributes:purpleTextAttributes]];//purpl
RequestSentTo.attributedText=string;
RequestSentTo.lineBreakMode=UILineBreakModeWordWrap;
RequestSentTo.numberOfLines = 3;
Whenever you want you can add the things you want with reusing cell. Hope this helps

2 methods come into my mind.
You can put the components as subview inside UITableViewCell(Through XIB or programatically subclassing UITableViewCell) and use it in UITableView.
You can subclass UITableViewCell, and override the -(void)drawRect method and draw all the components that you wish to be displayed on cell.

See if can help.
You can create a new class extends to UITableViewCell, which means to rewrite UITableViewCell as your own cell named as MyTestCell.
And in this Cell you call create your properties, like labels and views, and add those to your new cell.
like add this to MyTestCell.h
#property (nonatomic, retain) UILable *myLable1;
#property (nonatomic, retain) UIView *mySubview1;
MyTestCell.m
_myLable1 = .....
_mySubview = .....
[self addSubview: _myLbale1];
[self addSubview: _mySubview1];
And when use, u can work like this
static NSString *CellIdentifier = #"MyCell";
MyTableViewCell *cell = [tableview dequeReuseID:CellIdentifier];
if (cell == nil) {
cell = [MyTableViewCell alloc] init.........
}
//And you can sign your property here in your cell
cell.myLable1 = ....
cell.myView1 = .....
return cell;
}
If your strings add to the lable is different,make the lable.height is different. you can use code like this
CGSize labelSize = [str sizeWithFont:[UIFont boldSystemFontOfSize:17.0f]
constrainedToSize:CGSizeMake(280, 100)
lineBreakMode:UILineBreakModeCharacterWrap]; //check your lableSize
UILabel *patternLabel = [[UILabel alloc] initWithFrame:CGRectMake(35, 157, labelSize.width, labelSize.height)];
patternLabel.text = str;
patternLabel.backgroundColor = [UIColor clearColor];
patternLabel.font = [UIFont boldSystemFontOfSize:17.0f];
patternLabel.numberOfLines = 0;// must have
patternLabel.lineBreakMode = UILineBreakModeCharacterWrap;// must have
add this to your cell, and make it dynamically resize your lable as well as your cell! And also you have to dynamically set high for your tableView Row height.(Do know what is dynamically resize?)
See this:
rewrite the method setMyLable1 in MyTableViewCell.m
-(void)setMyLable1:(UILable*)aLable
{
//in here when never your sign your alabel to your cell (like this : cell.myLable1) this method will be call and u can get the size of your string and set this label's height
//get string size StringSzie
[_myLable1 setFrame:CGRectMake(10,10,stringSize.width,stringSize.height)];
//And resize your cell as well
[self setFrame:CGRectMake(0,0,_myLable1.frame.size.width+20,_myLable1.frame.size.height+20)];
//done!!!
}
OK you get a automactically reszie cell for yourself and you have to dynamically reset height for your row in tableView too!!!!!

What do you need is called custom cell
Here is good tutorial for it
customize table view cells for uitableview

Related

Creating a Label in each of my custom cell in cellForRowIndexPath

I am trying to create a label in each of my customCells. In cellForRowIndexPath, I have:
EDIT:
static NSString *checkInTableIdendifier = #"ChatCell";
cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:checkInTableIdendifier];
if (cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"ChatTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSAttributedString *title;
title = [[NSAttributedString alloc] initWithString:[self.savedMsgs objectAtIndex:indexPath.row] attributes:#{ NSFontAttributeName : [UIFont fontWithName:#"Noteworthy-Bold" size:12], NSUnderlineStyleAttributeName : #1 , NSStrokeColorAttributeName : [UIColor blackColor]}]; //1
UILabel *label;
label = [[UILabel alloc] initWithFrame:CGRectMake( (self.view.bounds.size.width - title.size.width) / 2.0f, 40.0f, title.size.width, title.size.height)]; //2
label.attributedText = title; //3
[cell addSubview:label]; //4
The label will all end up in the same point overlapping each other because of line 2. How can I represent the origin of the label generated according to each respective cell?
The method you are using to generate cells, 'dequeueReusableCellWithIdentifier' can return a cell that has already been used before. When you add your labels etc after getting the cell, you are adding labels on top of labels that are already there.
The easiest way to handle cell reuse is to create a UITableViewCell subclass and an associated xib. After this ViewController is created and the tableView is accessible (viewDidLoad, usually) register the nib for the cell to the tableView. Then when you dequeue the cell, you need only to set the text values on the labels that are already there.
An aside, UITableView had a newer better method for dequeuing cells that takes an index path and always returns a cell so you don't need to nil check it. I would switch to that if you don't need to support iOS 6.
You should use the cell's bounds, not self.view's to position the label. Be careful as the frame might change after the cell has been created, so it's recommended to use auto layout to ensure the label is always positioned when you want it to be in respect to the cell.
Also, remember that the cell get reused in which case you might end up adding the label multiple times.
To mitigate both issues, subclass the cell and design it in the accompanying xib.

create UITableViewCell programmatically

I am trying to create a UITableViewCell that contains a UIScrollView that is able to scroll horizontally for each cell in the UITableView.
Everything shows correctly and works well. However, when I scroll constantly up and down on the UITableView, memory usage goes up and up and up and up..... which I think means that I am constantly adding the custom elements over each over when the UITableViewCell is being reused. I would like to know how I can stop this from happening.
Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Configure the cell...
NSDictionary *cellDictionary = [xmlMArray objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// code
codeString = [[UILabel alloc] initWithFrame:CGRectMake(15.0, 0.5, 70.0, 40.0)];
codeString.text = [cellDictionary objectForKey:#"Code"];
codeString.backgroundColor = [UIColor clearColor];
// series
addressString = [[UILabel alloc] initWithFrame:CGRectMake(220.0, 10.5, addressString.frame.size.width, 50.0)];
addressString.text = [NSString stringWithFormat:#"PC %#: %#",[cellDictionary objectForKey:#"Number"] ,[cellDictionary objectForKey:#"Street"]];
[addressString sizeToFit]; // Dynamic UILabel width
addressString.backgroundColor = [UIColor clearColor];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIScrollView *scrollCell = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, cell.frame.size.width, cell.frame.size.height)];
[scrollCell setContentSize:(CGSizeMake((220.0 + addressString.frame.size.width)+15, cell.frame.size.height))];
[scrollCell addSubview:codeString];
[scrollCell addSubview:addressString];
[cell addSubview:scrollCell];
return cell;
}
There seems to be two problems here. I'll try to explain what's actually happening, but you should follow mbm29414's answer on what to do.
In the first part of this method, you are asking for a UITableViewCell using the identifier:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
The next part is to check if you received a cell with this call:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
This means that if your first line of code DID NOT return a cell-object, then you are instantiating a new one.
Later on in your method, you are instantiating yet another cell:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
UIScrollView *scrollCell (...)
This way you might not actually be reusing the cells, I'm not sure. At the very least, it should not be there. You potentionally allocate double the space each time.
Remove the last instantiation, and that should probably help a little.
The other problem, I think, is that you're adding scrollView and UILabels to your cells' subviews. Your circle of life:
Create cell
Create 2 UILabels
Create ScrollView
Add labels to ScrollView's subview
Add scrollView to Cell's subview
Send cell on it's merry way
When your cell is brought back from the dead during [tableView dequeueReusable..], they still contain the UIScrollView with the UILabel. Your code does not take advantage of that, but rather ignores it. This means that you are adding an ADDITIONAL scrollView with labels into your cell. If you scroll up and down a lot, this means that one single cell can possibly contain 50 different scrollViews, all of them taking the same amount of processing.
This is what happens next:
Get cell from the queue
(this cell already contains UIScollView and UILabels)
Add new scrollView with labels anyway
Send it on it's merry way (now with 2 scrollViews and 4 labels)
To solve this, you should do as mbm29414 suggested, to make your own subclass of UITableViewCell. That way, you can say cell.codeString.text=#"blah";
While you are re-creating a new UITableViewCell each time, you also appear to be endlessly adding UIScrollView and UILabel objects.
First, remove the second call of:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
Second, try subclassing UITableViewCell, making the UI like you like it either in an init method or in IB. Then, make a "setup" method that takes an object and configures each UI element accordingly. That way, you can not only recycle your cells, but also keep from continually creating more UIView subclass instances.
Couple of things.
Your check to see if the table view cell returned by
dequeueReusableCellWithIdentifier: is nil is not necessary, because
that method won't return nil anymore unless you've made a mistake with your identifier. It used to (I think before iOS
6).
Later in the code you're creating a new cell and assigning it to the cell variable which is eventually returned. Why? You already created one, so creating a second is fundamentally wrong.

Making custom, dynamic tableview cells

I am struggling to figure out how to get complete control over my tableview cells. I want them to look something like this:
Right now I need to know how to properly manage my cells. Should I make a table view cells subclass? Should I be doing this all within the storyboard of the tableview? That's what I'm doing now. Also, how do I implement dynamic cell heights based on the amount of lines of text?
Thanks
You should subclass the UITableViewCell class and create your own custom cell using XIB. This will give you a lot of leg room for dynamism.
Refer to this tutorial for how to do so:
http://www.appcoda.com/customize-table-view-cells-for-uitableview/
U can create a custom view and use the followingin the cellForRowAtIndex
static NSString * cellIdentifier=#"MyTableView";
UITableViewCell * cell;
if(cell== nil)
{
cell = [myTableView dequeueReusableCellWithIdentifier:cellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
contentCell.tag =100;
contentCell=[[ContentOfCell alloc]initWithFrame:CGRectMake(0, 0, 100, 50)];
[cell.contentView addSubview:contentCell];
}
else{
// Reusable part. Reuse the UI controls here from existing cell
contentCell = (ContentOfCell *)[cell.contentView viewWithTag:100];
}
//Assign all the data here
contentCell.nameField.text=[arr objectAtIndex:indexPath.section];
//same way for other fields
}
Where contentCell is a custom view
I will try to answer your question in three parts :
For Dynamic cell height which is based on text content : you have a table view delegate called heightForRowAtIndexPath, you should calculate the height of the text based on its font and font size characteristics, and of course by providing the available width, for this you can use method "sizeWithFont" of NSString.
For more control on the cell appearance : you should make a table view cell subclass and use it in the cellForRowAtIndexPath.
Should you be doing this using storyboard : It is not necessary to do it using storyboard.

Dynamically display an image in a UITableViewCell

I have a UITableViewCell, it is scroll-disabled and with fixed sections and rows.
There are two sections, with 1 row in section 0, and several rows in section 1.
The tableView is for users to make some choices.
So, the first section (with only one row) is going to display the result of users' choices,
and no doubt the second section (with several rows) is for choosing.
Now I want to put an image in the cell of the only row of the first section,
and this image will change according to users' choose.
It is very easy to judge which png image should be displaying, but I have trouble update it.
I tried use the cell's imageView, and manually alloc a UIImageView or UIView there to display those images.
But all of them won't work, I mean they just keep what they are displaying at the beginning and never changes it, even if I set the view's background or its image to a new png.
I tried some method like
[myImage setNeedsDisplay] for the manually alloced view,
or
[thatCell setNeedsDiaplsy] & [self.tableView reloadData] for the imageView of that cell,
but in vain.
So I wonder how can I achieve this function that dynamically display an image in a UITableViewCell in different situations?
Thanks a lot!
_____update line_____
I'm sorry that I didn't provide my code.
and here they are.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *inOutTableCellIdentifier = #"DealTableViewIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:inOutTableCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:inOutTableCellIdentifier] autorelease];
cell.textLabel.font = [UIFont fontWithName:cMyFont size:[UIFont systemFontSize]];
cell.detailTextLabel.font = [UIFont fontWithName:cMyFont size:[UIFont smallSystemFontSize]];
// I tried using both imageView and UIView here, but won't work
if (indexPath.section == 0) {
//cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"moneyCell.png"]];
cell.backgroundColor = [UIColor cyanColor];
// cell.imageView.image = [UIImage imageNamed:#"dealDone2.png"];
self.undoneIcon = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)] autorelease];
//self.undoneIcon.image = [UIImage imageNamed:#"dealUndone2.png"];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"dealUndone2.png"]];
[cell.contentView addSubview:self.undoneIcon];
.... // deal with other rows in section 1
return cell;
}
// and this is the method that update the image, the images are named "dealDoneX.png",
// where X is an integer from 0 to 4.
- (void)checkUndoneDegree { // here I also tried 2 ways corresponding to UIView or a cell's imageView, but neither works well.
int i = 0;
if (self._date)
i++;
if (self.moneyTextField.text)
i++;
if (self._incomingAccount)
i++;
if (self._expensingAccount)
i++;
if (_dealType != kTransferView)
if (self._type)
i++;
NSLog(#"undone degree: %d",i);
NSString *imageName = [#"dealUndone" stringByAppendingFormat:#"%d",i];
self.undoneIcon.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:imageName]];
[self.undoneIcon setNeedsDisplay];
// NSUInteger p[2] = {0,0};
// UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndexes:p length:2]];
// [cell setNeedsDisplay];
}
and everytime I update the table's data, like changing some text of some cell,
I would call [self checkUndoneDegree] and then call [self.tableView reloadData],
But the picture is never updated, at least from the screen.
I even tried to put the codes that decide which png to set in the
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
method, but it can only make the view displaying the first png, without any updating.
Thanks for helping!
Make your undoneDegree (represented by i variable in your code) an ivar of your view controller, so it is accessible in all of it's methods, also in the UITableView delegate and data source methods.
Forget about setNeedsDisplay method. Since you are using UITableView to display your content, you need to play by its rules. This means you should use reloadSections:withRowAnimation: method.
Check again if you need self.undoneIcon. I'm pretty sure that imageView property should do. That is if you really want to display an image in the cell. If you want to create some kind of progress bar by manipulating cell's background, then use backgroundView property, or just place UIProgressView in your cell. This is what I think your want to do, after looking at your code.

iOS xcode, adding UILabel subview to custom table cell, label text does not update on screen

I've created a custom UITableViewCell class, and used the layoutSubviews method to add a custom label. Like this:
- (void)layoutSubviews
{
[super layoutSubviews];
if (statusLabel == nil)
{
statusLabel = [[UILabel alloc]initWithFrame:CGRectMake(430.0, 10.0, 100.0, 20.0)];
[statusLabel setTextAlignment:UITextAlignmentRight];
[statusLabel setText:#"Status, set in code"];
statusLabel.tag = 1;
[self.contentView addSubview:statusLabel];
}
}
As you can see, I have set the initial text of the label to "Status, set in code".
In the table view controller I set the text for this custom label in the cellForRowAtIndexPath method, like so:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
int index = [indexPath row];
NSString *introducerString =[introducers objectAtIndex:index];
NSArray *parts = [introducerString componentsSeparatedByString:#","];
static NSString *MyIdentifier = #"Requester";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[DanceCardCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:MyIdentifier] autorelease];
}
UIImage *image = [UIImage imageNamed:#"1.jpg"];
[cell.imageView setImage:image];
cell.textLabel.text = [parts objectAtIndex:1];
cell.detailTextLabel.text = #"Some text";
UILabel *statusLabel = (UILabel *)[cell viewWithTag:1];
statusLabel.text = #"Did it!";
return cell;
}
I'm using one table view to display two lists, depending on which of two buttons is pressed. When a button is pressed the appropriate table view controller is attached to the table view, and the reloadData method is called to trigger display of the new data. The new data does display, but the custom label text, which should read "Did it!" reads "Status, set in code ...", until I switch lists again twice.
How can I get the new text for the custom label to update straight away? I have checked the official documentation and cannot find any reference to refreshing a cell's display after updating its custom content.
Here is a screen shot to demonstrate what happens: http://www.dsbsystems.co.uk/images/xcode1.png
You're initializing the cell and immediately attempting to find the statusLabel with tag 1 inside it. layoutSubviews hasn't had the opportunity to be called yet, and so the label hasn't been created and added. (I suggest overriding the designated initializer method on your table view cell and creating the label there.)
Because of this, when you try to pull out statusLabel, it becomes nil because there's no such view, and messaging (calling a method on) nil simply does nothing (actually, it returns nil). You will need to watch out for this going forward if you're used to things blowing up with e.g. null reference exceptions.
When the cell is requested again, a new cell isn't needed because it's available from the reuse queue, and the label will be found correctly.

Resources