2009-06-29 16 views
37

अद्यतन के साथ वर्गोंUITableView: को हटाने एनीमेशन

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


मूल प्रश्न मैं पहले इतने पर एक प्रश्न पूछा है कि मैं अपने मुद्दों को हल कर सोचा:

How to deal with non-visible rows during row deletion. (UITableViews)

हालांकि, मैं अब इसी तरह की समस्याओं फिर से जब एक UITableView से वर्गों को हटाने की है। (जब मैंने तालिका में अनुभागों/पंक्तियों की संख्या को अलग किया तो वे पुन: जीवित हुए)।

मेरी पोस्ट की कतरनी लंबाई के कारण मैं आपको खोने से पहले, मुझे समस्या स्पष्ट रूप से बताएं, और आप जितना अधिक जवाब देने के लिए आवश्यक हो उतना पढ़ सकते हैं।


समस्या:

बैच एक UITableView, अनुप्रयोग क्रैश से पंक्तियों और वर्गों को हटाने, तो कभी कभी। यह तालिका की कॉन्फ़िगरेशन और पंक्तियों और खंडों के संयोजन पर निर्भर करता है जिन्हें मैं निकालना चुनता हूं। अब जल्दी से

Invalid update: invalid number of rows in section 5. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted). 

, इससे पहले कि आप स्पष्ट जवाब लिखते हैं, मैं आपको विश्वास दिलाता मैं वास्तव में जोड़ लिया है और नष्ट कर दिया:

लॉग मैं दुर्घटनाग्रस्त हो गया है, क्योंकि यह कहता है कि मैं डेटा स्रोत और ठीक से तालिका अद्यतन नहीं किया है कहते हैं डेटा स्रोत से ठीक से पंक्तियां और खंड। स्पष्टीकरण लंबा है, लेकिन विधि के बाद, आप इसे नीचे पाएंगे।

उस के साथ तो, अगर आप अभी भी रुचि रखते हैं ...


विधि कि वर्गों और पंक्तियों को हटाने के हैंडल:

- (void)createFilteredTableGroups{ 

    //index set to hold sections to remove for deletion animation 
    NSMutableIndexSet *sectionsToDelete = [NSMutableIndexSet indexSet]; 
    [sectionsToDelete removeIndex:0]; 


    //array to track cells for deletion animation 
    NSMutableArray *cellsToDelete = [NSMutableArray array]; 

    //array to track controllers to delete from presentation model 
    NSMutableArray *controllersToDelete = [NSMutableArray array]; 

    //for each section 
    for(NSUInteger i=0; i<[tableGroups count];i++){ 

     NSMutableArray *section = [tableGroups objectAtIndex:i]; 

     //controllers to remove 
     NSMutableIndexSet *controllersToDeleteInCurrentSection = [NSMutableIndexSet indexSet]; 
     [controllersToDeleteInCurrentSection removeIndex:0]; 
     NSUInteger indexOfController = 0; 

     //for each cell controller 
     for(ScheduleCellController *cellController in section){ 

      //bool indicating whether the cell controller's cell should be removed 
      NSString *shouldDisplayString = (NSString*)[[cellController model] objectForKey:@"filteredDataSet"]; 
      BOOL shouldDisplay = [shouldDisplayString boolValue]; 

      //if it should be removed 
      if(!shouldDisplay){ 

       NSIndexPath *cellPath = [self indexPathOfCellWithCellController:cellController]; 

       //if cell is on screen, mark for animated deletion 
       if(cellPath!=nil) 
        [cellsToDelete addObject:cellPath]; 

       //marking controller for deleting from presentation model 
       [controllersToDeleteInCurrentSection addIndex:indexOfController];     

      } 
      indexOfController++; 
     } 

     //if removing all items in section, add section to removed in animation 
     if([controllersToDeleteInCurrentSection count]==[section count]) 
      [sectionsToDelete addIndex:i]; 

     [controllersToDelete addObject:controllersToDeleteInCurrentSection]; 

    } 


    //copy the unfiltered data so we can remove the data that we want to filter out 
    NSMutableArray *newHeaders = [tableHeaders mutableCopy]; 
    NSMutableArray *newTableGroups = [[allTableGroups mutableCopy] autorelease]; 


    //removing controllers 
    int i = 0; 
    for(NSMutableArray *section in newTableGroups){ 
     NSIndexSet *indexesToDelete = [controllersToDelete objectAtIndex:i]; 
     [section removeObjectsAtIndexes:indexesToDelete]; 
     i++; 
    } 

    //removing empty sections and cooresponding headers 
    [newHeaders removeObjectsAtIndexes:sectionsToDelete]; 
    [newTableGroups removeObjectsAtIndexes:sectionsToDelete]; 

    //update headers 
    [tableHeaders release]; 
    tableHeaders = newHeaders; 

    //storing filtered table groups 
    self.filteredTableGroups = newTableGroups; 


    //filtering animation and presentation model update 
    [self.tableView beginUpdates]; 
    tableGroups = self.filteredTableGroups; 
    [self.tableView deleteSections:sectionsToDelete withRowAnimation:UITableViewRowAnimationTop]; 
    [self.tableView deleteRowsAtIndexPaths:cellsToDelete withRowAnimation:UITableViewRowAnimationTop]; 
    [self.tableView endUpdates]; 


    //marking table as filtered 
    self.tableIsFiltered = YES; 


} 

