Cant insert rows into UITableView - Swift 3

2019-09-19 07:33发布

I have the following code which causes the app to crash with the error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update'

I have tried multiple solutions as listed on StackOverflow but none can stop this error.

Tried:

Code:

import UIKit
import MapKit

class TableViewController: UITableViewController, UISearchResultsUpdating {

    var localSearchRequest:MKLocalSearchRequest!
    var localSearch:MKLocalSearch!
    var localSearchResponse:MKLocalSearchResponse!
    var locations = [MKMapItem]()

    @available(iOS 8.0, *)
    public func updateSearchResults(for searchController: UISearchController) {
        localSearch?.cancel()
        localSearchRequest = MKLocalSearchRequest()
        localSearchRequest.naturalLanguageQuery = searchController.searchBar.text
        localSearch = MKLocalSearch(request: localSearchRequest)
        localSearch.start { (localSearchResponse, error) -> Void in
            if localSearchResponse != nil {
                let toNum = localSearchResponse?.mapItems.count ?? 0
                for i in stride(from: 0, to: toNum, by: 1) {
                    self.locations.append((localSearchResponse?.mapItems[i])!)
                    print((localSearchResponse?.mapItems[i])!)
                }
                self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: UITableViewRowAnimation(rawValue: 5)! )
            }
        }
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.locations.count;
    }

    func tableView(tableView: UITableView, cellForRowAt indexPath: NSIndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath)
        let row = indexPath.row
        cell.textLabel?.text = self.locations[row].name
        return cell
    }

    let searchController = UISearchController(searchResultsController: nil)

    override func viewDidLoad() {
        super.viewDidLoad()
        searchController.searchResultsUpdater = self
        searchController.hidesNavigationBarDuringPresentation = false
        searchController.dimsBackgroundDuringPresentation = false
        searchController.searchBar.sizeToFit()
        searchController.searchBar.tintColor = UIColor(colorLiteralRed: 1, green: 1, blue: 1, alpha: 1)
        searchController.searchBar.placeholder = "Location"
        searchController.searchBar.returnKeyType = UIReturnKeyType(rawValue: 6)!
        self.tableView.tableHeaderView = searchController.searchBar
    }

    override func viewDidDisappear(_ animated: Bool) {
        searchController.searchBar.isHidden = true
    }
}

EDIT: Using the new localSearch.start that vadian suggested as follows:

localSearch.start { (localSearchResponse, error) -> Void in
        if let response = localSearchResponse {
            self.locations.removeAll()
            for mapItem in response.mapItems {
                self.locations.append(mapItem)
                print(mapItem)
            }
            if !response.mapItems.isEmpty {
                let indexPaths = (0..<response.mapItems.count).map { IndexPath(row: $0, section: 0) }
                self.tableView.insertRows(at: indexPaths, with: .none)
            }
        }
    }

This still produces the same error,
attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update .
Any help will be appreciated

2条回答
我只想做你的唯一
2楼-- · 2019-09-19 07:57

Your code got two major issues:

  • Even if the number of map items is 0 one row is inserted anyway. (That caused the error).
  • If there is more than one map item also only one row is inserted.

Try something like this

    if let response = localSearchResponse { 
        for mapItem in response.mapItems { // please not those ugly C-stlye loops
           self.locations.insert(mapItem, at:0)
           print(mapItem)
        }
        if !response.mapItems.isEmpty {
            let indexPaths = (0..<response.mapItems.count).map { IndexPath(row: $0, section: 0) }
            self.tableView.insertRows(at: indexPaths, with: .none )
        }
    }

Note: UITableViewRowAnimation(rawValue: 5)! and .none is exactly the same, the latter is much shorter and dramatically more descriptive.

But as you don't want to use row animation at all I'd recommend

    if let response = localSearchResponse { 
        for mapItem in response.mapItems {
           self.locations.append(mapItem)
           print(mapItem)
        }
        self.tableView.reloadData()
    }
查看更多
一夜七次
3楼-- · 2019-09-19 08:12

Please try to reload your UITableview after fetching data from network call.

https://developer.apple.com/reference/uikit/uitableview/1614862-reloaddata

(You are inserting before the locations are fetched, and hence the tableview rows are still 0.)

Thanks Sriram

查看更多
登录 后发表回答