1

I'm subclassing UITableViewCell, and it's causing my app to crash... The issue I was having is my cells kept drawing over themselves, so the answer I found after searching is a subclass, but I'm having some issues. Here is my code.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


    static NSString *requestCell = @"requestCell";

    RequestCell *cell;

    switch (indexPath.section) {
        {case 0:
            cell = [tableView dequeueReusableCellWithIdentifier:requestCell forIndexPath:indexPath];
            if (cell == nil) {
                cell = [[RequestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:requestCell];
            }

            //APP CRASHES ON THIS LINE 
            cell.requestTitle.frame = CGRectMake(60, 5, self.view.frame.size.width - 70, 30);
            cell.detailTitle.frame = CGRectMake(60, 30, self.view.frame.size.width - 70, 25);

            Request *request = [[Request alloc] init];
            request = self.isNotFilledList[indexPath.row];
            cell.requestTitle.text = request.productName;
            cell.detailTitle.text = request.dateRequested;
            cell.detailTitle.font = [UIFont systemFontOfSize:11.0];

            NSString *imageURL = @"myurl";
            imageURL = [imageURL stringByAppendingString:request.productImageName];

            cell.requestImageButton.frame = CGRectMake(5, 5, 50, 50);
            [cell.requestImageButton sd_setBackgroundImageWithURL:[NSURL URLWithString:imageURL] forState:UIControlStateNormal];

            NSLog(@"request name %@", request.productName);
            return cell;
        break;}

        {case 1:
            cell = [tableView dequeueReusableCellWithIdentifier:requestCell forIndexPath:indexPath];
            if (cell == nil) {
                cell = [[RequestCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:requestCell];
            }

            Request *request = [[Request alloc] init];
            request = [self.isFilledList objectAtIndex:indexPath.row];
            cell.textLabel.text = request.productName;
            return cell;
            break;}

        default:
            break;
    }

    return cell;
}

And my RequestCell class

@interface RequestCell : UITableViewCell

@property (strong, nonatomic) UILabel *requestTitle;
@property (strong, nonatomic) UILabel *detailTitle;
@property (strong, nonatomic) UIButton *requestImageButton;

and .m file

#import "RequestCell.h"

@implementation RequestCell

- (void)awakeFromNib {
    // Initialization code
}

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {


    }
    return self;
}

-(UILabel *)requestTitle
{
    if(!_requestTitle)
    {
        _requestTitle = [[UILabel alloc] init];
    }
    return _requestTitle;
}

-(UILabel *)detailTitle
{
    if(!_detailTitle)
    {
        _detailTitle = [[UILabel alloc] init];
    }
    return _detailTitle;
}


-(UIButton *)requestImageButton
{
    if(!_requestImageButton)
    {
        _requestImageButton = [[UIButton alloc] init];
    }
    return _requestImageButton;
}



- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

What am I doing wrong???

Original Code **Re-draws cells over top

switch (indexPath.section) {
        {case 0:
            cell = [tableView dequeueReusableCellWithIdentifier:requestCell forIndexPath:indexPath];
            if (cell == nil) {
                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:requestCell];
            }

            UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(55, 5, self.view.frame.size.width - 60, 30)];
            UILabel *detailTitle = [[UILabel alloc] initWithFrame:CGRectMake(55, 30, self.view.frame.size.width - 60, 25)];

            Request *request = [[Request alloc] init];
            request = self.isNotFilledList[indexPath.row];
            title.text = request.productName;
            detailTitle.text = request.dateRequested;

            NSString *imageURL = @"myurl";
            imageURL = [imageURL stringByAppendingString:request.productImageName];

            UIButton *keyImageButton = [[UIButton alloc] initWithFrame:CGRectMake(5, 5, 50, 50)];
            //[[keyImageButton imageView] setContentMode: UIViewContentModeScaleAspectFill];
            [keyImageButton sd_setBackgroundImageWithURL:[NSURL URLWithString:imageURL] forState:UIControlStateNormal];

            [cell addSubview:detailTitle];
            [cell addSubview:title];
            [cell addSubview:keyImageButton];

            NSLog(@"request name %@", request.productName);
            return cell;
        break;}
