2009-10-30 60 views
9

我在使用UITableView時讓我的核心數據實體玩起來很不錯並且有點麻煩。如何使用核心數據維護UITableView中的顯示順序?

我已經在StackOverflow上通過了很多教程和其他問題,但似乎沒有一個清晰或優雅的方式來做到這一點 - 我真的希望我失去了一些東西。

我有一個單一的Core Data實體,它上面有一個int16屬性,叫做「displayOrder」。我使用已在「displayOrder」上排序的NSFetchRequest來返回我的UITableView的數據。除了重新排序之外,所有東西都受到尊重。這裏是我的(低效率)moveRowAtIndePath方法:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {   

    NSUInteger fromIndex = fromIndexPath.row; 
    NSUInteger toIndex = toIndexPath.row; 

    FFObject *affectedObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:fromIndex]; 
    affectedObject.displayOrderValue = toIndex; 

    [self FF_fetchResults]; 


    for (NSUInteger i = 0; i < [self.fetchedResultsController.fetchedObjects count]; i++) { 
     FFObject *otherObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:i]; 
     NSLog(@"Updated %@/%@ from %i to %i", otherObject.name, otherObject.state, otherObject.displayOrderValue, i); 
     otherObject.displayOrderValue = i; 
    } 

    [self FF_fetchResults]; 
} 

任何人都可以指出我的示例代碼好位的方向,或者看到什麼我做錯了嗎? tableview顯示更新OK,我可以看到我的日誌消息displayOrder屬性正在更新。這只是不一致的保存和重新加載,並且對於這個實現感覺非常「不合適」(除了所有FFObjects的浪費迭代之外)。

預先感謝您提供任何建議。

回答

27

我在你的代碼接過來一看,這可能工作得更好:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {   

    NSUInteger fromIndex = fromIndexPath.row; 
    NSUInteger toIndex = toIndexPath.row; 

    if (fromIndex == toIndex) { 
     return; 
    } 

    FFObject *affectedObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:fromIndex]; 
    affectedObject.displayOrderValue = toIndex; 

    NSUInteger start, end; 
    int delta; 

    if (fromIndex < toIndex) { 
     // move was down, need to shift up 
     delta = -1; 
     start = fromIndex + 1; 
     end = toIndex; 
    } else { // fromIndex > toIndex 
     // move was up, need to shift down 
     delta = 1; 
     start = toIndex; 
     end = fromIndex - 1; 
    } 

    for (NSUInteger i = start; i <= end; i++) { 
     FFObject *otherObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:i]; 
     NSLog(@"Updated %@/%@ from %i to %i", otherObject.name, otherObject.state, otherObject.displayOrderValue, otherObject.displayOrderValue + delta); 
     otherObject.displayOrderValue += delta; 
    } 

    [self FF_fetchResults]; 
} 
+0

輝煌!這工作完美。我非常盯着原始代碼太久 - 謝謝你的幫助。 – 2009-11-02 09:17:32

+4

這應該並行執行與http://stackoverflow.com/questions/1077568/how-to-implement-re-ordering-of-coredata-records/2013070#2013070 – 2010-01-28 20:09:47

+0

如果項目可以被刪除,這不工作。在刪除行時,您還必須確保更新訂單字段。 – Kamchatka 2010-06-13 22:30:22

2

(這是爲了爲作爲上述gerry3的回答發表評論,但我現在還無法對其他用戶的問題發表評論,答案)

gerry3的一個小改進 - 非常優雅 - 解決方案。如果我沒有記錯的話,行

otherObject.displayOrderValue += delta; 

將實際執行指針算術如果displayOrderValue是原始類型的不行。這可能不是你想要的。相反,設置實體的價值,我建議:

otherObject.displayOrderValue = [NSNumber numberWithInt:[otherObject.displayOrderValue intValue] + delta]; 

這應該正確更新您的實體屬性,並避免任何EXC_BAD_ACCESS崩潰。

+1

displayOrder是一個核心數據實體屬性(因此,的NSNumber) - 我使用mogenerator,其自動創建由「值」後綴原始值的存取,所以在這種情況下,指針運算是完全沒有問題。 感謝,雖然指出了陷阱;) – 2010-02-18 11:34:11

