stepik21
stepik21

Reputation: 2640

UITableViewCell with subviews is not working properly

I have UITableView in my iOS app and I want to add some subviews to cell. I do it by using

[cell.contentView addSubview:someView];

and it works well, but... When I scroll down, subviews are starting to hide from cells that are on top and when I scroll back to top, they wont appear again... What I'm doing wrong? Is there some solution please?

EDIT

Mainly, I'm talking about "detailtext" label, but I have those problems in more cases...

Here is whole code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell;

switch (indexPath.row) {
    case 0:
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        break;

    default:
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
        break;
}


UIView *separatorLine = [[UIView alloc] init];

separatorLine.frame = CGRectMake(15.0f, 60 - 0.5f, cell.frame.size.width-15.0f, 0.5f);
separatorLine.tag = 4;
separatorLine.backgroundColor = [UIColor lightGrayColor];

cell.layer.masksToBounds = NO;

tableView.backgroundColor = [UIColor colorWithRed:33.0 / 255.0 green:157.0 / 255.0 blue:147.0 / 255.0 alpha:1.0];

cell.selectionStyle = UITableViewCellSelectionStyleNone;

UIView *row2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 200)];

UIView *profileBorder = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2-50, 50, 102, 102)];

profileBorder.layer.borderColor = [UIColor whiteColor].CGColor;
profileBorder.layer.borderWidth = 5; //2
profileBorder.layer.cornerRadius = 50;


NZCircularImageView *profileImage = [[NZCircularImageView alloc] initWithFrame:CGRectMake(1,1, 100, 100)];
profileImage.image = profilePhoto;
profileImage.contentMode = UIViewContentModeScaleAspectFill;

UITapGestureRecognizer *showBigProfilePhoto = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showImage:)];

profileImage.userInteractionEnabled = YES;
[profileImage addGestureRecognizer:showBigProfilePhoto];

[profileBorder addSubview:profileImage];

UILabel *numberFeelings = [[UILabel alloc] initWithFrame:CGRectMake(10, 100-25, 100, 50)];

numberFeelings.text = [NSString stringWithFormat:@"%@\nFeelings", feelings];
numberFeelings.font = [UIFont boldSystemFontOfSize:16];
numberFeelings.textAlignment = NSTextAlignmentCenter;
numberFeelings.textColor = [UIColor whiteColor];
numberFeelings.numberOfLines = 0;

UILabel *numberFriends = [[UILabel alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2+60, 100-25, 100, 50)];

numberFriends.text = [NSString stringWithFormat:@"%@\nFollowers", friends];
numberFriends.font = [UIFont boldSystemFontOfSize:16];
numberFriends.textColor = [UIColor whiteColor];
numberFriends.numberOfLines = 0;
numberFriends.textAlignment = NSTextAlignmentCenter;

[row2 addSubview:profileBorder];
[row2 addSubview:numberFriends];
[row2 addSubview:numberFeelings];

int rectButtons = cell.frame.size.width-246;

UIImageView *graph = [[UIImageView alloc] initWithFrame:CGRectMake(rectButtons/2, -20, 82, 82)];
UIImageView *badgets = [[UIImageView alloc] initWithFrame:CGRectMake(rectButtons/2+82, -20, 82, 82)];
UIImageView *photos = [[UIImageView alloc] initWithFrame:CGRectMake(rectButtons/2+164, -20, 82, 82)];

graph.image = [UIImage imageNamed:@"graph.jpg"];
badgets.image = [UIImage imageNamed:@"badgets.jpg"];
photos.image = [UIImage imageNamed:@"photos.jpg"];

graph.userInteractionEnabled = YES;
badgets.userInteractionEnabled = YES;
photos.userInteractionEnabled = YES;

UITapGestureRecognizer *graphTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showGraph:)];

[graph addGestureRecognizer:graphTap];

NSArray *jmenoCasti = [name componentsSeparatedByString:@" "];

krestni = [jmenoCasti objectAtIndex:0];

int indexOfPost = indexPath.row-3;

NSMutableAttributedString *str;
int countFeeling;
int countString;
int countBeforeFeeling;

if (indexPath.row >=3) {

    str = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ was %@", krestni, [naladyHim objectAtIndex:[[[posts objectAtIndex:indexOfPost] objectForKey:@"_feel"] integerValue]]]];

    countFeeling = [[naladyHim objectAtIndex:[[[posts objectAtIndex:indexOfPost] objectForKey:@"_feel"] integerValue]] length];

    countString = krestni.length+5+countFeeling;

    countBeforeFeeling = countString-countFeeling+1;

    int rangeStart = countBeforeFeeling-1;
    int rangeStop = str.length-rangeStart;

    NSLog(@"%i ... %i", countBeforeFeeling-1, countString-1);

    [str addAttribute:NSFontAttributeName value: [UIFont fontWithName:@"Helvetica-Bold" size:16.0f] range:NSMakeRange(rangeStart, rangeStop)];
    [str addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:32.0 / 255.0 green:147.0 / 255.0 blue:138.0 / 255.0 alpha:1.0] range:NSMakeRange(rangeStart, rangeStop)];

}