मेरा अनुमान है:

probl एम यह प्रतीत होता है: यदि आप ऊपर देखते हैं जहां मैं प्रत्येक सेक्शन में सेल्स की संख्या सूचीबद्ध करता हूं, तो आप देखेंगे कि सेक्शन 5 1 से बढ़ता प्रतीत होता है। हालांकि, यह सच नहीं है। मूल धारा 5 वास्तव में हटा दिया गया है और एक और खंड ने अपना स्थान लिया है (विशेष रूप से, यह पुराना खंड 10 है)।

तो टेबल दृश्य को यह क्यों नहीं लगता है? यह जानना चाहिए कि मैंने पुराना खंड और हटा दिया है, जो पुराने खंड की अनुक्रमणिका में स्थित एक नए खंड की अपेक्षा नहीं करनी चाहिए, जो हटाए गए अनुभाग की पंक्तियों की संख्या से बंधे रहें।

उम्मीद है कि यह समझ में आता है, यह लिखने के लिए थोड़ा जटिल है।

(नोट करें कि इस कोड ने पंक्तियों/वर्गों की एक अलग संख्या के साथ पहले काम किया था।यह विशेष कॉन्फ़िगरेशन इसे समस्याएं देता है)

उत्तर

87

मैंने पहले इस समस्या में भाग लिया है। आप एक सेक्शन के सभी पंक्तियों को हटाने की कोशिश कर रहे हैं और फिर, इसके अलावा, अब खाली अनुभाग। हालांकि, यह केवल उस खंड को हटाने के लिए पर्याप्त (और उचित) है। इसके भीतर की सभी पंक्तियों को भी हटा दिया जाएगा। यहां मेरी परियोजना से कुछ नमूना कोड है जो एक पंक्ति को हटाने को संभालता है।

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if (editingStyle == UITableViewCellEditingStyleDelete) 
    { 
     // modelForSection is a custom model object that holds items for this section. 
     [modelForSection removeItem:[self itemForRowAtIndexPath:indexPath]]; 

     [tableView beginUpdates]; 

     // Either delete some rows within a section (leaving at least one) or the entire section. 
     if ([modelForSection.items count] > 0) 
     { 
      // Section is not yet empty, so delete only the current row. 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
          withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     else 
     { 
      // Section is now completely empty, so delete the entire section. 
      [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] 
        withRowAnimation:UITableViewRowAnimationFade]; 
     } 

     [tableView endUpdates]; 
    } 
} 
4

मुझे लगता है कि आप पहले तालिका से अनुभागों को हटा रहे हैं, और फिर पंक्तियों को हटा रहे हैं।

मुझे पता है कि तालिका दृश्य प्रोग्रामिंग मार्गदर्शिका में UITableViews के लिए complicated discussion of batch insertion and deletion है, लेकिन यह विशेष रूप से इसे कवर नहीं करता है।

मुझे लगता है कि क्या हो रहा है यह है कि अनुभागों को हटाने से पंक्ति पंक्तियों को गलत पंक्ति का संदर्भ मिल रहा है।

यानी आप अनुभाग # 2 से अनुभाग # 2 और पंक्ति # 1 को हटाना चाहते हैं ... लेकिन अनुभाग # 2 को हटाने के बाद, पुराना खंड # 4 अब तीसरा खंड है, इसलिए जब आप साथ हटाते हैं पुराना एनएसआईएनएक्सएक्सपाथ (4, 1) आप कुछ यादृच्छिक अलग पंक्ति को हटा रहे हैं जो मौजूद नहीं हो सकता है।

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

+0

वैकल्पिक रूप से, प्रत्येक सेल के लिए इंडेक्सपैथ को ट्रैक करें जिसे आप छुटकारा पाने के लिए जरूरी हैं, और जब आप अपने हटाना चाहते हैं तो उन्हें उचित रूप से समायोजित करें। (यह करने के लिए यह लंबा/गठबंधन/अनुचित तरीका हो सकता है - बस एक विचार।) – Tim

+0

