我在我的iPhone应用中使用了CoreData,但是CoreData没有提供一种自动的方式来允许你对记录进行重新排序。我想使用另一个列来存储订单信息,但是使用连续的数字作为排序索引有一个问题。如果我要处理大量数据,重新排序一条记录可能会涉及到更新排序信息中的许多记录(这有点像更改数组元素的顺序)
实现有效排序方案的最佳方式是什么?
发布于 2010-01-06 21:09:31
FetchedResultsController及其委托不能用于用户驱动的模型更改。参见the Apple reference doc。寻找用户驱动的更新部分。所以,如果你在寻找一些神奇的,一行的方法,遗憾的是,没有这样的方法。
您需要做的是在此方法中进行更新:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
userDrivenDataModelChange = YES;
...[UPDATE THE MODEL then SAVE CONTEXT]...
userDrivenDataModelChange = NO;
}
还可以阻止通知做任何事情,因为用户已经做了更改:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
if (userDrivenDataModelChange) return;
...
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
if (userDrivenDataModelChange) return;
...
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if (userDrivenDataModelChange) return;
...
}
我刚刚在我的待办事项应用程序(Quickie)中实现了这一点,它工作得很好。
发布于 2013-03-26 06:18:29
下面是一个快速示例,展示了将获取的结果转储到NSMutableArray中的方法,您可以使用它来移动单元格。然后,您只需更新名为orderInTable
的实体上的属性,然后保存托管对象上下文。
这样,您就不必担心手动更改索引,而是让NSMutableArray为您处理。
创建一个可以用来暂时绕过NSFetchedResultsControllerDelegate
的BOOL
@interface PlaylistViewController ()
{
BOOL changingPlaylistOrder;
}
@end
表视图委托方法:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
// Refer to https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40008228-CH1-SW14
// Bypass the delegates temporarily
changingPlaylistOrder = YES;
// Get a handle to the playlist we're moving
NSMutableArray *sortedPlaylists = [NSMutableArray arrayWithArray:[self.fetchedResultsController fetchedObjects]];
// Get a handle to the call we're moving
Playlist *playlistWeAreMoving = [sortedPlaylists objectAtIndex:sourceIndexPath.row];
// Remove the call from it's current position
[sortedPlaylists removeObjectAtIndex:sourceIndexPath.row];
// Insert it at it's new position
[sortedPlaylists insertObject:playlistWeAreMoving atIndex:destinationIndexPath.row];
// Update the order of them all according to their index in the mutable array
[sortedPlaylists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Playlist *zePlaylist = (Playlist *)obj;
zePlaylist.orderInTable = [NSNumber numberWithInt:idx];
}];
// Save the managed object context
[commonContext save];
// Allow the delegates to work now
changingPlaylistOrder = NO;
}
您的代理现在应该如下所示:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
if (changingPlaylistOrder) return;
switch(type)
{
case NSFetchedResultsChangeMove:
[self configureCell:(PlaylistCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if (changingPlaylistOrder) return;
[self.tableView reloadData];
}
发布于 2009-12-19 11:28:04
迟来的回复:也许您可以将排序关键字存储为字符串。在两个现有行之间插入记录可以通过向字符串添加额外的字符来简单地完成,例如在行"A“和"B”之间插入"AM“。不需要重新排序。一个类似的想法可以通过在4字节整数上使用浮点数或一些简单的位运算来实现:插入一个排序键值位于相邻行中间的行。
当字符串太长,浮点数太小,或者int中没有更多空间时,可能会出现病理性的情况,但随后您可以对实体重新编号并重新开始。在罕见的情况下扫描和更新所有记录要比每次用户重新排序时都让每个对象出错要好得多。
例如,考虑int32。使用最高的3个字节作为初始排序提供了近1700万行,并且能够在任意两行之间插入最多256行。2字节允许在重新扫描之前在任意两行之间插入65000行。
下面是我想要的2字节增量和2字节插入的伪代码:
AppendRow:item
item.sortKey = tail.sortKey + 0x10000
InsertRow:item betweenRow:a andNextRow:b
item.sortKey = a.sortKey + (b.sortKey - a.sortKey) >> 1
通常情况下,调用AppendRow会产生sortKeys为0x10000、0x20000、0x30000等的行。有时,您必须在第一行和第二行之间执行InsertRow操作,从而导致sortKey为0x180000。
https://stackoverflow.com/questions/1077568
复制相似问题