Selected UITableViewCell changes background on Scroll - objective-c

I have a UITableView with transparent background color, each of its cells have a custom background view, and gray selection style. The selection works fine, but when i select and drag the tableview up or down, the cell changes its background to transparent instead of the custom one. What shall i look to fix it?
EDIT: Source Code as requested by André Morujão
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
RestaurantInfo *restaurantInfo = [requestBean.restaurantArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
else {
cell = nil;
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleGray];
UIImageView *bgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)];
[bgView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"table_cell_bg.png"]]];
[cell setBackgroundView:bgView];
UILabel *restaurantNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, 310, 15)];
[restaurantNameLabel setFont:[UIFont boldSystemFontOfSize:16]];
[restaurantNameLabel setText:restaurantInfo.restaurantName];
[restaurantNameLabel setBackgroundColor:[UIColor clearColor]];
[restaurantNameLabel setUserInteractionEnabled:NO];
[cell addSubview:restaurantNameLabel];
return cell;
}

Sorry, these aren't necessarily the cause for your problem, but start by doing these:
no need for the else block (or did you just put it there for debugging purposes?)
it should be enough to apply the selectionStyle and background inside the if block (unless you're changing them somewhere else)
the restaurantNameLabel should probably be added to the cell's contentView and not directly as a subview
you're leaking restaurantNameLabel and bgView; add [bgView release] and [restaurantNameLabel release] after you're done with them
Also, are you using a UIImageView for any reason in particular? It'd probably be enough to use a UIView, or even just to apply a backgroundColor.

Related

sticky uitableview

i'm having a problem with uitableview when i scroll it up and down many times it becomes sticky and all the containing view becomes sticky too.
here is my code:
- (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] ;
}
cell.textLabel.text=[NSString stringWithFormat:#" %#",
[SongsNames objectAtIndex:indexPath.row]];
UIImageView *b=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 35, 34)];
b.image=[UIImage imageNamed:#"playbuttone.png"];
[cell.contentView addSubview:b];
[b release];
UIButton *b2=[[UIButton alloc] initWithFrame:CGRectMake(260, 0, 50, 35)];
[b2 setImage:[UIImage imageNamed:#"buye.png"] forState:UIControlStateNormal];
[cell.contentView addSubview:b2];
[b2 release];
cell.textLabel.textColor=[UIColor colorWithWhite:1 alpha:1];
}
after update:
cell.textLabel.text=[NSString stringWithFormat:#" %#",[SongsNames objectAtIndex:indexPath.row]];
[cell.contentView addSubview:b];
// [b release];
[cell.contentView addSubview:b2];
//[b2 release];
cell.textLabel.textColor=[UIColor colorWithWhite:1 alpha:1];
and in the viewdidload:
- (void)viewDidLoad{
b=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 35, 34)];
b.image=[UIImage imageNamed:#"playbuttone.png"];
b2=[[UIButton alloc] initWithFrame:CGRectMake(260, 0, 50, 35)];
[b2 setImage:[UIImage imageNamed:#"buye.png"] forState:UIControlStateNormal];
}
You are reloading your images for each call of that function. When scrolling this function gets called really a lot of times which is not neccessary, in particular because your images are static and identical for each cell.
Load the two images once when the view did load, save a reference in the view controller and reuse them. When the table view requests the cell. This very likely will solve your problem.
ok for anyone who will get that confusing problem stop adding uicontrols to ur uitableviewcell and use custom cell like in this tutorial
http://www.youtube.com/watch?v=PwcBdCUZNWs

How do I clear a cell completely when I reuse it?

When I call [table reloaddata];
The cells get redrawn with new data, but my UILabels get messed up because they are drawn over the old UILabels, so its a mess.
static NSString* PlaceholderCellIdentifier = #"PlaceholderCell";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:PlaceholderCellIdentifier] autorelease];
cell.detailTextLabel.textAlignment = UITextAlignmentCenter;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.contentView.backgroundColor = [UIColor clearColor];
}
Is my Init of the cell.
I add a UILabel like so
UILabel *theDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 35,140, 20)];
[theDateLabel setBackgroundColor:[UIColor clearColor]];
[theDateLabel setTextColor:[UIColor lightGrayColor]];
[theDateLabel setText:[dateFormatter stringFromDate:theDate]];
[theDateLabel setFont:[UIFont fontWithName:#"TrebuchetMS-Bold" size:15]];
[cell addSubview:theDateLabel];
[theDateLabel release];
There are a few more labels in the cell, same thing.
What I would like to happen is that the old labels disappear from the cell so that they are no longer visible.
You should not add theDateLabel as a subview of cell. You should add it as a subview of cell.contentView.
As yuji suggests, one way to implement this is to create a subclass of UITableViewCell with a property for each custom subview. That way you can easily get to the date label of a reused cell to set its text for the new row.
Another common approach is to use the tag property that every UIView has. For example:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* PlaceholderCellIdentifier = #"PlaceholderCell";
static const int DateLabelTag = 1;
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:PlaceholderCellIdentifier] autorelease];
UILabel *theDateLabel = [[UILabel alloc] initWithFrame:CGRectMake(140, 35,140, 20)];
theDateLabel.tag = DateLabelTag;
theDateLabel.backgroundColor = [UIColor clearColor];
theDateLabel.textColor = [UIColor lightGrayColor];
theDateLabel.font = [UIFont fontWithName:#"TrebuchetMS-Bold" size:15];
[cell.contentView addSubview:theDateLabel];
[theDateLabel release];
}
NSDate *theDate = [self dateForRowAtIndexPath:indexPath];
UILabel *theDateLabel = [cell.contentView viewWithTag:DateLabelTag];
theDateLabel.text = [dateFormatter stringFromDate:theDate];
return cell;
}
While Richard's solution will work, if your cells have any other subviews they'll get removed as well. Also, allocating and initializing your subviews every time you draw a cell isn't necessarily optimal.
The standard solution here is to create a subclass of UITableViewCell with a property #dateLabel (and so on for the other labels). Then, when you're initializing a cell, if it doesn't have a #dateLabel yet you can give it a new one, otherwise you only have to set its text.

