March 2011 Archive



Fixing UIAnimator removeAnimationsForTarget crash

You tested your app on the Simulator and everything is fine. You’ve moved it to the iPhone, and when you reach a certain TableViewController your app crashes with the following message: [UIAnimator removeAnimationsForTarget:]. How would you approach this issue? Using common sense, try to set a breakpoint in each function from your TableViewController, then step through them to see in/after which function does the crash happens. In my case, the responsible code was found in the function:

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

It entered this function, once for each cell, but it crashed after exiting. As a new iOS developer, I was trying to set a couple of different UITableViewCell in one TableViewController. Here is a sample of the code I was using in the first version:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // Setting a generic cell
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
                autorelease];
    }
    // ... Other code
    // Replace generic cell with custom cell
    if(objectType == CMMUItemViewSectionSummary){
        NSArray *topLevelObjects = [[NSBundle mainBundle]
            loadNibNamed:@"SummaryCell"
            owner:nil
            options:nil];
        for(id currentObject in topLevelObjects){
            if([currentObject isKindOfClass:[SummaryCell class]]){
                cell = (SummaryCell *) currentObject;
                break;
            }
        }
        cell.summaryData = summaryData;
    } else if(objectType == CMMUItemViewSectionStatuses){
            NSArray *topLevelObjects = [[NSBundle mainBundle]
                                        loadNibNamed:@"ItemStatusCell"
                                        owner:nil
                                        options:nil];
            for(id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[ItemStatusCell class]]){
                    cell = (ItemStatusCell *) currentObject;
                    break;
                }
            }
        cell.statusesData = statusesData;
    } else {
        cell.textLabel.text = someDefaultData;
    }
    // Return the cell
    return cell;
}

As you can see in the above code, after I was getting a generic cell, I would try to replace it with a custom cell. I guess that somewhere after tableView:cellForRowAtIndexPath: some other piece of code was trying to cleanup the unused cells. Figuring this, it was pretty easy to get it fixed. Using the same sample code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *CellIdentifier = @"Cell"; //Generic identifier
    UITableViewCell *cell;
    // Other code ...
    CellIdentifier = [NSString stringWithFormat:@"Cell%d", objectType]; //cell of my object type
    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(objectType == CMMUItemViewSectionSummary){
        if (cell == nil) {
            NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"SummaryCell"
                owner:nil
                options:nil];
            for(id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[SummaryCell class]]){
                    cell = (SummaryCell *) currentObject;
                    break;
                }
            }
        }
        cell.summaryData = summaryData;
    } else if(objectType == CMMUItemViewSectionStatuses){
        if (cell == nil) {
            NSArray *topLevelObjects = [[NSBundle mainBundle]
                loadNibNamed:@"ItemStatusCell"
                owner:nil
                options:nil];
            for(id currentObject in topLevelObjects){
                if([currentObject isKindOfClass:[ItemStatusCell class]]){
                    cell = (ItemStatusCell *) currentObject;
                    break;
                }
            }
        }
        cell.statusesData = statusesData;
    }
    if(cell == nil){
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                           reuseIdentifier:CellIdentifier]
                    autorelease];
    }
    return cell;
}

Even if is not the optimal way to use custom TableViewCells, it solves the crash problem. Improvements and suggestions, are welcomed.