我有UITableView,附带NSFetchedResultsController。 FRC的委托方法工作得很好,一切都很好,但我的单元格具有依赖于单元格位置的自定义背景: 1.在UITableView的顶部(顶部有圆角的背景) 2.中间(背景没有圆角) 3.在底部(底部有圆角的背景) 4.单细胞(所有角都是圆形的)。 在我的单元格配置方法中,我计算单元格的位置,并为其设置适当的背景视图。一切正常,但FRC的委托方法的实现存在问题:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.agenciesTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
{
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
break;
}
case NSFetchedResultsChangeDelete:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
break;
}
case NSFetchedResultsChangeUpdate:
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeMove:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
};
}
正如您所看到的那样,它很常见,但有一个问题: 如果单元格被插入到UITableView的顶部,它的配置方法(cellForRowAtIndexPath)被调用并且它正确绘制(带有圆角顶角)但是它下面的单元格(之前位于顶部)仍然具有圆角顶角和我需要以某种方式重绘它。
- = = EDITED -
我更改了FRC的委托方法以反映更新逻辑:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.agenciesTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
{
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
//if we inserting a cell not to the middle of table view
//we need to update the previous cell (if we are inserting to the end)
// or the next cell (if we are inserting to the beggining)
if ([self controller:controller positionForCellAtIndexPath:newIndexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row+1) inSection:newIndexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row-1) inSection:newIndexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:nextIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
};
break;
}
case NSFetchedResultsChangeDelete:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
//if we deleting a cell not fromo the middle of table view
//we need to update the previous cell (if we are deleting from the end)
//or the next cell (if we are deleting from the beggining)
if ([self controller:controller positionForCellAtIndexPath:indexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(indexPath.row-1) inSection:indexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:nextIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
};
break;
}
case NSFetchedResultsChangeUpdate:
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
}
case NSFetchedResultsChangeMove:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
if ([self controller:controller positionForCellAtIndexPath:indexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(indexPath.row-1) inSection:indexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:nextIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
};
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
if ([self controller:controller positionForCellAtIndexPath:newIndexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row+1) inSection:newIndexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row-1) inSection:newIndexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:prevIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:nextIndexPath] withRowAnimation:UITableViewRowAnimationNone];
};
};
break;
}
};
}
但在这种情况下,我收到了一个错误:
2013-05-24 21:04:19.458 PharmaTouch[6994:fb03] *** Assertion failure in -[_UITableViewUpdateSupport _computeRowUpdates], /SourceCache/UIKit_Sim/UIKit- 1912.3/UITableViewSupport.m:386
2013-05-24 21:04:19.483 PharmaTouch[6994:fb03] CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid table view update. The application has requested an update to the table view that is inconsistent with the state provided by the data source. with userInfo (null)
- = EDITED2 = - 这是我的控制器:cellExistsAtIndexPath:方法实现。它用于安全检查,以避免使用超出范围的indexPath参数进行reloadRowsAtIndexPaths调用。
-(BOOL)controller:(NSFetchedResultsController *)controller cellExistsAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row<0)
{
return NO;
};
NSInteger numberOfRows = 0;
NSArray *sections = controller.sections;
if(sections.count > 0)
{
id <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:indexPath.section];
numberOfRows = [sectionInfo numberOfObjects];
};
if (indexPath.row>(numberOfRows-1))
{
return NO;
}
else
{
return YES;
};
}
答案 0 :(得分:2)
当您使用UITableViewRowAnimationNone
时,您可能只是reloadData
,然后它将正确完成,因为所有行都将被刷新。
您使用UITableViewRowAnimationFade
的位置,请考虑您对它的重视程度......
您需要在检查和重新加载时更全面一些。特别是,您需要从FRC获取有关正在更改的部分的部分信息。如果传入或传出的行是第一行或最后一行,则需要刷新多行。沿着:
if first is changing and count > 1, also refresh second
if last is changing and count > 1, also refresh first
答案 1 :(得分:0)
我通过添加属性@property (nonatomic, retain) NSMutableSet *indexPathsForRowsToReloadAfterFRCUpdates;
来解决问题,以保持需要在controllerWillChangeContent:controllerDidChangeContent:方法调用之间更新的行(在FRC委托更新周期中)。
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
self.indexPathsForRowsToReloadAfterFRCUpdates=[[NSMutableSet alloc] init];
[self.agenciesTableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
UITableView *tableView = self.agenciesTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
{
[tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeDelete:
{
[tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
};
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.agenciesTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
{
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
//if we inserting a cell not to the middle of table view
//we need to update the previous cell (if we are inserting to the end)
// or the next cell (if we are inserting to the beggining)
if ([self controller:controller positionForCellAtIndexPath:newIndexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row-1) inSection:newIndexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row+1) inSection:newIndexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:prevIndexPath];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:nextIndexPath];
};
};
break;
}
case NSFetchedResultsChangeDelete:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
//if we deleting a cell not fromo the middle of table view
//we need to update the previous cell (if we are deleting from the end)
//or the next cell (if we are deleting from the beggining)
if ([self controller:controller positionForCellAtIndexPath:indexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(indexPath.row) inSection:indexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:prevIndexPath];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:nextIndexPath];
};
};
break;
}
case NSFetchedResultsChangeUpdate:
{
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
}
case NSFetchedResultsChangeMove:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationNone];
if ([self controller:controller positionForCellAtIndexPath:indexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(indexPath.row) inSection:indexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(indexPath.row+1) inSection:indexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:prevIndexPath];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:nextIndexPath];
};
};
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationNone];
if ([self controller:controller positionForCellAtIndexPath:newIndexPath]!=UITableViewCellPositionMiddle)
{
NSIndexPath *prevIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row-1) inSection:newIndexPath.section];
NSIndexPath *nextIndexPath=[NSIndexPath indexPathForRow:(newIndexPath.row+1) inSection:newIndexPath.section];
if ([self controller:controller cellExistsAtIndexPath:prevIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:prevIndexPath];
};
if ([self controller:controller cellExistsAtIndexPath:nextIndexPath])
{
[self.indexPathsForRowsToReloadAfterFRCUpdates addObject:nextIndexPath];
};
};
break;
}
};
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.agenciesTableView endUpdates];
if ([self.indexPathsForRowsToReloadAfterFRCUpdates count]>0)
{
[self.agenciesTableView reloadRowsAtIndexPaths:[[self.indexPathsForRowsToReloadAfterFRCUpdates allObjects] copy] withRowAnimation:UITableViewRowAnimationNone];
};
self.indexPathsForRowsToReloadAfterFRCUpdates=nil;
}