2009-06-29 8 views
37

と削除セクションを更新のUITableView:アニメーション

私は、以下の答えとして、この問題に対する私の解決策を掲載しています。私の最初の改訂とは異なるアプローチをとっています。


私は以前、私は私の問題を解決思っように質問をし 元の質問は:のUITableViewからセクションを削除するとき

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

しかし、私は今、再び同様の問題を抱えています。 (テーブルのセクション数/行数を変更したときに再表示されます)。

私の郵便物のせん断長さのためにあなたを失う前に、私は問題をはっきりと述べ、答えを出すのに必要なだけ読むことができます。


問題:時々

バッチの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). 

ログには、それは私がデータソースと適切にテーブルを更新していないと言うので、私は墜落したと言います行とセクションを適切にdataSourceから取得します。説明は時間がかかりますが、この方法に従えば、以下で説明します。

- (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とセクション#1をセクション4から削除しますが、セクション2を削除した後は古いセクション#4が3番目のセクションになります。古いNSIndexPathの(4、1)は存在しないかもしれないランダムな異なる行を削除しています。

だから、この2行のコードを入れ替えるだけの簡単な方法かもしれないと思いますので、最初に行を削除してからセクションを削除します。

+0

代わりに、取り除く必要のある各セルのindexPathを追跡し、削除の進行に合わせて適切に調整します。 (これは長い/畳み込み/不適切な方法かもしれません - ちょっと考えてください) – Tim

+0

私はバッチ削除を行っていますので、操作のリストの順序に違いはありません。テーブルビューは、更新ブロック内にあるときに「すぐに」操作を実行します。私は妄想だったので、私は操作の順序を無駄に切り替えることを試みました。セクション/行の番号付けは、バッチ削除中に切り替わらない(切り替えてはならない)。ブロックを使用していない場合、あなたは正しいでしょう。 –

+0

@Tim興味深い考え。あなたが正しいです、それは削除(私は持っている)の大量で非常に退屈かもしれません。私はまた、私は迅速に連続して複数の削除を行うことができるかと思います。私はこれらの問題を避けるために一括削除をしようとしていましたが、必要かもしれません。 –

3

だから、最終的にここにこの問題に私の解決策である:それはそのセクションの最後の残りの行がある場合は、それはセクションからのみ、この行を削除するか、またはセクション全体を削除するかどうかを判断する必要があります。 この方法は、任意のサイズのテーブル、任意の数のセクション(私が知る限り)に適用することができます。

前と同じように、セル固有のロジックを別のセルコントローラに配置するMatt Gallagherのtableviewコードを変更しました。

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

MattのオリジナルIVAR:

NSArray *allTableGroups 

...いつものポイントしかし、あなたは簡単に私がマットのコードに次の(関連)アイバーズを追加した

異なるモデルにこの方法を適用することができます上記の配列の1つに追加します。

これはおそらくリファクタリングされ、大幅に改善される可能性がありますが、必要はありませんでした。また、コアデータを使用する場合は、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

私は途中で私のカスタムテーブルビューセルの背景ビューを解放した結果として、この同じ正確なエラーを見ました。

NSZombieEnabledを使用すると、再利用のためにセルを準備する関数の内部呼び出しの下に例外がスローされてしまいました。 NSZombieEnabledがなければ、内部一貫性エラーが発生していました。

ちなみに、セルの背景ビューで保持/解放の問題を修正したとき、セクションを明示的に削除することなく、セクションの最後の行を削除することができました。

ストーリーのモラル:このエラーは、削除しようとしたときに何か悪いことが起こったことを意味します。削除すると起こることの1つは、セルが再利用の準備ができているためです。テーブルビューのセルでエラーが発生する可能性があります。

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

ディレクトリを行います!すべてのコードは私のために働いていない!これは安価で、ちょうど意味があります!

2

セクションを表すオブジェクトを内部ストレージから削除して、すべてのセクションが削除された後に-numberOfSectionsInTableView:メソッドが1を返すのを忘れていると思われます。

これはまさに私が同じクラッシュをしたときに間違っていたことです!これに対処するための

1

Aはるかに簡単な方法は、あなたのデータソースを更新し、単一のセクションをリロードしますreloadSections

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

これを呼び出すことです。あるいは、複数のセクションを同時にリロードするには、indexSetWithIndexesInRange:を使用することもできます。

関連する問題