uitableview subview : why this uilabel is not visible? - iphone

I want to display a label showing a number in each cell of the tableview but the label is only visible when I click on a row (when the cell is highlited)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UILabel *label;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
label = [[UILabel alloc] initWithFrame:CGRectMake(200,10, 15, 15)];
label.tag = 1;
[cell.contentView addSubview:label];
[label release];
}
else {
label = (UILabel *)[cell viewWithTag:1];
}
if (indexPath.row == 0) {
cell.textLabel.text = #"Photos";
label.text = [NSString stringWithFormat:#"%d",1];
}
return cell;
}

I had the same problem and it was solved by setting the text for textlabel BEFORE adding the custom label as a subview.
...
cell.textLabel.text = #"X";
...
[cell.contentView addSubview:label]

When you update the textLabel property of a UITableViewCell, it lazily creates a UILabel and adds it to the cell's subviews. Usually you wouldn't use a combination of textLabel and adding subviews to contentView, but if you do you need to make sure the textLabel view isn't placed over the top of your contentView subviews.

First, I assume this is targeting 3.0. Apple has changed how UITableViewCells are created in 3.0, and you should move over to that. -initWithFrame:reuseIdentifier: is deprecated.
That said, a likely problem is that the built-in textLabel is interfering with your added label, perhaps overlapping. You should look first at whether one of the new built-in styles meets your needs directly. If not I would recommend either just using your own views or only using the built-in views, possibly rearranging them. If you want to rearrange them, Apple suggests subclassing the cell and overloading -layoutSubviews. I also believe that -tableView:willDisplayCell:forRowAtIndexPath: is a good place to do final cell layout without subclassing.

Using a custom UITableViewCell gives you more control over the layout of a cell. Add custom views to the cell's contentView in the subclass and override the layoutSubviews to set the order of the subviews:
- (void)layoutSubviews {
[super layoutSubviews];
[self.contentView bringSubviewToFront:self.yourCustomView];
}

Related

Dequeueing UIViews withing UITableViewCell

In a normal situation when working with a UITableView I have the standard code for reusing old cells:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
return cell;
}
I noticed, however, that in the case when I added subviews to the cell that they weren't deleted and that a new view were added every time. I have an example below that demonstrate it perfectly:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
UILabel *label = [[UILabel alloc] init];
label.text = #"HELLO";
label.frame = CGRectMake(arc4random() % 50, -1, 286, 45);
label.backgroundColor = [UIColor clearColor];
// Add views
[cell addSubview:label];
return cell;
}
I need some code that reuses my labels again in the same way the cells are being reused. What should I do?
Thanks
You must only add the subviews if you are making a new cell. If you are dequeuing, the subview is already present and should not be re-created.
Your method should be:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:cellIdentifier];
UILabel *label;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
label = [[UILabel alloc] init];
label.tag = 1;
// Add views
[cell addSubview:label];
}
else
{
// Label will already exist, get a pointer to it
label = [cell viewWithTag:1];
}
// Now set properties on the subview that are unique to each cell
label.text = #"HELLO";
label.frame = CGRectMake(arc4random() % 50, -1, 286, 45);
label.backgroundColor = [UIColor clearColor];
return cell;
}
Note how the label is only created when the cell is nil. Otherwise, it is found using the tag.
I need some code that reuses my labels again in the same way the cells
are being reused.
No, you need to understand the table view design better. It should be obvious why the views are being added multiple times – reusing a cell means that you take a previous instance of UITableViewCell that’s no longer needed (thus saving a costly allocation of a new object) and reuse this instance for the new cell. But this previous instance already has the label attached to it, so the number of labels grows.
I would subclass UITableViewCell and put the label creation inside the initialization code for this new class. (Or create a UIView subclass and set it as the cell’s contentView, as suggested in this nice table tutorial by Matt Gallagher.) That’s the proper way to encapsulate the view details and hide them from the table data source.
you can use something like in the else part for if(cell == nil)
for (UIView *sub in [cell.contentView subviews])
{
if([UILabel class] == [sub class])
NSLog(#"%#",[sub class]);
UILabel *label = (UILabel *)sub;
//do label coding ie set text etc.
}
I use lazy initialization of views within my custom table cell class.
It only needs to load views and "addSubview" once.
- (void) lazyInitTitleLabel {
if (_titleLabel != nil) {
return;
}
_titleLabel = [[UILabel alloc] initWithFrame: CGRectMake(10.0f, 10.0f, 200.0f, 30.0f)];
// Cell adds the label as a subview...
[self addSubview: _titleLabel];
}
The only thing you need to be careful about is resetting any content that views display like text in your labels and images in your image views. If you don't old content may get reused along with the recycled table cells.
Good luck!

adding uilabel on uiimage showing on uitable view

I am adding label to uiimage and then adding that image on uitable view. but it is not showing. Can anyone help please?
This is code i am using:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}
//image which contains label
UIImageView *BgImage=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cell-bg.png"]];
BgImage.frame=cell.frame;
//label which will be on image
UILabel *NameLAbel=[[UILabel alloc]initWithFrame:CGRectMake(25, 5, 45, 45)];
NameLAbel.textColor=[UIColor blackColor];
if (indexPath.row==0) {
NameLAbel.text=#"Home";
}
else if (indexPath.row==1) {
NameLAbel.text=#"You";
}
else if (indexPath.row==2) {
NameLAbel.text=#"Contacts";
}
else if (indexPath.row==3) {
NameLAbel.text=#"Settings";
}
else {
NameLAbel.text=#"Sign Out";
}
NSLog(#"%#",NameLAbel.text);
[BgImage addSubview:NameLAbel];
//adding image on cell
[cell.contentView addSubview:BgImage];
[NameLAbel release];
[BgImage release];
return cell;
}
I'm not sure if you can actually add a UILabel to an UIImageView, although they are both UIView subclasses, I think the imageview will only display the image.
try adding the label to cell.contentView after you've added the image to the view. That should achieve the affect you are after.
Either that, or create of UIView which will hold both the UIImageView and the UILabel
Adding some UI widget as a subview of a UIImageView sounds really questionable. Even if it works now it may not work in the future, and I believe this breaks the UIKit convention. If you want to make one view look like it is "part of" another view, place the two as siblings, either directly in the cell.contentView as Amit noted, or if you want clipping so that the label looked like it was being clipped by the image, create a UIView container, enable the "clip subviews" setting of that view, and place the UIImageView and UILabel as siblings within that container UIView.

