2016-01-28 10 views
9

フルスクリーンテーブルビューiPad専用アプリ。スワイプを有効にして、自分の行を削除しました。行アニメーションは、削除(commitEditingStyleが完了)後も常に終了しますが、時々テーブルビュー全体がフリーズします。 UI全体ではなく、ブロックされたメインスレッドではありません。私は、列ヘッダーをタップするか、ナビゲーションコントローラーの戻るボタンをタップすることができますが、テーブル自体がロックアップされ、スワイプできません。私は列ヘッダーボタンの1つをタップするだけで簡単に解凍できます。UITableViewがスワイプしてUI全体ではなく削除されます

enter image description here

私は完全にフリーズを引き起こしている可能性があります何のために途方に暮れてよ。私はNSFetchedResultsControllerを使用していますが、ここではそのための代理コードです。 (:バッチ処理のアプローチを使用していないボイラープレートとして今。更新):それはかなりボイラープレートです

// MARK: NSFetchedResultsController delegate methods 

lazy var deletedSectionIndexes : NSMutableIndexSet = { 
    return NSMutableIndexSet() 
}() 

lazy var insertedSectionIndexes : NSMutableIndexSet = { 
    return NSMutableIndexSet() 
}() 

lazy var deletedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 

lazy var insertedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 

lazy var updatedRowIndexPaths : [NSIndexPath] = { 
    return [NSIndexPath]() 
}() 


func controllerWillChangeContent(controller: NSFetchedResultsController) { 

} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
    switch(type) { 

    case .Delete: 
     if let indexPath = indexPath { 
      self.deletedRowIndexPaths.appendDistinct(indexPath) 
     } 
    case .Update: 
     if let indexPath = indexPath { 
      self.updatedRowIndexPaths.appendDistinct(indexPath) 
     } 
    case .Insert: 
     if let newIndexPath = newIndexPath { 
      self.insertedRowIndexPaths.appendDistinct(newIndexPath) 
     } 
    case .Move: 
     if let indexPath = indexPath, newIndexPath = newIndexPath { 
      self.insertedRowIndexPaths.appendDistinct(newIndexPath) 
      self.deletedRowIndexPaths.appendDistinct(indexPath) 
     } 
    } 
} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    switch(type) { 

    case .Delete: 
     self.deletedSectionIndexes.addIndex(sectionIndex) 
    case .Insert: 
     self.insertedSectionIndexes.addIndex(sectionIndex) 
    default: 
     break 
    } 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    self.tableView.beginUpdates() 
    self.tableView.insertSections(self.insertedSectionIndexes, withRowAnimation: .None) 
    self.tableView.deleteSections(self.deletedSectionIndexes, withRowAnimation: .None) 

    self.tableView.insertRowsAtIndexPaths(self.insertedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.deleteRowsAtIndexPaths(self.deletedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.reloadRowsAtIndexPaths(self.updatedRowIndexPaths, withRowAnimation: .None) 
    self.tableView.endUpdates() 

    self.insertedSectionIndexes.removeAllIndexes() 
    self.deletedSectionIndexes.removeAllIndexes() 
    self.deletedRowIndexPaths.removeAll() 
    self.insertedRowIndexPaths.removeAll() 
    self.updatedRowIndexPaths.removeAll()   
} 

削除はしかし、技術的にそれは本当の削除ではありません、didChangeObjectのデリゲートメソッドで呼び出されます。私は単純にプロパティを-1に設定し、その要素をNSMangagedObjectContextに保存します。この時点で、NSFRCはこの述語を使用してフェッチされたフェッチされたオブジェクトのリストからその要素を削除します。

NSPredicate(format: "account = %@ and quantity != -1", account) 

ここで、accountは有効なアカウント管理オブジェクトです。行は90%以上問題なく消えます。アニメーションが完了した後で、私が説明したマナーの中でテーブルがフリーズすることがあります。まだ表示されている削除ボタンでフリーズすることはないので、commitEditingStyleが呼び出された後のことです。削除ボタンにはカスタム実装がありません。これは、削除するスワイプのデフォルトのUITableView実装です。ここに私のcommitEditingStyleメソッドがあります:

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     if let frameboardItem = self.fetchedResultsController.objectAtIndexPath(indexPath) as? IRMFrameBoardItemMO { 
      if frameboardItem.isNew { 
       // If it's never been pushed to the server, just delete locally. This will trigger a table reload 
       // via NSFetchedResultsController 
       DataManager.mainContext.deleteObject(frameboardItem) 
      } else { 
       // Otherwise mark it with a negative quantity which tells the server to delete it and tells the 
       // app to hide it. 
       frameboardItem.quantity = -1 
      } 

      do { 
       try DataManager.mainContext.save() 
      } catch let error as NSError { 
       dLog("Something went wrong: \(error.localizedDescription)") 
      } 

     } 

    } 

} 

ここで私が話していることのビデオを見ることができます。 2分以上経過しているので、すべてを見たくないかもしれませんが、ここで参考にしておきます。

https://vimeo.com/153406113