UITableViewCell with images in different dimensions

I'm trying to use my ReusableCell for cells with images in different dimensions. The images are put inside a 220x150 black box with with scaling UIViewContentModeScaleAspectFit.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NewsTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NewsItem *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:item.imageUrl]];
[cell.imageView setImage:[[UIImage alloc] initWithData:data]];
[cell.imageView setBackgroundColor:[UIColor blackColor]];
[cell.imageView setContentMode:UIViewContentModeScaleAspectFit];
CGRect imageViewFrame = cell.imageView.frame;
imageViewFrame.size.width = 220;
imageViewFrame.size.height = 150
[cell.imageView setFrame:imageViewFrame];
[cell.textLabel setText:item.title];
return cell;
}
The above code results in a layout like below and the images are sometimes changing when scrolling in the table view.
Instead of this unstructured layout, I would like the images to be aligned like this:
What am I doing wrong with this ReusableCell?
EDIT1:
I'm trying to create an imageView and add this imageView as a superview to cell.contentView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NewsTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NewsItem *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *placeholderImage = [UIImage imageNamed:#"ImagePlaceholderThumb"]; //220x150
UIImageView *imageView = [[UIImageView alloc] initWithImage:placeholderImage];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:item.imageUrl]];
[imageView setImage:[[UIImage alloc] initWithData:data]];
[imageView setBackgroundColor:[UIColor blackColor]];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
CGRect imageViewFrame = imageView.frame;
imageViewFrame.size.width = placeholderImage.size.width;
imageViewFrame.size.height = placeholderImage.size.height;
[imageView setFrame:imageViewFrame];
[cell.contentView addSubview:imageView];
[cell.textLabel setText:item.title];
return cell;
}
The above code results in the following:
It is like some of the images are visible in two cells. It seems that they are not keeping the size I've set in the imageViewFrame. Do you know why?
A quick fix would be using content mode UIViewContentModeScaleAspectFill. Images will be stretched in one or both dimensions to fill up the whole image view bounds.
You really need subclassing UITableViewCell to do this right.
Thre is a lazy solution adding a new UIImageView and using a spacer, as Keller told you in his answer (feel free to accept his answer, this is just the missing code).
Extract of tableView:cellForRowAtIndexPath::
...
cell.textLabel.text = [NSString stringWithFormat:#"Cell #%i", indexPath.row];
cell.imageView.image = [UIImage imageNamed:#"spacer.png"]; /* spacer is 64 x 44 */
/* image view width should be ~ 64 px, otherwise it will overlap the text */
UIImageView *iv = [[UIImageView alloc] initWithFrame:(CGRect){.size={64, tableView.rowHeight}}];
switch (indexPath.row) {
case 0:
iv.image = [UIImage imageNamed:#"waterfall.png"];
break;
/* etc... */
}
if (indexPath.row < 3) {
/* add black bg to cell w/ images */
iv.backgroundColor = [UIColor blackColor];
}
iv.contentMode = UIViewContentModeScaleAspectFit;
[cell.contentView addSubview:iv];
...
The table will look like this:
You need to set the placeholder (spacer.png above) in the existing cell image view. It will push the text label to the right.
You can use aspect fill and remove the background color bit:
iv.contentMode = UIViewContentModeScaleAspectFill;
The table will look wrong because the image is drawn outsite the bounds:
Just clip to bounds to get a better result:
iv.clipsToBounds = YES;
Create a UIImageView subview for each cell and it to the contentView. Each UIImageView contains an image with a consistent frame but with option UIViewContentModeScaleAspectFit. Then just Set the background color of the UIImageView to black.
I just confirmed this works, but you need to also create a placeholder spacer image to make sure the textLabel moves out of the way. Just make it the same dimensions of your image (with the letter boxing).
- (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];
//spacer
cell.imageView.image = [UIImage imageNamed:#"placeholder"];
//imageview
UIImageView *thumbnail = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 80, 44)];
thumbnail.tag = kThumbTag;
thumbnail.backgroundColor = [UIColor blackColor];
thumbnail.contentMode = UIViewContentModeScaleAspectFit;
[cell.contentView addSubview:thumbnail];
}
// Configure the cell...
cell.textLabel.text = [NSString stringWithFormat:#"Cell %d", indexPath.row];
cell.imageView.frame = CGRectMake(0, 0, 80, 44);
UIImageView *thumb = (UIImageView*)[cell.contentView viewWithTag:kThumbTag];
if (indexPath.row == 0) {
[thumb setImage:[UIImage imageNamed:#"image1.png"]];
} else {
[thumb setImage:[UIImage imageNamed:#"image2.png"]];
}
return cell;
}
Obviously, this example isn't lazy loading the images (I didn't realize you were loading them from a URL). For that, I would use a subclass with EGOImageView or something of the like.

How to change one UITableViewCell when it is touched

I have a table view cell with multiple images in them. When touching the images they shold display an overlay on top of the image which tells the user that this image was selected.
Is there a way to change the look of just one UITableViewCell without having to do a [tableView reloadData] which would allow me to style the cell differently in the table view datasource delegate method.
The way I would do it is to subclass UITableViewCell and then on tableView:didSelectRowAtIndexPath: get a reference to the cell and do whatever you want to it (or just target the image touch event if this is not a selection).
There might be another way of doing this without having to subclass, but I find myself subclassing UITableViewCell all the time and it's pretty straightforward to do.
If you wish to avoid subclassing, this can be achieved with gesture recognisers. Your question suggests a Tap and Hold user interaction on each image, which I have implemented in the code below. One point to remember, if the user is tapping and holding, they may not see the text you wish them to see.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ImageCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
UILongPressGestureRecognizer *recognizer2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Ben.png"]];
imageView.frame = CGRectMake(cell.contentView.bounds.origin.x,cell.contentView.bounds.origin.y , 100, 40);
imageView.userInteractionEnabled = YES;
[imageView addGestureRecognizer:recognizer];
[cell.contentView addSubview:imageView];
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Steve.png"]];
imageView2.frame = CGRectMake(cell.contentView.bounds.origin.x + imageView.frame.size.width + 10,cell.contentView.bounds.origin.y , 100, 40);
imageView2.userInteractionEnabled = YES;
[imageView2 addGestureRecognizer:recognizer2];
[cell.contentView addSubview:imageView2];
[imageView release];
[imageView2 release];
[recognizer release];
[recognizer2 release];
return cell;}
- (void)imageTapped:(id)sender {
NSLog(#"%#", sender);
UILongPressGestureRecognizer *recognizer = (UILongPressGestureRecognizer *)sender;
if (recognizer.state == UIGestureRecognizerStateBegan) {
UILabel *label = [[UILabel alloc] initWithFrame:recognizer.view.bounds];
label.text = #"Pressed";
label.backgroundColor = [UIColor clearColor];
label.tag = 99999;
label.textColor = [UIColor whiteColor];
[recognizer.view addSubview:label];
[label release];
}
else {
[[recognizer.view viewWithTag:99999] removeFromSuperview];
}
}
Hope this helps.

Make a cell's background transparent, without also making the text transparent

I have a UITableView that I want to have a transparent background, but I can't seem to get that without making the text in the cells transparent as well. Here is what I have.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSString *labelString = #"Hello";
[cell setBackgroundColor:[UIColor darkGrayColor]];
cell.alpha 0.2f;
cell.textLabel.textcolor = [UIColor whiteColor];
cell.textLabel.text = labelString;
I have tried several other things such as setting the contentView's background color and alpha, as well as the backgroundView's color and alpha, nothing seems to work.
Instead of setting the cell.alpha,
cell.backgroundColor = [UIColor clearColor];
You may also need to set the table view background.
Placing this code in the -tableView:willDisplayCell:forRowAtIndexPath: solved the issue.
UIView *backView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
backView.backgroundColor = [UIColor clearColor];
cell.backgroundView = backView;