मैं बैच हटा रहा हूं, इसलिए यह उस क्रम में कोई फर्क नहीं पड़ता जिसके क्रम में मैं संचालन करता हूं। जब तालिका अद्यतन ब्लॉक में होती है तो तालिका दृश्य "एक बार में" संचालन करता है। जैसे-जैसे मैं पागल था, मैंने ऑपरेशन के आदेश को बदलने का प्रयास नहीं किया। बैच हटाने के दौरान खंड/पंक्तियों की संख्या स्विच नहीं होनी चाहिए (स्विच नहीं करना चाहिए)। यदि ब्लॉक का उपयोग नहीं कर रहा था, तो आप सही होंगे। –

+0

@Tim दिलचस्प विचार। आप सही हैं, जो बड़ी मात्रा में हटाने (जो मेरे पास होगा) के साथ काफी कठिन हो सकता है। मुझे यह भी आश्चर्य है कि क्या मैं जल्दी उत्तराधिकार में कई विलोपन कर सकता हूं। मैं इन मुद्दों से बचने के लिए बैच हटाने की कोशिश कर रहा था, लेकिन यह आवश्यक हो सकता है। –

3

तो अंत में यहाँ इस मुद्दे पर मेरे समाधान है: यह निर्धारित करने के लिए कि क्या यह एक अनुभाग से केवल यह पंक्ति निकालना या पूरे हिस्से को हटाना चाहिए, अगर यह उस अनुभाग में अंतिम शेष पंक्ति है की जरूरत है। इस विधि को किसी भी आकार की तालिकाओं पर लागू किया जा सकता है, जहां तक ​​मैं कह सकता हूं)

जैसा कि मैंने मैट गैलाघर के टेबलव्यू कोड को संशोधित किया है, जो एक अलग सेल नियंत्रक में सेल-विशिष्ट तर्क को स्थानांतरित करता है।

NSArray *allTableGroups; //always has a copy of every cell controller, even if filtered 
NSArray *filteredTableGroups; //always has a copy of the filtered table groups 

मैट मूल इवर:

NSArray *allTableGroups 

... हमेशा अंक हालांकि, आप आसानी से एक अलग मॉडल

मैं मैट कोड के लिए निम्न (प्रासंगिक) Ivars जोड़ लिया है के लिए इस विधि अनुकूलित कर सकते हैं उपरोक्त सरणी में से एक के लिए।

इसे संभवतः दोबारा सुधार और सुधार किया जा सकता है, लेकिन मुझे इसकी आवश्यकता नहीं है। साथ ही, यदि आप कोर डेटा का उपयोग करते हैं, तो NSFetchedResultsController यह आसान बनाता है।

अब पर विधि (मैं के रूप में मैं यह कर सकते हैं के रूप में ज्यादा टिप्पणी करने के लिए कोशिश कर रहा हूँ) के लिए:

