Deleting from UISearchController's filtered search results

后端 未结 1 485
一生所求
一生所求 2020-12-19 01:24

I have a tableView sourcing its cell content from CoreData and have been replacing the SearchDisplayController (deprecated) with the n

相关标签:
1条回答
  • 2020-12-19 02:06

    The problem arises because of a mismatch between the indexPath used by the fetched results controller and the indexPath for the corresponding row in the tableView.

    Whilst the search controller is active, the existing tableView is reused to display the search results. Hence your logic to differentiate the two tableViews:

    if searchPredicate == nil {
        tableView = self.tableView
    } else {
        tableView = (searchController.searchResultsUpdater as LocationViewController).tableView
    }
    

    is unnecessary. It works, because you set searchController.searchResultsUpdater = self when you initialise the searchController, so there is no need to change it, but the same tableView is used in either case.

    The difference lies in the way the tableView is populated whilst the searchController is active. In that case, it looks (from the numberOfRowsInSection code) as though the filtered results are all displayed in one section. (I assume cellForRowAtIndexPath works similarly.) Suppose you delete the item at section 0, row 7, in the filtered results. Then commitEditingStyle will be called with indexPath 0-7, and the following line:

    let location: Location = self.fetchedResultsController.objectAtIndexPath(indexPath) as Location
    

    will try to get the object at index 0-7 from the FRC. But the item at index 0-7 of the FRC might be a completely different object. Hence you delete the wrong object. Then the FRC delegate methods fire, and tell the tableView to delete the row at index 0-7. Now, if the object really deleted was NOT in the filtered results, then the count of rows will be unchanged, even though a row has been deleted: hence the error.

    So, to fix it, amend your commitEditingStyle so that it finds the correct object to delete, if the searchController is active:

    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == .Delete {
            var location : Location
            if searchPredicate == nil {
                location = self.fetchedResultsController.objectAtIndexPath(indexPath) as Location
            } else {
                let filteredObjects = self.fetchedResultsController.fetchedObjects?.filter() {
                    return self.searchPredicate!.evaluateWithObject($0)
                }
                location = filteredObjects![indexPath.row] as Location
            }
            location.removePhotoFile()
    
            let context = self.fetchedResultsController.managedObjectContext
            context.deleteObject(location)
    
            var error: NSError? = nil
            if !context.save(&error) {
                abort()
            }
        }
    }
    

    I haven't been able to test the above; apologies if some errors slipped in. But it should at least point in the right direction; hope it helps. Note that similar changes may be required in some of the other tableView delegate/datasource methods.

    0 讨论(0)
提交回复
热议问题