Mark Molina
Mark Molina

Reputation: 5077

Which method is better / cleaner?

While looping the

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {}

method im setting data to my custom cell (an image and a text). I was wondering what's the best way to achieve this.

I can use 2 different array (image array and text array) and do something like:

cell.image setImage: //get image from imageArray with indexPath.row
cell.text.text = [textArray objectAtIndex:indexPath.row];

or use a multi dimensional array like this:

cell.image setImage: [imageArray objectAtIndex:indexPath.row] objectAtIndex:0;
cell.text.text = [textArray objectAtIndex:indexPath.row] objectAtIndex:1;

// or use of dictionary with keys

What method is quicker or more readable?

Upvotes: 1

Views: 86

Answers (3)

Wolfgang Schreurs
Wolfgang Schreurs

Reputation: 11834

Personally I think the following is the cleanest solution:

  • Create a model for the items in your array.
  • Create an UITableViewCell subclass to display the model in the cell. The subclass will have a property that accepts the model and redraw itself when the model changes.

Let's say we have a news app. The array is filled with items for which we create the model NewsItem. The model header could look like this:

NewsItem.h

@interface FSNewsItem : NSObject <NSCoding>

@property (nonatomic, copy, readonly)   NSString *title;
@property (nonatomic, copy, readonly)   NSURL    *URL;
@property (nonatomic, copy, readonly)   NSString *time;
@property (nonatomic, copy, readonly)   NSString *points;
@property (nonatomic, copy, readonly)   NSString *author;

// initialiser
+ (FSNewsItem *)newsItemWithTitleNode:(HTMLNode *)node 
                          subTextNode:(HTMLNode *)subNode;

@end

Now for the cell we create a NewsItemCell. The code for NewsItemCell might look like the following:

NewsItemCell.h

@interface FSNewsItemCell : UITableViewCell

@property (nonatomic, strong) FSNewsItem *newsItem;

@end

NewsItemCell.m

@interface FSNewsCell ()

@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *detailLabel;

@end


@implementation FSNewsItemCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.titleLabel                       = [[UILabel alloc] init];
        [self addSubview:_titleLabel];

        self.detailLabel                      = [[UILabel alloc] init];
        [self addSubview:_detailLabel];
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    const CGRect bounds = self.bounds;
    CGFloat width = bounds.size.width - (FS_FLOAT_PADDING * 2) - 15.0f;
    _titleLabel.frame = CGRectMake(FS_FLOAT_PADDING, FS_FLOAT_CELL_PADDING_VERTICAL, width, 20.0f);

    CGFloat y = _titleLabel.frame.size.height + (FS_FLOAT_CELL_PADDING_VERTICAL * 2);
    _detailLabel.frame = CGRectMake(FS_FLOAT_PADDING, y, width, 15.0f);
}

#pragma mark - Private

- (void)setNewsItem:(FSNewsItem *)newsItem
{
    if (_newsItem == newsItem)
    {
        return;
    }

    _newsItem = newsItem;

    self.titleLabel.text = newsItem.title;

    if (_newsItem.points && _newsItem.time)
    {
        self.detailLabel.text = [NSString stringWithFormat:@"%@ | %@", _newsItem.points, newsItem.time];
    }
    else
    {
        self.detailLabel.text = newsItem.time;
    }

    [self setNeedsLayout];
}

Finally, when we want to display the news item in the cell, our code would look like this:

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

    FSNewsItemCell *cell = (FSNewsItemCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[FSNewsItemCell alloc] initWithStyle:UITableViewCellStylePlain reuseIdentifier:CellIdentifier];
    }

    cell.newsItem = [_items objectAtIndex:indexPath.row];

    return cell;
}

This is in my opinion the cleanest solution and the approach I take most of the time. I like to keep my view controllers small. I also like the fact that my view controller doesn't have to know which controls my (custom) table view cell has. The table view cell gets full responsibility on how to draw itself depending on the data supplied.

Upvotes: 2

Jeremy
Jeremy

Reputation: 9030

One idea is to create a custom class:

@interface CellInfo : NSObject
@property (....) UIImage *image;
@property (....) NSString *text;
@end

Then:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{
...
    CellInfo *info = [self.cellInfoArray objectAtIndex:indexPath.row];
    cell.image = info.image;
    cell.text = info.text;
...
}

Upvotes: 1

zaph
zaph

Reputation: 112855

I would add a simple data class and put class instances in the NSArray. Or use a NSArray of NSDictionary objects so the items can be addressed by name, not position.

Example class code (classes are very easy these days):

@interface DisplayItems : NSObject
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) UIImage *image;
@end

@implementation DisplayItems
@end

Upvotes: 1

Related Questions