How to retrieve the data on selection of a cell from a custom UITableview

Might be a stupid question.
I have a UITableview, with multiple cells. In each cell I am displaying some data. I am not using cell's text property to display data. Instead I have a custom label inside my cell, which is displaying the text.
My question is:
When I click on the cell, I need to retrieve the data from the cell. How can I do this.
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
UILabel *CellTextlabel = [[UILabel alloc] init];
CellTextlabel.tag = 222;
[CellTextlabel setFrame:CGRectMake(40, 5, 200, 20)];
[cell.contentView addSubview:CellTextlabel];
[CellTextlabel release];
}
UILabel *editCellTextlabel = (UILabel *)[cell.contentView viewWithTag:222];
editCellTextlabel.font = [UIFont boldSystemFontOfSize:18];
editCellTextlabel.text = contact.lastName;
In your didSelectRowAtIndexPath method, you may do it as follows:
UITableViewCell *cell = (UITableViewCell *)[self.tableViecellForRowAtIndexPath:indexPath];
UILabel *textLabel = (UILabel *)[cell viewWithTag:222];
Now you can retrieve the data in the cell's UILabel using textLabel.text
You could get access to that label in didSelectRowAtIndexPath: with
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *myLabel = [cell.contentView viewWithTag:222];
But probably worth asking, why are you adding a sub label instead of using the textLabel property? you can modify its frame, settings etc and then you don't have to worry about tags, since this property is exposed in UITableViewCell by default
In the -tableView:didSelectRowAtIndexPath: method, you can get the data from your tableView's array:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
id objectForCell = [self.myArray objectAtIndex:indexPath.row];
//do what you want with the above data.
}

How can I resize the size of the cell.textLabel?

I want to resize the default textLabel of a UITableViewCell because I display a image at the right of the rows. I Tryed with this code but it doesn't works, and I don't understand why.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
cell.textLabel.frame = CGRectMake(5, 5, 100, 50);
//...
}
u should used custom label
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
UILabel *Lbl = [[UILabel alloc]initWithFrame:CGRectMake(75.0f, 4.5f, 360.0f, 20.0f)];
[cell.contentView addSubview:Lbl];
[Lbl release];
}
I think that is impossible.
Make custom cell.
UILabel myTextLabel;
//Set Frame and do something.
[cell.contentView addSubview:myTextLabel];
textLabel is readonly property so we can't set frame..
#property(nonatomic,readonly,retain) UILabel *textLabel __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0); // default is nil. label will be created if necessary.
#property(nonatomic,readonly,retain) UILabel *detailTextLabel __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0); // default is nil. label will be created if necessary (and the current style supports a detail label).
use custom cell ...
You can not change a cell's textLabel's frame except or you go with custom cell and use UILabel, UIImageView as a subview of the cell.

