A workaround to get textAlignment to center in UITableViewCellStyleSubtitle - iphone

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;

Related

UITableView Cell Button mixed up

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;

UITableView Cell Label Duplicating

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;}

left+right aligned text in UITableView cell, breaks when table is scrolled

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.

UITableView dequeueReusableCellWithIdentifier Theory

When apple developed the UITableView for the first iPhone they had a problem in performance when scrolling through it. Then one clever engineer discovered that the cause of this was that allocation of objects comes with a price, so he came up with a way to reuse cells.
"Object allocation has a performance cost, especially if the allocation has to happen repeatedly over a short period—say, when the
user scrolls a table view. If you reuse cells instead of allocating
new ones, you greatly enhance table-view performance."
Source: iOS Reference Library
To reuse a cell you use:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Now, what I am wondering is, what actually happens here? Does it look in the TableView if there is a cell with that identifier and just returns that one? Well yea duh, but if it sends a reference instead of allocating and I have a table view with let's say 4 cells with the same identifier all visible. How can it multiply itself into four instances without allocating?
I want to know this because I am building a calendar type component and all the cells have the same structure only the text within changes. So if I could somehow reuse my cells instead of allocating I think I might get a better performance.
My own theory is that it allocates the four cells (simply because it has too). When a cell disappears from the screen it will be put in the TableView reuse queue. When a new cell is needed it looks in the que if a cell with the same identifier is available, it invokes prepareForReuse method on that cell and it removes itself from the queue.
dequeueReusableCellWithIdentifier: only returns a cell if it has been marked as ready for reuse. This is why in almost every cellForRowAtIndexPath: method you will see something like
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Do something to cell
return cell;
In effect, enough rows will be allocated to fill the visible part of the tableview (plus one or two more). As cells scroll off screen, they are removed from the table and marked as ready for reuse. As the queue of "available cells" grows, your line that asks for a dequeued cell will start obtaining a cell to use, at which point you will not have to allocate anymore.
The code for deqeueueReusableCellsWithIdentifier: will look something like this:
(Taken from one of my own projects where I do something similar with views/pages in a paged scroll view)
- (UIView*) dequeueReusablePage
{
UIView* page = [reusablePages_ anyObject];
if (page != nil) {
[[page retain] autorelease];
[reusablePages_ removeObject: page];
}
return page;
}
So it keeps a simple NSMutableSet with reusable objects.
When cells scroll off the screen and are not longer visible, they are put in this set.
So you start with an empty set and the set will only grow if you actually have more data to show then is visible on the screen.
Used cell scrolls off the top of the screen, is put in the set, then taken for the cell that appears at the bottom of the screen.
The purpose of dequeueReusableCellWithIdentifier is to use less memory. if we use 100 cells in a tableView then need to create 100 cells every time.It reduce the app functionality and may cause crash.
For that dequeueReusableCellWithIdentifier initialise the particular number of cells that we created and the cells will use again for further processing.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *TableIdentifier = #"YourCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableIdentifier];
}
ExternalClassTableViewCell *myCell = [[ExternalClassTableViewCell alloc]init];
myCell.MyCellText.text = [tableData objectAtIndex:indexPath.row];
myCell.MyCellImage.backgroundColor = [UIColor blueColor];
return cell;
}

How can i make my UITableViewCell contain two labels?

I want my UITableViewCell to look like the image below where there seems to be two labels. Is this possible without subclassing UITableViewCell?
alt text http://img31.imageshack.us/img31/2764/photoobp.jpg
There are different styles of UITableVieWCell. See here:
https://developer.apple.com/documentation/uikit/uitableviewcell/cellstyle
I think you want to use UITableViewCellStyleValue1.
You can initialise your UITableViewCell with the relevant style:
https://developer.apple.com/documentation/uikit/uitableviewcell/1623276-init
When you use a style that has two labels, you can use the textLabel and detailTextLabel properties to set them, respectively.
You do not need to subclass a UITableViewCell in order to add content to it. Here could be a sample cell generation method with an extra label:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
UILabel *secondLabel = [[UILabel alloc] initWithFrame:cell.textLabel.frame];
secondLabel.textAlignment = UITextAlignmentRight;
secondLabel.tag = 12345;
[cell.contentView addSubview:secondLabel];
}
UILabel *second = [cell viewWithTag:12345];
second.text = #"Second!";
return cell;
}
Let me know if you have any questions. I can clarify some things if needed.
Not sure where u think you see 2 labels...you can set the UILabels number of lines property if you want more lines UILabel ref....Also there is a UITableViewCell type UITableViewCellStyleSubtitle
which contains a detailTextLabel on top of the regular text labels in UITableCell, so you already have a built in cell with 2 text fields, here is a ref ref to UITableViewCell
Its not 2 labels but 2 buttons, you need to add 2 buttons in contentView view of the cell. Or you can create a footer or header View and add these 2 buttons.