UILabel *mainText = [[UILabel alloc] initWithFrame:CGRectMake(15, 70, cell.frame.size.width-10, 20)];
mainText.attributedText = str;

UILabel *detailText;

if (!detailText) {
    detailText = [[UILabel alloc] initWithFrame:CGRectMake(15, 90, cell.frame.size.width-10, 30)];
}
detailText.textColor = [UIColor grayColor];
detailText.font = [UIFont systemFontOfSize:13];

switch (indexPath.row) {
    case 0:
        cell.textLabel.textAlignment = NSTextAlignmentCenter;
        cell.textLabel.text = name;
        cell.textLabel.textColor = [UIColor whiteColor];
        cell.textLabel.font = [UIFont systemFontOfSize:20];
        cell.backgroundColor = [UIColor clearColor];
        break;

    case 1:
        [cell.contentView addSubview:row2];
        cell.backgroundColor = [UIColor clearColor];
        break;

    case 2:
        cell.backgroundColor = [UIColor colorWithRed:236.0 / 255.0 green:235.0 / 255.0 blue:210.0 / 255.0 alpha:1.0];
        [cell.contentView addSubview:graph];
        [cell.contentView addSubview:badgets];
        [cell.contentView addSubview:photos];
        break;

    default:
        detailText.text = [[posts objectAtIndex:indexPath.row-3] objectForKey:@"_text"];
        [cell.contentView addSubview:detailText];
        cell.textLabel.attributedText = str;
        cell.backgroundColor = [UIColor colorWithRed:236.0 / 255.0 green:235.0 / 255.0 blue:210.0 / 255.0 alpha:1.0];
        break;
}


return cell; }

Upvotes: 1

Views: 1039

Answers (3)

blackpower
blackpower

Reputation: 21

This is an easy way thats works for me:

for(UIView *subview in cell.contentView.subviews)
{
    if([subview isKindOfClass: [UIView class]])
    {
        [subview removeFromSuperview];
    }
}

You can use it at the begin of

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

Upvotes: 2

JonahGabriel
JonahGabriel

Reputation: 3084

You should create a subclass of UITableViewCell for each different cell and add all your view related code that doesn't change depending on the data into an initialization method. Then create a method in each cell called something like configureWithData and pass in the data relevant to the cell. The creation of your attributed string and modification of label frames can occur in this configuration method.

It will dramatically reduce the clutter in your UITableViewController and is much better design wise. There is no real need for your view controller to know what your table cells look like.

Here is an example of what I am talking about:

-(void)awakeFromNib
{
    if( self.accessoryType == UITableViewCellAccessoryDisclosureIndicator )
    {
        DTCustomColoredAccessory *accessory = [DTCustomColoredAccessory accessoryWithColor:[UIColor whiteColor]];
        accessory.highlightedColor = [UIColor blackColor];
        self.accessoryView = accessory;
    }
}

-(void)configureCellWithObject:(id)inObject
{
    TableDataModel *dataObject = (TableDataModel *)inObject;
    self.titleLabel.text = dataObject.titleString;
    self.subtitleLabel.text = dataObject.subtitleString;
    self.accessibilityIdentifier = dataObject.accessIdString;

    if( dataObject.imageUrlString != nil )
    {
        UIImage *iconImage = [UIImage imageNamed:dataObject.imageUrlString];
        if( iconImage != nil )
        {
            NSInteger yOffset = [StaticTools centerYOffset:self.frame.size objectFrameSize:iconImage.size];
            self.iconImageView.image = iconImage;
            CGRect frame = self.iconImageView.frame;
            frame.origin.y = yOffset;
            frame.size = iconImage.size;
            [self.iconImageView setFrame:frame];
        }
        else
        {
            [self.iconImageView loadImageFromUrl:dataObject.imageUrlString];
        }
    }
}

Upvotes: 1

Maksim Usenko
Maksim Usenko

Reputation: 311

In your tableView:cellForRowAtIndexPath:, you hide the info when you don't want it to be shown, but you don't explicitly unhide it for cells where it should be shown.

Look at the first two lines in that method: What you are - correctly - doing is reusing your cells, so when cells are scrolled out of view, they are removed from the UITableView and put into the reuse queue. Then, when cells should become visible, the TableView gets cells from that queue - or creates new ones if none are available.

This all goes very well, but after a while, cells with hidden info buttons are put on the queue. And then, some time later, those cells are reused - and sometimes for rows in which there should be info visible.

There are two solutions to this: You could either explicitly unhide the information for those rows where you want it to be shown, or you could use two different kinds of cell, one with hidden info, and one with visible info. You then give each of those cells a different identifier, and based on what row the cells are in, set the identifier before dequeuing/creating cells.

Upvotes: 1

Related Questions