+1

NP和...感謝您指出我朝着mogenerator! – octy 2010-02-18 14:26:14

0

我認爲:

affectedObject.displayOrderValue = toIndex; 

必須放置後:

for (NSUInteger i = start; i <= end; i++) { 
    FFObject *otherObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:i]; 
    NSLog(@"Updated %@/%@ from %i to %i", otherObject.name, otherObject.state, otherObject.displayOrderValue, otherObject.displayOrderValue + delta); 
    otherObject.displayOrderValue += delta; 
} 

之前:

[self FF_fetchResults]; 
1

這裏完整的解決方案如何與核心數據管理的索引表。您的屬性被稱爲displayOrder,我稱之爲index。首先,你最好分開視圖控制器和模型。爲此,我使用模型控制器,它是視圖和模型之間的接口。

有3種情況需要管理,用戶可以通過視圖控制器進行影響。

  1. 添加新對象
  2. 刪除現有對象
  3. 重新排序對象。

前兩種情況添加和刪除是非常簡單的。刪除調用名爲renewObjectIndicesUpwardsFromIndex的例程以更新刪除對象後的索引。

- (void)createObjectWithTitle:(NSString*)title { 
    FFObject* object = [FFObject insertIntoContext:self.managedObjectContext]; 

    object.title = title; 
    object.index = [NSNumber numberWithInteger:[self numberTotalObjects]]; 
    [self saveContext]; 
} 

- (void)deleteObject:(FFObject*)anObject { 
    NSInteger objectIndex = [anObject.index integerValue]; 
    [anObject deleteObject]; 
    [self renewObjectIndicesUpwardsFromIndex:objectIndex]; 
    [self saveContext]; 
} 

- (void)renewObjectIndicesUpwardsFromIndex:(NSInteger)fromIndex { 
    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; 
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext]]; 

    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(index > %d)", fromIndex]; 
    [fetchRequest setPredicate:predicate]; 

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES]; 
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil]; 

    [fetchRequest setSortDescriptors:sortDescriptors]; 

    NSError* fetchError = nil; 
    NSArray* objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError]; 

    NSInteger index = fromIndex; 
    for (FFObject* object in objects) { 
    object.index = [NSNumber numberWithInteger:index]; 
    index += 1; 
    } 
    [self saveContext]; 
} 

在我來到控制器例程進行重新排序之前,這裏是視圖控制器中的部分。我使用類似於this answer的bool isModifyingOrder。請注意,視圖控制器調用控制器moveObjectOrderUpmoveObjectOrderDown中的兩個函數。根據您在表格視圖中顯示對象的方式 - 最新的第一個或最新的最後一個 - 您可以切換它們。

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { 

    isModifyingOrder = YES; 

    NSUInteger fromIndex = sourceIndexPath.row; 
    NSUInteger toIndex = destinationIndexPath.row; 

    if (fromIndex == toIndex) { 
    return; 
    } 

    FFObject *affectedObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:fromIndex]; 

    NSInteger delta; 
    if (fromIndex < toIndex) { 
    delta = toIndex - fromIndex; 
    NSLog(@"Moved down by %lu cells", delta); 
    [self.objectController moveObjectOrderUp:affectedObject by:delta]; 
    } else { 
    delta = fromIndex - toIndex; 
    NSLog(@"Moved up by %lu cells", delta); 
    [self.objectController moveObjectOrderDown:affectedObject by:delta]; 
    } 

    isModifyingOrder = NO; 
} 

而這裏的部分在控制器中。這可以寫得更好,但對於理解這可能是最好的。

​​

別忘了在您的視圖控制器使用第二BOOL對於刪除操作,防止轉移通知做任何事情。我把它叫做isDeleting並放在這裏。

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
    atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 
    newIndexPath:(NSIndexPath *)newIndexPath { 
    if (isModifyingOrder) return; 

    ... 

    switch(type) { 

    ... 

    case NSFetchedResultsChangeMove: 
     if (isDeleting == false) { 
      [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:localIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:localNewIndexPath]withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     break; 

    ... 

    } 
} 
相關問題