UITableViewCell textLabel offset

I want to have my text in my UITableViewCell to be a little bit to the right. In other words I want to have an x-offset. Is this even possble? Do I have to create a custom cell just because of this?
You could use cell.indentationLevel and if needed cell.indentationWidth instead of custom UITableViewCell for this.
You can try this :
[cell setIndentationLevel:SOME_NUMBER];
[cell setIndentationWidth:SOME_OTHER_NUMBER];
A simple solution is you can change the frame of the textLabel.
CGRect textLabelFrame = cell.textLabel.frame;
textLabelFrame.origin.x += xOffset;
textLabelFrame.size.width -= xOffset;
cell.textLabel.frame = textLabelFrame;
I've also done this by creating a custom UILabel that supports edgeInsets similar to a UIButton. This is a better solution b/c you can layout the label to correct size but the above will work if you have simple needs.
[EDIT 1/2: fixed typo w/ CGRect]
[EDIT 3: fixed typo setting modified frame]
[EDIT 4: need a simple subclass]
Mea culpa. I was wrong that you can do this in tableView:cellForRowAtIndexPath. The UITableViewCell layout happens after the tableView delegate/datasource has a chance to customize the cell. I've tested the implementation below, it works.
Do as I say above but create a (simple) subclass of UITableViewCell that adds the xOffset in layoutSubviews. If you do this, you can also add an xOffset property that you can set in tableView:cellForRowAtIndexPath:.
#implementation XOffsetCell
// assumes property xOffset is defined and synthesized
- (void) layoutSubviews
{
[super layoutSubviews];
CGRect textLabelFrame = cell.textLabel.frame;
textLabelFrame.origin.x += self.xOffset;
textLabelFrame.size.width -= self.xOffset;
cell.textLabel.frame = textLabelFrame;
}
#end
The solution that suggested adding a custom UILabel to cell.contentView is also a good solution. I saw your comment that it obscures the built-in textLabel but that's the whole point. You would not use the built-in label anymore, use the custom label instead.
I'm not trolling for votes, but wanted to show what the code provided by #iPhone monster "should" look like. His solution is a valid option. If you add the label to the cell after the if (cell == nil) as he did you will be continually adding labels to dequeued cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(50.0f, 10.0f, 150.0f, 20.0f)];
lbl.tag = OffsetLabelTag; // define this as a constant
[cell.contentView addSubview:lbl];
[lbl release];
}
UILabel *lbl = (UILabel *)[cell.contentView viewWithTag:OffsetLabelTag];
[lbl setText:#"test text"];
return cell;
}
You can do that with Autolayouts:
UITableViewCell *cell = [UITableViewCell new];
cell.textLabel.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(indentation)-[textLabel]-(indentation)-|"
options:0
metrics:#{#"indentation": #35}
views:#{#"textLabel": cell.textLabel}]];
Or using Parus lib:
[cell.contentView addConstraints:(PVVFL(#"H:|-(indentation)-[textLabel]-(indentation)-|")
.withViews(#{#"textLabel": textLabel})
.metrics(#{#"indentation": #35}).asArray)];
Other way:
- (NSInteger)tableView:(UITableView *)tableView
indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
return intOffset; // but is like '\t', not points
}
There is another not very pretty and smart way : ) Position of Label in TableViewCell depends on size of image assigned to cell.imageView.image property. So if you want your text to move right you can add several blank pixel columns to image in cell.
Based off of https://stackoverflow.com/a/5659826/1058199, here is a Swift version.
This works within the cell to ONLY move the textLabel to the right. Swift 5.4
override func layoutSubviews() {
super.layoutSubviews()
var textLabelFrame = self.textLabel?.frame
textLabelFrame?.origin.x += 32
textLabelFrame?.size.width -= 32
self.textLabel?.frame = textLabelFrame ?? CGRect.zero
}
Will cell.textLabel.textAlignment = UITextAlignmentCenter; be enough for you. It could align the text to the center creating that space for you