My goal is to display 2 strings in the same cell, one of them left aligned and the other right aligned. The code I have attached does just that in a table view, however it breaks when you scroll up/down. I need this to work in a table that can scroll. Someone had mentioned using CustomUITableViewCells instead of my current method, can anyone point me to an example of this?
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
UILabel *rank = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, 100, 20];
[rank setTag:5];
[cell.contentView addSubview:rank];
[rank release];
UILabel *item = [[UILabel alloc] initWithFrame:CGRectMake(110, 5, 220, 20];
[item setTextAlignment:UITextAlignmentRight];
[item setTag:6];
[cell.contentView addSubview:item];
[item release];
}
UILabel *rank = (UILabel *)[cell viewWithTag:5];
UILabel *item = (UILabel *)[cell viewWithTag:6];
rank.text = #"leftside";
item.text = #"rightside";
}
Any ideas and thoughts greatly appricated, thanks for lookin
This problem is because of dequeueReusableCellWithIdentifier. While the cell is being re-used, and when you scroll up and down it will cause major problems as labels are being added as subviews to the cells and they do not have the properties of the cell. However, if you use the cell.textLabel as your label, it would not cause problems like the one you are facing now, but you cannot add more than one label.
You have two solutions for this.
In your case, you need to stop using the same cellIdentifier for each and use different identifiers for each cells so that they do not get reused. This would be helpful if you have a very small number of rows in the tableView or it would turn out to be inefficient.
A better solution would be to subclass UITableViewCell and add those two labels in it's code, and then use that UITableViewCell with dequeueReusableCellWithIdentifier. This is just a small amount of work, and you get to re-use cells. This would be very helpful if you have a large number of rows in your tableview.
Go through THIS TUTORIAL to learn on how to subclass UITableViewCell with 2 labels.
You will need to work with the method, - (void)layoutSubviews and add those labels to your custom UITableViewCell subclass.
And remember to reference this customUITableViewCell instead of the default uitableviewcell when you are loading up the tableView. Your UILabels will not be messed up anymore.
Another reference.
Well, I have paste the same code that you have posted and I got 2 compiler error mentioning the ) is missing at the allocation of UILable and as I have clear it out, its compiled and started successfully.
The only exception and crash I have faced and that was due to the datasource method is not returning any cell. And that is also missing in given code.
Beside that, the code is working perfectly at my end and not having a single crash even though I scrolled many times.
So, just verify your code again or there should be another problem regarding datasource provided by array and also check the number of rows in section.
I would use a custom UITableViewCell. The easiest way is to just download a sample project, and copy and paste to see how you set up a custom cell. This is a good tutorial with a sample project included. You can use if (indexPath.row == int) in the cellForRowAtIndexPath method to determine which cells should be the standard ones, and which should be your custom cell.
Related
I have to make an application where I can add N number of rows in UITableview.This is how I am using reusability.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
UITableViewCell *Cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(Cell==nil)
{
Cell=[self tableViewCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath ofTableView:tableView];
}
[Cell setSelectionStyle:UITableViewCellEditingStyleNone];
[Cell setAccessoryType:UITableViewCellAccessoryNone];
[self ConfigureCell:Cell forIndexPath:indexPath forTableView:tableView];
return Cell;
}
Then on swipe right I have to draw a line and add button on a particular cell of UITableview that I am doing in swipe gesture method after retrieving the UITableviewCell in that method and have to move cell to the bottom after adding the subviews.
CGPoint location = [recognizer locationInView:GroupedTableView];
NSIndexPath *IndexPath = [GroupedTableView indexPathForRowAtPoint:location];
UITableViewCell *cell = [self.GroupedTableView cellForRowAtIndexPath:IndexPath];
UIButton *CrossButton=[UIButton buttonWithType:UIButtonTypeRoundedRect];
CrossButton.frame=CGRectMake(250, 12, 15, 15);
[CrossButton setBackgroundImage:[UIImage imageNamed:#"x.png"] forState:UIControlStateNormal];
CrossButton.tag=800+IndexPath.row;
[CrossButton addTarget:self action:#selector(CrossButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:CrossButton];
UIImageView *LineImage=[[UIImageView alloc]init];
LineImage.frame=CGRectMake(10, 19, 220, 2);
LineImage.tag=700+IndexPath.row;
LineImage.image=[UIImage imageNamed:#"line.png"];
LineImage.userInteractionEnabled=YES;
[cell.contentView addSubview:LineImage];
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:RowNum inSection:0];
[self.GroupTableContentArray insertObject:[self.GroupTableContentArray objectAtIndex:IndexPath.row] atIndex:lastIndexPath.row+1];
[self.GroupTableContentArray removeObjectAtIndex:IndexPath.row];
[self.GroupedTableView moveRowAtIndexPath:IndexPath toIndexPath:lastIndexPath];
This also works fine till the table height is small but once it is bigger than the screen size and I scroll to see the bottom content,the line I have drawn on that particular cell vanishes and sometimes its visible on other cell which was not marked.I know this is the issue of reusability but I am unable to figure out a way.
My requirement is like any.Do app where we swipe right to select a finished task and put it at the bottom of the table.Any help would be highly appreciated.
It seems to me that you don't quite understand how table view cell reuse works.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Asks the table delegate for a cell that is going to be displayed at the specified index path. You ask the table view for a reusable cell with dequeueReusableCellWithIdentifier:. Now, if the returned cell is nil it is your job to create a new cell.
When setting cell properties like text, detail text and image, you have to treat each cell as if it has unwanted data, meaning that you should overwrite everything each time before returning a cell.
To give you an example of this, imagine you have a table view with 10 visible cells and a 100 rows. Your first cell has "Hello world" in it and the other ones are empty. Now, when you start scrolling you are going to be seeing "Hello world" every 10 or so cells. This is happening because a random available cell is being returned from the tables reusable cell queue which has kept all of its changes like text, images, and subviews.
And that is also what is happening to your cell with the line and the button, so to avoid this, appropriate properties should be set for each index path. The problem is that you are adding subviews to your cell with no reference to them so they cannot be removed or hidden easily and so are always visible at random index paths. Furthermore you're going to get in a situation where you have multiple lines and buttons in the same cell.
It would be best for you to create a UITableViewCell subclass where each cell has its own line and button which can be shown/hidden as necessary for each index path.
I have a problem, setting a button to a UITableviewCell.
After viewDidLoad, the button is on the right place. But when I am scrolling down, the button is anyplace else.
Here is my code, I hope you can help me.
Thanks In Advance.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.section == 0 && indexPath.row == 0 && _isAddImageViewLoad == NO) {
// Add Image Button
UIButton *addImage = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage* image = [UIImage imageNamed:#"AddImage#2x"];
addImage.frame = CGRectMake(110.0f, 10.0f, 110.0f, 110.0f);
[addImage setImage:image forState:UIControlStateNormal];
[cell.contentView addSubview:addImage];
_isAddImageViewLoad = YES;
} else {
NSDictionary *dictionary = [_items objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"data"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}
return cell;
}
It is because you are reusing the cells, and the button is getting placed when it shouldn't an easy solution in your else section. Write addImage.hidden = YES; and in your if statement put addImage.hidden = NO;
Just a couple things. If you use "AddImage" it will use the "AddImage#2x" automatically if it's a retina display. I don't think that will solve your issue but it could be causing weirdness.
When a table view cell is scrolled off the view it is "recycled" in a sense. It appears like you are using a bool to exclude the original cell from being loaded again with a button. You may want to use a header to hold your button if you always want it at the "top". You may also want to verify that the button is being removed when the cell is reused. if its not it will show up in the next row that reuses that cell.
On a side note... Buttons don't usually work very well in table view cells because they handle touches in very different ways. It's quite a bit of modification to get them to feel natural but that's another matter.
Hope that helps!
The problem is because of cell reuse. You need to put some code in the else clause to delete the button if it exits. One way to do this, would be to give your button a tag, like:
addImage.tag = 10;
Then in your else clause:
}else{
if (cell viewWithTag:10) [[cell viewWithTag: 10] removeFromSuperview];
...
The problem is because of the dequeue for the cells. The first time the tableview creates the cells, all the cells run through the
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
code. But when the section 0 row 0 is moved off the screen, that cell is pushed into the cell reusable queue.
Now when your tableview needs to display section 0 row 0, it will get a cell from the reuse queue. you will not get the same cell as the first time. So now you might have 2 cells with the button.
What you should do is have different CellIdentifier for section 0 row 0 , and all other sections and rows. Also create the button when creating the cell. So after the first time the tableView creates the cell, you will not be creating the the button everything.
Look at this line of code:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
This line of code means the UITableViewCells are not created every time. They are re-used as you scroll up and down. Using the code you have above, the UIButton will be created in the correct spot, but then as the cells are re-used, it will create the button in random spots.
One quick way to solve the problem, change the above line of code to simply
UITableViewCell *cell;
I have a UINavigationController full of UITableViews and I use custom cells in a specific one of these.
In tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath I add a UILabel with [cell addSubview:label];
This works great, however if I then go into the detail view, then return back, it duplicates the label. How can I stop this?
Thanks.
The problem is that you are adding a new label every time the cell gets reused and displayed again.
Since you're using a custom cell already, the easiest solution would be to give the cell a UILabel property, and use that instead of adding a new label each time. Specifically, you should only add a new label if you're creating a cell instead of getting a recycled one. Or, you could add a new UILabel only if the property is nil.
Or as an alternative to adding the label in the custom class, just make sure to add the label inside the conditional cell creation. e.g.
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:ident];
if(cell == nil){
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:ident] autorelease];
[cell.contentView addSubview:[[[UILabel alloc] init] autorelease]]; // <-------
}
Add a tag to the label, then check if it exists
UILabel *label;
if ([cell viewWithTag:2]){
label = [cell viewWithTag:2];}
else{
label = [[UILabel alloc]init];label.tag = 2;}
i have the following code which will displays result in a UItable view along with an image.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//create a cell
UITableViewCell *cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"cell"];
// fill it with contnets
NSDictionary *exercise = [exercises objectAtIndex:indexPath.row];
cell.textLabel.text = [exercise valueForKey:#"player"];
UIImage *image = [UIImage imageNamed:#"iphone.gif"];
cell.imageView.image = image;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
// return it
return cell;
}
is there an option where i can display one image for all the cells for ex. 1 image on the left side and then 3 rows by the right side. I am a new bid and still getting my grip on iPhone coding.Please suggest me how we can do this.Thanks.
Yup, a UITableViewCell is pretty much another UIView, so you can add subviews to it and customize it anyway you need. For example, if you need to add an image to all the cells, just add it onto the contentView;
[cell.contentView addSubview:myImageView];
If you have several customizations needed for your cell, and are looking for a highly custom look as opposed to the generic look provided by the standard cells, I'd recommend looking into creating a custom UITableViewCell. The reason is that the standard cells have already laid out UI's with labels, images etc, and anything you add onto it may interfere with the existing UI in ways you do not intend.
According to this question (and many many google search results), when we're in a table view, and we set the cells to have UITableViewCellStyleSubtitle, we have been forced to align our texts inside the cell label to be on the left.
However I want one of the rows (in fact the row) of my table view to be aligned center... Obviously this wouldn't work:
cell.textLabel.textAlignment = UITextAlignmentCenter;
So I've been thinking what I should do instead... I'm not sure how to do it cleanly. The best way I can think of is to change this particular cell so that the style of the cell is UITableViewCellStyleDefault, but I don't think the cell property is changeable. Another way that I can think of is to subclass the UITableViewCell class, but that seems to be a bit of an overkill just for one table view cell. What do you think I should do to make that one last row aligned center?
EDIT: for those of you who're interested in the answer:
static NSString *CellIdentifier = #"Cell";
static NSString *CellIdentifierDefault = #"CellDefault";
UITableViewCell *cell; // = [pTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (indexPath.row >= [data count])
cell = [pTableView dequeueReusableCellWithIdentifier:CellIdentifierDefault];
else
cell = [pTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil && indexPath.row < [data count]) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
else if(cell == nil && indexPath.row >= [data count]) {
//Use default style for the last cell.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifierDefault] autorelease];
}
#the_great_monkey:
You can change the cellIdentifier for the cell where you want this center alignment.
For the other cells you should keep one common cellIdentifier.
Then when you do this
cell.textLabel.textAlignment = UITextAlignmentCenter;
cell.detailTextLabel.textAlignment = UITextAlignmentCenter;
Then simply you need to check if cellIdentifier is the one for the cell which requires center alignment?
If Yes, then apply,
cell.textLabel.textAlignment = UITextAlignmentCenter; and cell.detailTextLabel.textAlignment = UITextAlignmentCenter; for that cell.
Hope this helps you.
It's easy to load a cell from a .xib, which lets you arrange the cell's subviews any way you like. Unfortunately, UITableViewCell's textLabel and detailTextLabel properties aren't IBOutlets, so you can't connect the labels that you'd like to use for these properties in IB.
One way to handle this is to connect those views to outlets in your controller instead, and use them when you set up the cell in -tableView:cellForRowAtIndexPath:. That's fine if you won't need to change the content of the cell later. In that case, you can just create a nib containing a UITableViewCell, set the type of File's Owner to match your controller, and set up the cell's subviews however you like and connect them to File's Owner. See Loading Custom TableView Cells from Nib Files for more information.
The other way to go is to create a UITableViewCell subclass that has outlets for the text label and detail label. Then do the same as above, but use your subclass rather than UITableViewCell. Once you've created this subclass, you can use it with different .xibs if you want different layouts at some point. The work needed to create the subclass is minimal, and it becomes a useful and reusable tool.
cell.textLabel.textAlignment=UITextBorderStyleLine;