vinylDeveloper
  • 687
  • 2
  • 7
  • 25
  • 1
    Please post your exact error. – Lyndsey Scott Jan 03 '15 at 04:52
  • -[UITableViewCell requestTitle]: unrecognized selector sent to instance 0x13fe17b20 `2015-01-02 23:28:49.982 Redrum[2729:1003782] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCell requestTitle]: unrecognized selector sent to instance 0x13fe17b20'` – vinylDeveloper Jan 03 '15 at 04:54
  • I think you're dequeuing a UITableViewCell (without a requestTitle property) instead of a RequestCell for some reason. Happen to have a `registerClass` line in your code? – Lyndsey Scott Jan 03 '15 at 04:56
  • Do I need to set up a Xib file to go with my `RequestCell` I have a prototype cell @"requestCell" in my storyboard – vinylDeveloper Jan 03 '15 at 05:00
  • You probably didn't change the class of your cell in the storyboard to a RequestCell. You don't need a xib. All of the code you have in the cell's .m file should be deleted. Some is unnecessary and some of it is wrong. – rdelmar Jan 03 '15 at 05:04
  • @rdelmar thank you, that fixed my error, but I'm still just getting blank cells now. where should I be init my labels? – vinylDeveloper Jan 03 '15 at 05:11
  • see the edit to my answer, you're combining 2 different methods of dequeuing cells, you need to pick 1 or the other – Mike S Jan 03 '15 at 05:12
  • If you're making your cell in the storyboard, why don't you add your labels there, and make IBOutlets to them. If you're not doing that, then what's the purpose of making the cell in the storyboard in the first place? – rdelmar Jan 03 '15 at 05:13
  • @rdelmar I guess I could just do that. I haven't been doing much iOS, and trying to wrap my head around auto layout and the size classes for a universal app is a little scary.. It seems easier to me to make my labels in code.. – vinylDeveloper Jan 03 '15 at 05:29
  • If you're going to make your labels in code, then you actually have to create them, give them proper frames, and add them to the cell's contentView. Are you doing any of that? – rdelmar Jan 03 '15 at 05:30
  • Yes, and I haven't had any issues, but my cells were drawing over themselves because they just kept creating new labels everytime I scrolled. That's why I did a subclass for my cell. – vinylDeveloper Jan 03 '15 at 05:39
  • I don't see where you're adding the labels to your cell. – rdelmar Jan 03 '15 at 05:42
  • check out my original code I added, this is worked, but just kept drawing over top of the cells on scroll... how do I add the labels to the cell from my subclass? – vinylDeveloper Jan 03 '15 at 06:00
  • 1
    Of course it does, because you're adding a new label every time a cell is reused. Override initWithCoder in your cell's class, and create your labels, and add them to the contentView there. Delete all the other code you have in there now. – rdelmar Jan 03 '15 at 06:03
  • @rdelmar ..initWithCoder.. works like a charm. Thank you!!! if you make an answer, I will definitely accept it. – vinylDeveloper Jan 03 '15 at 06:22

2 Answers2

3

If your cell is made in a storyboard, then initWithCoder: is the init method called, not initWithStyle:reuseIdentifier:. So, override that, and create your labels, and add them to the contentView there. Also, there no reason for an if cell == nil clause in your cellForRowAtIndexPath method, since your dequeue method is guaranteed to return a cell.

Another thing you should change is this,

Request *request = [[Request alloc] init];
request = self.isNotFilledList[indexPath.row];

You instantiate a Request object in that first line, but then throw that away by redefining it as self.isNotFilledList[indexPath.row] in the next line. You should just have,

Request *request = self.isNotFilledList[indexPath.row];
rdelmar
  • 103,982
  • 12
  • 207
  • 218
0

You don't need to manually instantiate the Cell instance variables. Try removing the extra functions

-(UILabel *)requestTitle
{
    if(!_requestTitle)
    {
        _requestTitle = [[UILabel alloc] init];
    }
    return _requestTitle;
}

-(UILabel *)detailTitle
{
    if(!_detailTitle)
    {
        _detailTitle = [[UILabel alloc] init];
    }
    return _detailTitle;
}


-(UIButton *)requestImageButton
{
    if(!_requestImageButton)
    {
        _requestImageButton = [[UIButton alloc] init];
    }
    return _requestImageButton;
}

in favor of this

@implementation RequestCell
@synethesize requestTitle;
@synthesize detailTitle;
@synthesize requestImageButton;

EDIT:

Ah I think I see the problem now. You are using dequeueReusableCellWithIdentifier:forIndexPath: which is new iOS 6 and designed to never return a nil cell (to prevent app crashes and developer convenience). So your if statement that follows is for the OLD dequeue method which is just dequeueReusableCellWithIdentifier

the fix should be changing this:

cell = [tableView dequeueReusableCellWithIdentifier:requestCell forIndexPath:indexPath];

to this:

cell = [tableView dequeueReusableCellWithIdentifier:requestCell];

more details can be found here

Community
  • 1
  • 1
Mike S
  • 11,329
  • 6
  • 41
  • 76
  • hmmm. it gives me the same issue – vinylDeveloper Jan 03 '15 at 05:03
  • Using the newer dequeue method isn't a problem. The if cell == nil clause is useless, but it's not hurting anything. – rdelmar Jan 03 '15 at 05:15
  • it is harmful because it makes the code SEEM like it's instantiating a RequestCell when actually iOS is returning an instance of UITableViewCell, that method that is dequeuing the cell will never return a cell of type RequestCell – Mike S Jan 03 '15 at 05:16
  • 2
    That may be a perception problem, but the real problem is that he probably didn't change the class of the cell in the storyboard to his custom class. Using the old dequeue method won't fix that. – rdelmar Jan 03 '15 at 05:19
  • That is correct. I forgot to add my class in my storyboard.. changing this still gives me blank cells – vinylDeveloper Jan 03 '15 at 05:22
  • As @rdelmar said, I would add your labels and image as IBOutlets in the storyboard – Mike S Jan 03 '15 at 05:25