2009-10-30 22 views
9

Tôi đang gặp một số sự cố khi nhận các đối tượng Dữ liệu cốt lõi của mình để phát tốt và đặt hàng khi sử dụng UITableView.Tôi có thể duy trì thứ tự hiển thị trong UITableView bằng cách sử dụng Dữ liệu lõi như thế nào?

Tôi đã trải qua một số hướng dẫn và các câu hỏi khác ở đây trên StackOverflow, nhưng dường như không có cách nào rõ ràng hoặc thanh lịch để làm điều này - tôi thực sự hy vọng tôi đang thiếu thứ gì đó.

Tôi có một thực thể Dữ liệu cốt lõi duy nhất có thuộc tính int16 trên nó được gọi là "displayOrder". Tôi sử dụng một NSFetchRequest đã được sắp xếp trên "displayOrder" để trả về dữ liệu cho UITableView của tôi. Tất cả mọi thứ nhưng sắp xếp lại đang được tôn trọng. Dưới đây là (không hiệu quả) phương pháp moveRowAtIndePath tôi:

- (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]; 
} 

bất cứ ai có thể chỉ cho tôi trong sự chỉ đạo của một chút tốt về mẫu mã, hoặc thấy những gì tôi đang làm sai? Màn hình hiển thị tableview cập nhật OK, và tôi có thể thấy thông qua các thông điệp tường trình của tôi rằng thuộc tính displayOrder đang được cập nhật. Nó không chỉ liên tục tiết kiệm và tải lại, và một cái gì đó cảm thấy rất "off" về việc thực hiện này (ngoài việc lặp đi lặp lại lãng phí của tất cả các FFObjects của tôi).

Cảm ơn trước vì bất kỳ lời khuyên nào bạn có thể cho vay.

Trả lời

27

tôi đã xem xét mã của bạn và điều này có thể làm việc tốt hơn:

- (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

Tuyệt vời! Điều này làm việc hoàn hảo. Tôi rất muốn nhìn chằm chằm vào mã gốc quá lâu - cảm ơn vì sự giúp đỡ của bạn. –

+4

Điều này cần được triển khai song song với http://stackoverflow.com/questions/1077568/how-to-implement-re-ordering-of-coredata-records/2013070#2013070 –

+0

Nếu các mục có thể bị xóa, điều này không công việc. Bạn phải đảm bảo cập nhật trường đơn đặt hàng cũng khi xóa một hàng. – Kamchatka

2

(này được thiết kế như là bình luận về câu trả lời gerry3 của trên, nhưng tôi chưa thể bình luận gì về câu hỏi của người dùng khác và câu trả lời.)

Một cải tiến nhỏ cho gerry3's - rất thanh lịch - giải pháp. Nếu tôi không nhầm, dòng

otherObject.displayOrderValue += delta; 

sẽ thực sự thực hiện con trỏ số học nếu displayOrderValue không phải là kiểu nguyên thủy. Mà có thể không phải là những gì bạn muốn. Thay vào đó, để đặt giá trị của pháp nhân, tôi đề xuất:

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

Điều này sẽ cập nhật thuộc tính tổ chức của bạn một cách chính xác và tránh mọi sự cố EXC_BAD_ACCESS.

+1

displayOrder là thuộc tính thực thể Core Data (do đó, NSNumber) - Tôi đang sử dụng mogenerator, nó sẽ tự động tạo ra các giá trị truy cập nguyên thủy được kết hợp bởi "Value", vì vậy trong trường hợp này số học con trỏ là hoàn toàn tốt. Cảm ơn bạn đã chỉ ra bẫy mặc dù;) –

+1

np và ... Cảm ơn bạn đã chỉ cho tôi về phía người đồng phát! – octy

0

Tôi nghĩ rằng:

affectedObject.displayOrderValue = toIndex; 

phải được đặt sau:

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; 
} 

và trước:

[self FF_fetchResults]; 
1

Dưới đây là một giải pháp đầy đủ như thế nào để quản lý một bảng chỉ mục với dữ liệu cốt lõi . Thuộc tính của bạn được gọi là displayOrder, tôi gọi nó là index. Trước hết, bạn nên xem bộ điều khiển và mô hình riêng biệt tốt hơn. Đối với điều này tôi sử dụng một bộ điều khiển mô hình, đó là giao diện giữa khung nhìn và mô hình.

Có 3 trường hợp bạn cần quản lý để người dùng có thể ảnh hưởng thông qua bộ điều khiển chế độ xem.

  1. Thêm một đối tượng mới
  2. Xóa một đối tượng hiện
  3. đối tượng Sắp xếp lại.

Hai trường hợp đầu tiên Thêm và xóa khá đơn giản. Xóa các cuộc gọi một thói quen gọi là renewObjectIndicesUpwardsFromIndex để cập nhật các chỉ số sau khi đối tượng đã xóa.

- (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]; 
} 

Trước khi đến phần điều khiển để sắp xếp lại, ở đây trong bộ điều khiển chế độ xem. Tôi sử dụng bool isModifyingOrder tương tự như this answer. Lưu ý rằng bộ điều khiển xem gọi hai chức năng trong bộ điều khiển moveObjectOrderUpmoveObjectOrderDown. Tùy thuộc vào cách bạn hiển thị các đối tượng trong chế độ xem bảng - mới nhất trước tiên hoặc mới nhất - bạn có thể chuyển đổi chúng.

- (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; 
} 

Và đây là bộ phận của bộ điều khiển. Điều này có thể được viết đẹp hơn, nhưng đối với sự hiểu biết này có lẽ là tốt nhất.

- (void)moveObjectOrderUp:(FFObject*)affectedObject by:(NSInteger)delta { 
    NSInteger fromIndex = [affectedObject.index integerValue] - delta; 
    NSInteger toIndex = [affectedObject.index integerValue]; 

    if (fromIndex < 1) { 
    return; 
    } 

    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; 
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext]]; 

    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(index >= %d) AND (index < %d)", fromIndex, toIndex]; 
    [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]; 

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

    affectedObject.index = [NSNumber numberWithInteger:fromIndex]; 

    [self saveContext]; 
} 

- (void)moveObjectOrderDown:(FFObject*)affectedObject by:(NSInteger)delta { 
    NSInteger fromIndex = [affectedObject.index integerValue]; 
    NSInteger toIndex = [affectedObject.index integerValue] + delta; 

    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; 
    [fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext]]; 

    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(index > %d) AND (index <= %d)", fromIndex, toIndex]; 
    [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]; 

    for (FFObject* object in objects) 
    { 
    NSInteger newIndex = [object.index integerValue] - 1; 
    object.index = [NSNumber numberWithInteger:newIndex]; 
    } 

    affectedObject.index = [NSNumber numberWithInteger:toIndex]; 

    [self saveContext]; 
} 

Đừng quên sử dụng giây thứ hai BOOL trong trình điều khiển chế độ xem để xóa hành động để ngăn thông báo di chuyển làm bất cứ điều gì. Tôi gọi nó là isDeleting và đặt nó ở đây.

- (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; 

    ... 

    } 
} 
Các vấn đề liên quan