は、任意の提案を聞くのが大好きです。私は更新を確実にするためにバッチ処理のアプローチを使用するようにNSFRCのデリゲートメソッドを更新

更新は一度、すべて適用されます。これは問題を修正していません。テーブルはまだ定期的にフリーズします。

+0

データソースが正しく更新されていますか? 'frameboardItem'が' nil'である状況がありますか? 'if let frameboardItem = self.fetchedResultsController.objectAtIndexPath(indexPath)as? IRMFrameBoardItemMO'?そして 'nil'の場合は、データソースを更新せずにテーブルを更新していますか? – Alexander

+0

私はあなたが削除していたところでビデオを数回見ましたが、それは凍っていました。 1つのセクションからいくつかのトップとボトムを削除した後、セクションを変更するとそうするようです。上部と下部を削除します。あるいは、それはちょうど1つまたは2つの特定のセクションです。あなたのリストの数に問題があるのか​​、アイテムがどのように再配置されているのか不思議です。それはテーブルのUIを保持しています。どのくらい正確に再現性があるかを絞り込むことができれば良いでしょう。ただ声を出して考えている。 –

+0

1)子ビューコントローラ内のtableViewですか? 2)テスト目的のために、(タッチに反応する)ボタンの1つを変更して、テレビが上(または下)にスクロールするようにする場合は、実際にスクロールしますか?それはテレビがタッチを無視しているのか、スクロールしようとしているのか凍結しているのかという手がかりを与えるかもしれない。 – pbasdf

答えて

2

私はこの問題についても推測しています。私の考えは、controllerDidChangeContentがテーブルを更新し、テーブルをハングアップできるtableView.beginUpdates()の複数の呼び出しのこの原因よりも2倍以上速く呼び出すことができるということです。

だからこの問題を解決するために、私はdispatch_asyncブロックにラップアップデートを示唆、または単純なブールフラグ

func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    dispatch_async(dispatch_get_main_queue(), {() -> Void in 
     self.tableView.beginUpdates() 
     // ..... rest of update code 
     self.updatedRowIndexPaths.removeAll() 
    }) 
} 
+0

これは正確に何をしていますか?更新がキューに入れられていますか? –

+0

@MattLongはい、更新はキュー内で結果的に実行されています – sage444

+0

残念ながら、それは非常に銀色の弾丸ではありませんが、かなり近いです。私はこの技術を使ってもフリーズを再現しましたが、頻度はごくわずかです。私はあなたの答えを正しいものとして受け入れています。暗闇の中で刺すようにしてくれてありがとう。それは有り難いです。 –

0

ブロックを使用してください。

あなたがfetchedResultsControllerを使用しているので、それはおそらくメインのですが、MOCは、からアクセスされているスレッドの私には不明です。

はそのように、あなたはperformBlockAndWait待ち

  • deleteObject
  • save

を実行することをお勧めします。それはデータの完全性を保証するのに役立ちます。線に沿って何か:

DataManager.mainContext.performBlockAndWait {() -> Void in 
    DataManager.mainContext.deleteObject(frameboardItem) 
    if DataManager.mainContext.hasChanges { 
     do { 
      try DataManager.mainContext.save() 
     } catch let error as NSError { 
      dLog("Something went wrong: \(error.localizedDescription)") 
     } 
    } 
} 
0

私は*/endEditing呼び出し(例外がスローされるだろうか、信号が送信されることを)始めるテーブルビューが原因メモリの問題やアンバランスにフリーズするとは思いません。

私は、それがメインスレッド以外のスレッド上で何かをしている可能性があると思います。このような場合でもブロックでさえ助けにならないでしょう。 (ブレークポイントを設定し、どのスレッドが停止しているかを確認してください...)

あるメソッド内のTableViewを実行します(フェッチ結果コントローラがそのデリゲート呼び出しを終了した後、そのメソッドをメインスレッド上で明示的に実行するように呼び出します)。

0

あなたはより一般的な方法でNSFe​​tchedResultsControllerDelegateのデリゲートを実装しようとしました、私はfetchedResultControllerがに尋ねたときに、テーブルの更新を始める意味します更新してから更新を終了しますか?

func controllerWillChangeContent(controller: NSFetchedResultsController) { 
    self.tableView.beginUpdates() 
} 

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 
    /* update table here */ 
} 

func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { 
    /* update table here */ 
} 

func controllerDidChangeContent(controller: NSFetchedResultsController) {  
    self.tableView.endUpdates() 
} 

UPADATE:
それはあなたが「削除されたように、オブジェクトをマーク」するとき、それは順番にdidChangeObject関数が複数回呼び出されることになりますオブジェクトの変更のいくつかのより複雑なチェーンをトリガーすることは可能です? didChangeObject関数が単一の「削除マーキング」中に呼び出された回数をトレースしましたか?

+0

はい。これは、バッチ更新に切り替える前の状態です。詳細は質問を参照してください。 –

+0

@MattLong yeap、すみません、私はそれを逃しました。 –

関連する問題