Dan Morgan
Dan Morgan

Reputation: 2500

Multiple UiTableViewCell subclasses in a single UiTableView? Why is this code not working

Okay so Here's my issue. I have a table, a bit like Apple's addressBook.app that uses different cell subclasses to display information in different ways. On this particular app I have 3 different UiTableViewCell subclasses, all with their own nibs.

I can't get the code right :-( if I try and access the instance variable of my cell in the CellForRowPath method it says' (ERROR request for member 'type' in something not a structure or union') Now I assume that the word 'something' is the root of my problem.. The code I am about to paste obviously doesn't setup my cell properly. Here is the code:

There are 3 if statements to get the right cell subclass depending on the section being called:

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

static NSString *PoolFacilityCellIdentifier = @"PoolFacilityCellIdentifier";
static NSString *PoolFacilityAddressCellIdentifier = @"PoolFacilityAddressCellIdentifier";
static NSString *PoolFacilityPoolCellIdentifier = @"PoolFacilityPoolCellIdentifier";
NSUInteger section = indexPath.section;

//Creat a generic container for cell which will be cast during one of the next two statements
id cell;

//Change the identifier depending on cell type. MIGHT NEED TO ADD SOMETHING ELSE FOR POOLS AND PRICES
if (section == KAddressIndex) {
    cell = (PoolFacilityAddressCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityAddressCellIdentifier];

    if (cell == nil) {

        //Load the appropriate nib for the type of cell being asked for.

            NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"PoolFacilityAddressCell" owner:self options:nil];

            for (id object in nib) {

                if ([object isMemberOfClass:[PoolFacilityAddressCell class]]) 
                    cell = (PoolFacilityAddressCell *) object;

            }
    }

}
else if (section == KPoolIndex) {
    cell = (PoolFacilityPoolCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityPoolCellIdentifier];

    if (cell == nil) {

        //Load the appropriate nib for the type of cell being asked for.

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"PoolFacilityPoolCell" owner:self options:nil];

        for (id object in nib) {

            if ([object isMemberOfClass:[PoolFacilityPoolCell class]]) 
                cell = (PoolFacilityPoolCell *) object;


        }
    }
}
else {
    cell = (PoolFacilityCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityCellIdentifier];

    if (cell == nil) {

        //Load the appropriate nib for the type of cell being asked for.

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"PoolFacilityCell" owner:self options:nil];

        for (id object in nib) {

            if ([object isMemberOfClass:[PoolFacilityCell class]]) 
                cell = (PoolFacilityCell *) object;
        }
    }

}

Any help would be greatly appreciated!!! I import all the header files for my cell subclasses.

The code that throws my error is this (specifically the cell.type.text = thePool.type) cell.type is an Iboutlet UiLabel:

if (section == KPoolIndex) {

    NSMutableArray *thePools = [allSections objectForKey:sectionAsNumber];

    if ([thePools count] > 0) {
        [cell setPromptMode:NO];
        //Set the label and Pool from the Pool Object
        Pool *thePool = [thePools objectAtIndex:row];
        cell.type.text = thePool.type;


    }

}

Thanks,

Dan

Upvotes: 4

Views: 5378

Answers (4)

benzado
benzado

Reputation: 84308

Oh my goodness this is much simpler than all of the answers are making it appear. I'm not going to discuss your overall design, just focus on why your code is not working.

This is the line that is triggering the error:

cell.type.text = thePool.type;

You can fix it by replacing that with these lines:

PoolFacilityCell* poolCell = (PoolFacilityCell *)cell;
poolCell.type.text = thePool.type;

The dot-syntax for accessing properties only works if the compiler knows what class you are working with. It can't deal with cell.type because cell is declared as type id, so at compile time it doesn't know that type is supposed to be an Objective-C property. The error message calls it "something not a structure or union" because it thinks you are treating cell like a plain C struct, which it is not, hence the error.

Incidentally, the lines where you write:

cell = (PoolFacilityAddressCell *) object;

are meaningless. Since cell is declared as id (any generic object), there is no need for the cast. Based on an earlier comment, you seem to think that type casting like that affects the cell variable somehow. It does not. The line above compiles into instructions that simply copy the memory address of the instance from object to cell. Type casting is there to avoid warnings and errors at compile time.

Upvotes: 0

Dan Morgan
Dan Morgan

Reputation: 2500

I might have a few answers to my own question:

1 - As drew has mentioned the cell remains of type ID. I thought my following if functions would typeCast the cell into being a specific cell type but that doesn't seem to be the case.

2 - A solution would be to have 3 big if statements for each type of cell. Each if statement ending in a

return cell;

call.

3 - The solution I'm going to try now is to have only one cell subclass instead of 3 and have a method in my cell subclass called

-(void)setCellBehaviour:(int)definedNumber

That way I can hide layers/labels etc and customize it how I wish.

update

I've achieved the required results like this:

I only have one cell subclass which has a single NIB with many different UIlabels.

I call this in the cellForRowAtIndexPath: method of my uiviewcontroller, once the cell has been alloc/init.

//Set the behaviour
    [cell setCellBehaviour:KCellBehaviourName];

This references the definitions I set in the header file of the view controller:

//Definitions for cell type behaviour. Passed to cell during creation.

(HASH)define KCellBehaviourStandard 0 (HASH)define KCellBehaviourName 1 (HASH)define KCellBehaviourAddress 2 (HASH)define KCellBehaviourPool 3

(Cant seem to do hashes in this post)

Then my UITableViewCellSubclass has this methos which we called earlier:

-(void)setCellBehaviour:(NSUInteger)definedBehaviour {

switch (definedBehaviour) {
    case KCellBehaviourStandard:
        self.label.hidden = NO;
        self.value.hidden = NO;
        self.length.hidden = YES;
        self.poolType.hidden = YES;
        break;
    case KCellBehaviourName:
        self.label.hidden = NO;
        self.value.hidden = NO;
        self.length.hidden = YES;
        self.poolType.hidden = NO;
        break;
    case KCellBehaviourAddress:
        self.label.hidden = NO;
        self.value.hidden = NO;
        self.length.hidden = YES;
        self.poolType.hidden = YES;
        break;
    case KCellBehaviourPool:
        self.label.hidden = NO;
        self.value.hidden = NO;
        self.length.hidden = NO;
        self.poolType.hidden = YES;
    default:
        break;
}

}

Upvotes: 1

drewh
drewh

Reputation: 10167

I believe it may be due to the fact that your cell is declared as id and you're trying to access a property off one of your cell subclasses. You can send all the messages you want to an object of type id, but trying to access properties on an object of type id will fail at compile time. Also, be sure to import the .h files containing your subclasses.

Upvotes: 1

Ryan Townshend
Ryan Townshend

Reputation: 2394

Matt Gallagher has an article that lays out a cleaner way to accomplish this type of table. He breaks out the cell specific code into their own classes. Makes it alot easier to manage.

cocoawithlove.com - Heterogeneous cells in a UITableViewController

In your code, this line looks like trouble

cell.type.text = thePool.type;

thePool is a "Pool" object, does it have a property named "type" ? Check that the @property and @synthesize are setup correctly for "type" in your "Pool" object.

Upvotes: 2

Related Questions