2012-03-31 13 views
1

Ich habe eine App mit einer Tabellenansicht, die Abschnitte erweitert/reduziert. Beachten Sie dabei das Beispiel in Apples Table View Animations & Gestures sample app. Ich stoße auf Probleme, wenn ein Element zu einem geschlossenen Abschnitt hinzugefügt wird: Danach wird der Abschnitt nicht mehr geöffnet, und ich bekomme eine Ausnahme, wenn ich versuche, es zu öffnen und dann zu schließen.Zeilen, die nicht in UITable eingefügt werden

Ich habe dies zu einem seltsamen Verhalten in der Öffnungs-/Schließ Methoden verfolgt:

-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionOpened:(NSInteger)section { 

if (![[sectionHeaderArray objectAtIndex:section] isOpen]) { 

    [[sectionHeaderArray objectAtIndex:section] setIsOpen:YES]; 

    NSLog(@"self.tableView: %@", self.tableView); 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; 
    NSInteger countOfRowsToInsert = [sectionInfo numberOfObjects]; 

    NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init]; 
    for (NSInteger i = 0; i < countOfRowsToInsert; i++) { 
     [indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]]; 
    } 

    // Apply the updates. 
    [self.tableView beginUpdates]; 
    NSLog(@"Count of rows to insert: %d", [indexPathsToInsert count]); 
    NSLog(@"Rows before insert: %d", [self.tableView numberOfRowsInSection:section]); 
    [self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationTop]; 
    NSLog(@"Rows after insert: %d", [self.tableView numberOfRowsInSection:section]); 
    [self.tableView endUpdates]; 
} 

} 


-(void)sectionHeaderView:(SectionHeaderView*)sectionHeaderView sectionClosed:(NSInteger)section { 

if ([[sectionHeaderArray objectAtIndex:section] isOpen]) { 

    [[sectionHeaderArray objectAtIndex:section] setIsOpen:NO]; 

    NSInteger countOfRowsToDelete = [self.tableView numberOfRowsInSection:section]; 

    if (countOfRowsToDelete > 0) { 
     NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init]; 
     for (NSInteger i = 0; i < countOfRowsToDelete; i++) { 
      [indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:section]]; 
     } 
     [self.tableView beginUpdates]; 
     NSLog(@"Count of rows to delete: %d", [indexPathsToDelete count]); 
     NSLog(@"Rows before delete: %d", [self.tableView numberOfRowsInSection:section]); 
     [self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop]; 
     NSLog(@"Rows after delete: %d", [self.tableView numberOfRowsInSection:section]); 

    } 

    [self.tableView endUpdates]; 
} 
} 

die Log-Meldungen zeigen, dass auf offenes (Einfügungen),> 0 Zeilen eingefügt werden, und doch ist die Zeilenanzahl für diesen Abschnitt bleibt 0:

2012-03-31 13:36:17.454 QuickList7[5523:fb03] Count of rows to insert: 3 
2012-03-31 13:36:17.454 QuickList7[5523:fb03] Rows before insert: 0 
2012-03-31 13:36:17.454 QuickList7[5523:fb03] Rows after insert: 0 

Dies stellt einen inkonsistenten Zustand zwischen dem Tisch und der Datenquelle, und dann, wenn ich versuche zu „Zusammenbruch“ den Abschnitt, erhalte ich die folgende Ausnahme:

Wie kann ich 3 Zeilen einfügen und trotzdem mit 0 Zeilen enden?

Danke,

Sasha

Antwort

1

