2016-12-14 53 views
1

我使用帶有Realm結果的UITableView作爲數據源。結果通過領域通知進行註冊,並在發生更改時進行更新。但是,同一個表視圖具有一個搜索欄,可根據用戶搜索查詢過濾結果。這也可以正常工作,但是如果通知偵聽器識別出更新,則部分和行計數不匹配。什麼是正確的方法來做到這一點?是否可以更新NotificationToken?如果在通知初始化後更新結果,我如何使用領域通知?

這是初始化通知令牌的代碼:

let realm = try! Realm() 

results = realm.objects(Person.self).filter("active = '1'").sorted(byProperty: "name", ascending: true) 

// Observe Results Notifications 
notificationToken = results?.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in 

    guard let tableView = self?.tableView else { return } 
    switch changes { 
    case .initial: 
     // Results are now populated and can be accessed without blocking the UI 
     tableView.reloadData() 
     break 
    case .update(_, let deletions, let insertions, let modifications): 

     // Query results have changed, so apply them to the UITableView 
     tableView.beginUpdates() 

     tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic) 

     tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic) 
     tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic) 
     tableView.endUpdates() 
     break 
    case .error(let error): 
     // An error occurred while opening the Realm file on the background worker thread 
     print("Error: \(error)") 
     break 
    } 
} 

此代碼更新基於用戶搜索輸入的結果:

func updateSearchResults(for searchController: UISearchController) { 

    let searchText = searchController.searchBar.text! 

    if searchText.isEmpty == false { 

     results = results?.realm?.objects(Person.self).filter("active = '1'") 

     results = results?.filter("name BEGINSWITH[c] %@ OR lastName CONTAINS[c] %@", searchText, searchText) 

     results = results?.sorted(byProperty: "name", ascending: true) 

     self.tableView.reloadData() 

    } 
    else { 

     results = results?.realm?.objects(Person.self).filter("active = '1'").sorted(byProperty: "name", ascending: true) 

     self.tableView.reloadData() 
    } 
} 

如果搜索qwuery已經執行和前面帶有對領域數據庫的更新,會發生NSRange異常。

回答

4

您在updateSearchResults()中覆蓋results。通知和notificationToken綁定到特定的Results對象和查詢。因此,如果您用另一個查詢覆蓋results,則應停止先前的通知,然後將通知重新添加到新的results對象。

所以updateSearchResults()方法應該是這樣的:

func updateSearchResults(for searchController: UISearchController) { 
    let searchText = searchController.searchBar.text! 
    if searchText.isEmpty == false { 
     results = results?.realm?.objects(Person.self).filter("active = '1'") 
     results = results?.filter("name BEGINSWITH[c] %@ OR lastName CONTAINS[c] %@", searchText, searchText) 
     results = results?.sorted(byProperty: "name", ascending: true) 
    } 
    else { 
     results = results?.realm?.objects(Person.self).filter("active = '1'").sorted(byProperty: "name", ascending: true) 
    } 

    notificationToken.stop() 
    notificationToken = results?.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in 
     guard let tableView = self?.tableView else { return } 
     switch changes { 
     case .initial: 
      tableView.reloadData() 
      break 
     case .update(_, let deletions, let insertions, let modifications): 
      tableView.beginUpdates() 

      tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic) 

      tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic) 
      tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic) 
      tableView.endUpdates() 
      break 
     case .error(let error): 
      print("Error: \(error)") 
      break 
     } 
    } 
} 
+0

優秀的,是有道理的。感謝您的好解決方案! – fisher

+1

沒有想到通知令牌的行爲如此,謝謝澄清 –