2016-06-15 26 views
6

私はコレクションビューでperformBatchUpdates中にクラッシュの悪夢に直面しています。performBatchUpdatesクラッシュでナイトメア

問題は基本的には次のとおりです。サーバー上のディレクトリに多数の画像があります。これらのファイルのサムネイルをコレクションビューに表示したいと思います。ただし、サムネイルはサーバーから非同期でダウンロードする必要があります。彼らが到着すると、次のようなものを使用してコレクションビューに挿入されます。

dispatch_async(dispatch_get_main_queue(), 
      ^{ 
       [self.collectionView performBatchUpdates:^{ 

       if (removedIndexes && [removedIndexes count] > 0) { 
        [self.collectionView deleteItemsAtIndexPaths:removedIndexes]; 
       } 

       if (changedIndexes && [changedIndexes count] > 0) { 
        [self.collectionView reloadItemsAtIndexPaths:changedIndexes]; 
       } 

       if (insertedIndexes && [insertedIndexes count] > 0) { 
        [self.collectionView insertItemsAtIndexPaths:insertedIndexes]; 
       } 

       } completion:nil]; 
      }); 

問題はこれです(私は思う)。時刻= 0でコレクションビューに10個のアイテムがあるとします。私は100のファイルをサーバーに追加します。アプリケーションで新しいファイルが表示され、サムネールのダウンロードが開始されます。サムネイルがダウンロードされると、コレクションビューに挿入されます。しかし、ダウンロードには異なる時間がかかる可能性があり、このダウンロード操作は非同期であるため、iOSはコレクションの要素数を失い、この致命的な悪名高いメッセージですべてがクラッシュします。

***によりキャッチされない例外「NSInternalInconsistencyException」、理由にアプリを終了:「無効な更新:セクション0の項目の無効 数更新後 既存のセクションに含まれる項目の数(213)更新前のそのセクションに含まれている アイテムの数(154)、そのセクションに挿入または削除されたアイテムの数(40 が挿入され、0が削除された)、プラスまたはマイナス 、プラスまたはマイナスのアイテム数 に移動したか、そのセクションから移動しました(0は移動し、0は移動しました)。

私がデータセットの項目の数を印刷すると、正確に213が表示されます。つまり、データセットが正しい数字に一致し、メッセージがナンセンスです。

私はhereの前にこの問題を抱えていましたが、それはiOS 7プロジェクトでした。何とか今問題がiOS 8に戻ってしまって、ソリューションが動作しなくなり、データセットがSYNCにあるようになりました。

答えて

1

私は問題がインデックスによって引き起こされると思います。

キー:更新され、削除された項目については

  • 、インデックスは、元のアイテムのインデックスでなければなりません。
  • 挿入アイテムの場合、インデックスは最終アイテムのインデックスでなければなりません。ここで

コメントとデモコードです:

class CollectionViewController: UICollectionViewController { 

    var items: [String]! 

    let before = ["To Be Deleted 1", "To Be Updated 1", "To Be Updated 2", "To Be Deleted 2", "Stay"] 
    let after = ["Updated 1", "Updated 2", "Added 1", "Stay", "Added 2"] 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Refresh", style: .Plain, target: self, action: #selector(CollectionViewController.onRefresh(_:))) 

     items = before 
    } 

    func onRefresh(_: AnyObject) { 

     items = after 

     collectionView?.performBatchUpdates({ 
      self.collectionView?.deleteItemsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0), NSIndexPath(forRow: 3, inSection: 0), ]) 

      // Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete and reload the same index path 
      // self.collectionView?.reloadItemsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0), NSIndexPath(forRow: 1, inSection: 0), ]) 

      // NOTE: Have to be the indexes of original list 
      self.collectionView?.reloadItemsAtIndexPaths([NSIndexPath(forRow: 1, inSection: 0), NSIndexPath(forRow: 2, inSection: 0), ]) 

      // Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert item 4 into section 0, but there are only 4 items in section 0 after the update' 
      // self.collectionView?.insertItemsAtIndexPaths([NSIndexPath(forRow: 4, inSection: 0), NSIndexPath(forRow: 5, inSection: 0), ]) 

      // NOTE: Have to be index of final list 
      self.collectionView?.insertItemsAtIndexPaths([NSIndexPath(forRow: 2, inSection: 0), NSIndexPath(forRow: 4, inSection: 0), ]) 

     }, completion: nil) 
    } 

    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { 
     return 1 
    } 
    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
     return items.count 
    } 

    override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 
     let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MyCell", forIndexPath: indexPath) 

     let label = cell.viewWithTag(100) as! UILabel 

     label.text = items[indexPath.row] 

     return cell 
    } 
} 
3

アニメーション「グループ」ごとに表示されている画像をバッチ処理する必要があるようです。前に、このようなクラッシュを扱うことから、道は「performBatchUpdatesの作品が

  1. あなたのブロック、すべてのアイテム数も二重のチェックを呼び出す前で、「numberOfItemsInSection」を呼び出すことで保存します(これはあなたのエラーメッセージの154です)。
  2. ブロックを実行して挿入/削除を追跡し、最後の項目数がの場合は、挿入と削除に基づいてになるはずです。
  3. ブロックを実行すると、dataSource 'numberOfItemsInSection'(これは213の数値)を要求するときに計算されたカウントが実際のカウントに二重にチェックされます。一致しない場合、クラッシュします。

変数 'insertedIndexes'と 'changedIndexes'に基づいて、サーバーからのダウンロード応答に基づいて表示する必要があるものを事前に計算してから、バッチを実行しています。しかし、私はあなたの 'numberOfItemsInSection'メソッドは常にちょうど真のアイテム数を返すと推測しています。

したがって、ステップ2でダウンロードが完了し、「3」でサニティチェックを実行すると、あなたの数字はもう整列しません。

最も簡単な解決策:すべてのファイルがダウンロードされるまで待ってから、「batchUpdates」を1回実行してください。おそらく最高のユーザーエクスペリエンスはありませんが、この問題は回避されます。

ハードナー解決策:必要に応じてバッチを実行し、アイテムの総数とは別に現在表示されているアイテム/現在アニメーション化されているアイテムを追跡します。その後解決する唯一のことは、最後の項目は、アニメーション(多分メソッドを持っている間に終わっダウンロードする場合は、残りの項目に1つの最終チェックを実行することを確認することです

BOOL _performingAnimation; 
NSInteger _finalItemCount; 

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 
    return _finalItemCount; 
} 

- (void)somethingDidFinishDownloading { 
    if (_performingAnimation) { 
     return; 
    } 
    // Calculate changes. 
    dispatch_async(dispatch_get_main_queue(), 
      ^{ 
       _performingAnimation = YES; 
       [self.collectionView performBatchUpdates:^{ 

       if (removedIndexes && [removedIndexes count] > 0) { 
        [self.collectionView deleteItemsAtIndexPaths:removedIndexes]; 
       } 

       if (changedIndexes && [changedIndexes count] > 0) { 
        [self.collectionView reloadItemsAtIndexPaths:changedIndexes]; 
       } 

       if (insertedIndexes && [insertedIndexes count] > 0) { 
        [self.collectionView insertItemsAtIndexPaths:insertedIndexes]; 
       } 

       _finalItemCount += (insertedIndexes.count - removedIndexes.count); 
       } completion:^{ 
       _performingAnimation = NO; 
       }]; 
      }); 
} 

「performFinalAnimationIfNeeded」あなたは:ような何か完了ブロックで実行)

+0

私は同じ問題に直面しています。私はあなたの解決策を試みます。 – vmeyer

関連する問題