Ich habe das Problem gefunden! Es war tatsächlich in der Change-Handler des geholtResultsController. Es reagierte auf Änderungen an geschlossenen Abschnitten, die die Tabelle in einem fehlerhaften Zustand und nicht synchron mit der Datenquelle belassen. Also habe ich für jedes Update eine Überprüfung hinzugefügt, um nur Zeilen einzufügen/zu löschen/zu aktualisieren, wenn der enthaltene Abschnitt geöffnet ist.

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
    atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 
    newIndexPath:(NSIndexPath *)newIndexPath 
{ 
UITableView *tv = self.tView; 
switch(type) { 
    case NSFetchedResultsChangeInsert: 
     if ([[sectionHeaderArray objectAtIndex:newIndexPath.section] isOpen]) { 
      [tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     break; 

    case NSFetchedResultsChangeDelete: 
     if ([[sectionHeaderArray objectAtIndex:indexPath.section] isOpen]) { 
      [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     break; 

    case NSFetchedResultsChangeUpdate: 
     if ([[sectionHeaderArray objectAtIndex:indexPath.section] isOpen]) { 
      [self configureCell:[tv cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
     } 
     break; 

    case NSFetchedResultsChangeMove: 
     if ([[sectionHeaderArray objectAtIndex:indexPath.section] isOpen]) { 
      [tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     if ([[sectionHeaderArray objectAtIndex:newIndexPath.section] isOpen]) { 
      [tv insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade]; 
     } 
     break; 
} 

} 
+0

Das ist ein kniffliger! Ich hatte das gleiche Problem und es dauerte Stunden, bis ich herausfand, dass es etwas damit zu tun hatte, dass die Abschnitte nicht erweitert wurden. –

0

In meiner app Ich habe ein ähnliches Verhalten in einer ganz anderen Art und Weise umgesetzt, weil ich viel in diese Art von Problem ausgeführt wurde.

Ich habe eine Tabelle mit MenuNameCell s, MenuItemCell s und eine statische Zelle an der Unterseite. Es wird jeweils nur ein Menü erweitert, und durch Tippen auf eine MenuNameCell wird dieses Menü erweitert oder ausgeblendet. Da ich die MenuNameCell in einem eigenen Abschnitt und die MenuItemCell s in einem anderen habe, muss ich nur ganze Abschnitte einfügen/löschen, wenn ich die Tabelle neu lade.

Hier Datenquelle meines Tisch:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    // number of menus, plus 1 if a menu is open, plus 1 static cell 
    return [self.restaurant.menus count]+(self.menu != nil ? 1 : 0)+1; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    // if this section is our selected menu, return number of items, otherwise return 1 
    int numberOfRowsInSection = ([self indexPathIsInMenuItemSection:section] ? [[self.menu items] count] : 1); 
    return numberOfRowsInSection; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if (indexPath.section == [tableView numberOfSections]-1) { 
     // ... set up and return static cell 
    } 
    if ([self indexPathIsInMenuItemSection:indexPath.section]) { 
     // ... set up and return menu item cell 
    } else { 
     // ... set up and return menu name cell 
    } 
} 

und Delegierter meines Tisches:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [tableView deselectRowAtIndexPath:indexPath animated:YES]; 
    // return if it's a static cell 
    if (indexPath.section==[tableView numberOfSections]-1) 
     return; 
    // if it's a menu name cell, close open menu and maybe expand this menu 
    if (![self indexPathIsInMenuItemSection:indexPath.section]) { 
     BOOL reset = self.menu == m; 
     if (reset) [self reloadTableView:self.tableView withMenu:nil animated:YES autoscroll:NO]; 
     else [self reloadTableView:self.tableView withMenu:m animated:YES autoscroll:YES]; 
    } 
} 

ein paar Es Helfer dort erwähnt wurden:

- (BOOL)indexPathIsInMenuItemSection:(NSInteger)section 
{ 
    // returns YES if section refers to our MenuItemCells 
    int indexOfMenu = [self.restaurant getIndexOfMenu:self.menu]; 
    return indexOfMenu != -1 && section == indexOfMenu+1; 
} 

- (void)reloadTableView:(UITableView *)tableView withMenu:(Menu *)menu animated:(BOOL)animated autoscroll:(BOOL)autoscroll 
{ 
    int oldIndex = [self.restaurant getIndexOfMenu:self.menu]; 
    int newIndex = [self.restaurant getIndexOfMenu:menu]; 

    [tableView beginUpdates]; 

    if (oldIndex != -1) { 
     // index of [section for items] is oldIndex+1 
     [tableView deleteSections:[NSIndexSet indexSetWithIndex:oldIndex+1] withRowAnimation:UITableViewRowAnimationTop]; 
    } 
    if (newIndex != -1) { 
     // index for [section for items] is newIndex+1 
     [tableView insertSections:[NSIndexSet indexSetWithIndex:newIndex+1] withRowAnimation:UITableViewRowAnimationTop]; 
     [self setMenu:menu]; 
    } else { 
     // no new menu 
     [self setMenu:nil]; 
    } 

    [tableView endUpdates]; 
    if (autoscroll) [self autoscroll]; 
} 

- (void)autoscroll 
{ 
    if (self.menu != nil) { 
     int section = [self.restaurant getIndexOfMenu:self.menu]; 
     if (section != -1) { 
      NSUInteger indexes[] = {section,0}; 
      NSIndexPath *indexPath = [NSIndexPath indexPathWithIndexes:indexes length:2]; 
      [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
     } 
    } 
} 

Da meine Daten asynchron an anderer Stelle geladen, habe ich diesen Controller eingerichtet, um eine NSNotification zu erhalten, aber es sollte funktionieren genauso gut unter viewDidAppear:

[self reloadTableView:self.tableView withMenu:self.menu animated:YES autoscroll:YES]; 

Ich hoffe, das hilft! Lassen Sie es mich wissen, wenn ich etwas klarstellen kann.

+0

Danke Spencer! Dies ist ungefähr, wie ich erwartet habe, um dies zu implementieren, bevor Sie den Apple-Beispielcode finden. Hatte nicht gedacht, die Titelzellen und Inhaltszellen in getrennten Abschnitten zu setzen - gute Idee. Ich denke, ich bleibe dran, bevor ich meinen Ansatz ändere, nur um zu sehen, ob jemand andere Vorschläge hat (weil ich faul bin :). Aber ich werde es definitiv als Vorlage verwenden, wenn ich neu schreiben muss. Vielen Dank! – Sasha

Verwandte Themen