- (void)createFilteredTableGroups{ 

    //Checking for the usual suspects. all which may through an exception 
    if(model==nil) 
     return; 
    if(tableGroups==nil) 
     return; 
    if([tableGroups count]==0) 
     return; 


    //lets make a new array to work with 
    NSMutableArray *newTableGroups = [[allTableGroups mutableCopy] autorelease]; 

    //telling the table what we are about to do 
    [self.tableView beginUpdates]; 


    //array to track cells for deletion animation 
    NSMutableArray *indexesToRemove = [NSMutableArray array]; 

    //loop through each section 
    for(NSMutableArray *eachSection in tableGroups){ 

     //keeping track of the indexes to delete for each section 
     NSMutableIndexSet *indexesForSection = [NSMutableIndexSet indexSet]; 
     [indexesForSection removeAllIndexes]; 

     //increment though cell indexes 
     int rowIndex = 0; 

     //loop through each cellController in the section 
     for(ScheduleCellController *eachCellController in eachSection){ 

      //Ah ha! A little magic. the cell controller must know if it should be displayed. 
      //This you must calculate in your business logic 
      if(![eachCellController shouldDisplay]){ 

       //add non-displayed cell indexes 
       [indexesForSection addIndex:rowIndex]; 

      } 
      rowIndex++; 
     } 
     //adding each array of section indexes, EVEN if it is empty (no indexes to delete) 
     [indexesToRemove addObject:indexesForSection]; 

    } 

    //Now we remove cell controllers in newTableGroups and cells from the table 
    //Also, each subarray of newTableGroups is mutable as well 
    if([indexesToRemove count]>0){ 

     int sectionIndex = 0; 
     for(NSMutableIndexSet *eachSectionIndexes in indexesToRemove){ 

      //Now you know why we stuck the indexes into individual arrays, easy array method 
      [[newTableGroups objectAtIndex:sectionIndex] removeObjectsAtIndexes:eachSectionIndexes]; 

      //tracking which cell indexPaths to remove for each section 
      NSMutableArray *indexPathsToRemove = [NSMutableArray array]; 
      int numberOfIndexes = [eachSectionIndexes count]; 

      //create array of indexPaths to remove 
      NSUInteger index = [eachSectionIndexes firstIndex]; 
      for(int i = 0; i< numberOfIndexes; i++){ 

       NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:sectionIndex]; 
       [indexPathsToRemove addObject:indexPath]; 
       index = [eachSectionIndexes indexGreaterThanIndex:index]; 
      } 

      //delete the rows for this section 
      [self.tableView deleteRowsAtIndexPaths:indexPathsToRemove withRowAnimation:UITableViewRowAnimationTop]; 

      //next section please 
      sectionIndex++; 
     } 

    } 

    //now we figure out if we need to remove any sections 
    NSMutableIndexSet *sectionsToRemove = [NSMutableIndexSet indexSet]; 
    [sectionsToRemove removeAllIndexes]; 

    int sectionsIndex = 0; 
    for(NSArray *eachSection in newTableGroups){ 

     //checking for empty sections 
     if([eachSection count]==0) 
      [sectionsToRemove addIndex:sectionsIndex]; 

     sectionsIndex++; 
    } 

    //updating the table groups 
    [newTableGroups removeObjectsAtIndexes:sectionsToRemove]; 

    //removing the empty sections 
    [self.tableView deleteSections:sectionsToRemove withRowAnimation:UITableViewRowAnimationTop]; 

    //updating filteredTableGroups to the newTableGroups we just created 
    self.filteredTableGroups = newTableGroups; 

    //pointing tableGroups at the filteredGroups 
    tableGroups = filteredTableGroups; 

    //invokes the animation 
    [self.tableView endUpdates]; 


} 
1

मैं समय से पहले ही अपने कस्टम tableview सेल की पृष्ठभूमि दृश्य को रिहा करने का परिणाम के रूप में इस एक ही सटीक त्रुटि को देखा।

NSZombieEnabled के साथ मुझे पुन: उपयोग के लिए सेल तैयार करने के लिए एक फ़ंक्शन में आंतरिक कॉल के नीचे एक अपवाद डाला जा रहा था। NSZombieEnabled के बिना, मुझे आंतरिक स्थिरता त्रुटि मिल रही थी।

संयोग से जब मैंने सेल के पृष्ठभूमि दृश्य पर रखरखाव जारी/जारी किया, तो मैं खंड को अंतिम रूप से हटाए बिना अनुभाग की अंतिम पंक्ति को हटाने में सक्षम था।

कहानी का नैतिक: यह त्रुटि केवल इसका मतलब है कि जब आप हटाने की कोशिश करते हैं तो कुछ बुरा होता है, और जब आप हटाते हैं तो चीजों में से एक यह है कि सेल पुन: उपयोग के लिए तैयार हो जाता है, इसलिए यदि आप अपने साथ कुछ भी कर रहे हैं टेबलव्यू सेल, वहां एक संभावित त्रुटि की तलाश है।

0

या सिर्फ फ़ोल्डर के इस

- (void)tableView:(UITableView *)tv  
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle 
forRowAtIndexPath:(NSIndexPath *)indexPath { 

if(editingStyle == UITableViewCellEditingStyleDelete) {  
    //Delete the object from the table. 
    [directoriesOfFolder removeObjectAtIndex:indexPath.row]; 
    [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
withRowAnimation:UITableViewRowAnimationFade]; 
} 
} 

निर्देशिका अपने सरणी जा रहा है! Thats सभी उपरोक्त कोड मेरे लिए काम नहीं किया है! यह करने के लिए कम महंगा है और बस समझ में आता है!

2

मुझे संदेह है कि आप अपने आंतरिक भंडारण से अनुभाग का प्रतिनिधित्व करने वाली वस्तु को हटाने के लिए भूल रहे हैं, ताकि सभी अनुभागों को हटा दिए जाने के बाद -numberOfSectionsInTableView: विधि अभी भी 1 लौट रही हो।

वही है जब मैं एक ही दुर्घटनाग्रस्त था तो मैं गलत कर रहा था!

1

एक बहुत सरल यह पता करने के लिए जिस तरह से अपने डेटा स्रोत अद्यतन करने के लिए, तो reloadSections

[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade]; 

यह फोन एक एकल खंड को फिर से लोड होगा। वैकल्पिक रूप से आप कई अनुभागों को एक साथ फिर से लोड करने के लिए indexSetWithIndexesInRange: का उपयोग कर सकते हैं।

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