2009-10-30 9 views
9

मुझे UITableView का उपयोग करते समय मेरे कोर डेटा इकाइयों को अच्छा और ऑर्डर खेलने में कुछ परेशानी हो रही है।कोर डेटा का उपयोग करके UITableView में प्रदर्शन ऑर्डर कैसे बनाए रख सकता हूं?

मैं स्टैक ओवरफ्लो पर कई ट्यूटोरियल और अन्य प्रश्नों के माध्यम से रहा हूं, लेकिन ऐसा करने के लिए एक स्पष्ट या सुरुचिपूर्ण तरीका प्रतीत नहीं होता है - मुझे उम्मीद है कि मुझे कुछ याद आ रहा है।

मेरे पास एक कोर डेटा इकाई है जिसमें "displayOrder" नामक int16 विशेषता है। मैं एक NSFetchRequest का उपयोग करता हूं जिसे मेरे UITableView के लिए डेटा वापस करने के लिए "displayOrder" पर सॉर्ट किया गया है। सबकुछ लेकिन पुनर्वितरण का सम्मान किया जा रहा है।

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

किसी को भी नमूना कोड का एक अच्छा सा की दिशा में मुझे बात कर सकते हैं, या देखना मैं गलत क्या कर रही हूं: यहाँ मेरी (अक्षम) moveRowAtIndePath विधि है? टेबलव्यू डिस्प्ले अपडेट ठीक है, और मैं अपने लॉग संदेशों के माध्यम से देख सकता हूं कि displayOrder प्रॉपर्टी को अपडेट किया जा रहा है। यह लगातार बचत और पुनः लोड नहीं कर रहा है, और इस कार्यान्वयन के बारे में कुछ "बंद" लगता है (मेरे सभी एफएफओब्जेक्ट्स के अपर्याप्त पुनरावृत्ति से अलग)।

किसी भी सलाह के लिए अग्रिम धन्यवाद जो आप उधार दे सकते हैं।

उत्तर

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

शानदार! यह पूरी तरह से काम किया। मैं बहुत लंबे समय तक मूल कोड पर बहुत अधिक देख रहा था - आपकी मदद के लिए धन्यवाद। –

+4

इसे http://stackoverflow.com/questions/1077568/how-to-implement-re-ordering-of-coredata-records/2013070#2013070 –

+0

के साथ समानांतर में कार्यान्वित किया जाना चाहिए यदि आइटम हटाया जा सकता है, तो यह नहीं है काम। पंक्ति को हटाने पर आपको ऑर्डर फ़ील्ड को भी अपडेट करना सुनिश्चित करना होगा। – Kamchatka

2

(यह ऊपर gerry3 के जवाब पर टिप्पणी के रूप में के रूप में करना है, लेकिन मैं अभी तक अन्य उपयोगकर्ताओं द्वारा पूछे गये सवालों पर टिप्पणी करने में सक्षम नहीं हूँ और उत्तर।)

गेरी 3 के लिए एक छोटा सुधार - बहुत ही सुरुचिपूर्ण - समाधान। अगर मैं गलत नहीं हूँ, लाइन

otherObject.displayOrderValue += delta; 

वास्तव में प्रदर्शन करेंगे सूचक अंकगणित अगर displayOrderValue आदिम प्रकार की नहीं है। जो आप चाहते हैं वह नहीं हो सकता है। इसके बजाय, संस्था के मूल्य निर्धारित करने के लिए, मेरा प्रस्ताव है:

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

यह आपके इकाई संपत्ति सही तरीके से अपडेट करने और किसी भी EXC_BAD_ACCESS दुर्घटनाओं से बचना चाहिए।

+1

डिस्प्ले ऑर्डर एक कोर डेटा इकाई विशेषता है (इस प्रकार, एनएसएनंबर) - मैं मोगनरेटर का उपयोग कर रहा हूं, जो स्वचालित रूप से "वैल्यू" द्वारा प्रत्ययित प्राइमेटिव वैल्यू एक्सेसर्स बनाता है, इसलिए इस उदाहरण में पॉइंटर अंकगणितीय बिल्कुल ठीक है। हालांकि जाल को इंगित करने के लिए धन्यवाद;) –

+1

एनपी और ... मुझे mogenerator की तरफ इशारा करने के लिए धन्यवाद !!! – octy

0

मुझे लगता है कि:

[self FF_fetchResults]; 
1

यहाँ एक पूर्ण समाधान कैसे कोर डेटा के साथ एक अनुक्रमित तालिका प्रबंधन करने के लिए:

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

और । आपकी विशेषता 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]; 
} 

पुन: आदेश के लिए नियंत्रक दिनचर्या में आने से पहले, यहां दृश्य नियंत्रक में हिस्सा शामिल है। मैं एक बूल isModifyingOrderthis answer के समान उपयोग करता हूं। ध्यान दें कि व्यू कंट्रोलर नियंत्रक moveObjectOrderUp और moveObjectOrderDown में दो फ़ंक्शन कॉल करता है। तालिका दृश्य में ऑब्जेक्ट्स को प्रदर्शित करने के तरीके के आधार पर - नवीनतम सबसे पहले या नवीनतम अंतिम - आप उन्हें स्विच कर सकते हैं।

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

और यहां नियंत्रक में हिस्सा। यह अच्छा लिखा जा सकता है, लेकिन यह समझने के लिए शायद सबसे अच्छा है।

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

कुछ भी करने को कदम अधिसूचना को रोकने के लिए हटाएं क्रिया के लिए अपनी व्यू नियंत्रक में एक दूसरे 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; 

    ... 

    } 
} 
संबंधित मुद्दे