Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues using NSIndexPath as key in NSMutableDictionary?

Is there any particular reason why attempting to store and retrieve a value in an NSMutableDictionary using an NSIndexPath as a key might fail?

I originally attempted to do this in order to store an NSMutableDictionary of UITableViewCell heights (self.cellHeights) for a UITableView. Each time you tapped a UITableViewCell, that cell would either expand or contract between two different heights based on the value stored in the NSMutableDictionary for that particular indexPath:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    NSNumber *heightNSNumber = [self.cellHeights objectForKey:indexPath];
    if (!heightNSNumber)
    {
        heightNSNumber = [NSNumber numberWithFloat:100.0];
        [self.cellHeights setObject:heightNSNumber forKey:indexPath];
    }
    return [heightNSNumber floatValue];
}

- (void)tableView:(UITableView *)tableView  
        didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSNumber *heightNSNumber = [self.cellHeights objectForKey:indexPath];
    if (!heightNSNumber)
    {
        heightNSNumber = [NSNumber numberWithFloat:100.0];
        [self.cellHeights setObject:heightNSNumber forKey:indexPath];
    }

    if ([heightNSNumber floatValue] == 100.0)
    {
        [self.cellHeights setObject:[NSNumber numberWithFloat:50.0] 
                             forKey:indexPath];
    } else {
        [self.cellHeights setObject:[NSNumber numberWithFloat:100.0] 
                             forKey:indexPath];
    }
    [tableView beginUpdates];
    [tableView endUpdates];
}

For reasons unknown to me, getting the cell height within tableView:didSelectRowAtIndexPath: via [self.cellHeights objectForKey:indexPath] works just fine. However, trying to get the cell height within tableView:heightForRowAtIndexPath: via [self.cellHeights objectForKey:indexPath] always returns nil because it seems that the indexPath used to store the height doesn't match the indexPath being used to fetch the cell height, even though they have the same values for indexPath.section and indexPath.row. Because of this, a new object for the "same" index path is added to self.cellHeights (as evident since self.cellHeights.count increases thereafter).

This does not happen when you store the cell heights in the NSMutableDictionary using the row ([NSNumber numberWithInteger:indexPath.row]) as the key...so that's what I'm doing for now, but I'd like to understand why indexPath isn't working as the key.

like image 228
Ken M. Haggerty Avatar asked Oct 27 '13 02:10

Ken M. Haggerty


Video Answer


2 Answers

Although I'm late in the discussion, here's a quick and simple solution that will allow you to use NSIndexPath instances as dictionary keys.

Just recreate the indexPath by adding the following line:

indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];

Voilà. tableView:heightForRowAtIndexPath: uses NSMutableIndexPath instances internally (as you would see with a breakpoint). Somehow those instances seem uncooperative with NSIndexPath when calculating hash keys.

By converting it back to an NSIndexPath, then everything works.

like image 140
Jean Le Moignan Avatar answered Oct 13 '22 01:10

Jean Le Moignan


@Jean's answer seems acceptable, but this question has been answered in more detail here. In short, UITableView sometimes uses instances of NSMutableIndexPath instead of NSIndexPath and instances of these two classes are never equal because [NSMutableIndexPath class] != [NSIndexPath class]. The workaround is to always generate a key NSIndexPath for anything that relies on isEqual or hash, such as looking up dictionary keys:

- (NSIndexPath *)keyForIndexPath:(NSIndexPath *)indexPath
{
    if ([indexPath class] == [NSIndexPath class]) {
        return indexPath;
    }
    return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
}
like image 35
Timothy Moose Avatar answered Oct 13 '22 01:10

Timothy Moose