简体   繁体   English

UI Tableview 不会在单元格中显示或更新搜索结果,Swift

[英]UI Tableview won't display or update search results in cells, Swift

I am an intermediate Swift developer and I am creating an app that involves using a search function to find an address and pinpoint said address on a map.我是一名中级 Swift 开发人员,我正在创建一个应用程序,该应用程序涉及使用搜索 function 来查找地址并在 map 上查明所述地址。 I followed a tutorial on how to achieve this and everything is functional besides the search function itself.我遵循了一个关于如何实现这一点的教程,除了搜索 function 本身之外,一切都正常。 Whenever I type in the UIsearchbar my search results tableview controller is instantiated, however, the table view is blank and does not update as I type.每当我在 UIsearchbar 中键入时,我的搜索结果 tableview controller 都会被实例化,但是,table view 是空白的,并且在我键入时不会更新。 An address API call should be present.应该存在地址 API 调用。

Below is my code for the search table下面是我的搜索表代码

import UIKit
import MapKit


class LocationSearchTable : UITableViewController {
    
    var resultSearchController:UISearchController? = nil
    var handleMapSearchDelegate:HandleMapSearch? = nil
    var matchingItems:[MKMapItem] = []
    var mapView: MKMapView? = nil
    @IBOutlet var searchTableView: UITableView!
    
}
extension LocationSearchTable : UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        searchController.showsSearchResultsController = true
    }
    
    func updateSearchResultsForSearchController(searchController: UISearchController) {
        guard let mapView = mapView,
              let searchBarText = searchController.searchBar.text else { return }
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = searchBarText
        request.region = mapView.region
        let search = MKLocalSearch(request: request)
        search.start { response, _ in
            guard let response = response
            else {
                return
            }
            self.matchingItems = response.mapItems
            self.tableView.reloadData()
        }
    }

        
    
}
extension LocationSearchTable {
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return matchingItems.count
        
    }
    func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
        let selectedItem = matchingItems[indexPath.row].placemark
        cell.textLabel?.text = selectedItem.name
        cell.detailTextLabel?.text = ""
        return cell
    }
}
extension LocationSearchTable {
        func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let selectedItem = matchingItems[indexPath.row].placemark
            handleMapSearchDelegate?.dropPinZoomIn(placemark: selectedItem)
            dismiss(animated: true, completion: nil)
    }
}

This is my code for my initial view controller这是我的初始视图 controller 的代码

import UIKit
import MapKit
import CoreLocation

protocol HandleMapSearch {
    func dropPinZoomIn(placemark:MKPlacemark)
}
//for some reason my location delegate is not working (resolved)
class locationViewController: UIViewController, UISearchResultsUpdating, UISearchBarDelegate {
    
    func updateSearchResults(for searchController: UISearchController) {
        return
        
        
    }
    
   
    
    var selectedPin:MKPlacemark? = nil
    
    var resultSearchController:UISearchController? = nil
    @IBOutlet weak var eventLocationMapView: MKMapView!
    @IBOutlet weak var backButton: UIButton!
    
    let locationManager = CLLocationManager()
    let regionInMeters: Double = 10000
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
       
        resultSearchController?.searchResultsUpdater = self
        
        let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
        resultSearchController = UISearchController(searchResultsController: locationSearchTable)
        resultSearchController?.searchResultsUpdater = locationSearchTable
        resultSearchController?.searchBar.delegate = self
       
            locationSearchTable.mapView = eventLocationMapView
       
        let searchBar = resultSearchController!.searchBar
                
        searchBar.sizeToFit()
                
        searchBar.placeholder = "Search for places"
        //this is the search bar
        navigationItem.titleView = resultSearchController?.searchBar
                
        resultSearchController?.hidesNavigationBarDuringPresentation = false
                
        resultSearchController?.obscuresBackgroundDuringPresentation = true
                
        definesPresentationContext = true
        
        locationSearchTable.handleMapSearchDelegate = self
        //all of this is in regards to the search functionality
        
        locationManager.startUpdatingLocation()
        self.checkLocationAuthorization()
        self.checkLocationServices()
        self.navigationController?.isNavigationBarHidden = false
        // Do any additional setup after loading the view.
        locationManager.requestWhenInUseAuthorization()
        //this pushes the user request. The issue was most likely in the switch statement
        locationManager.delegate = self
        locationManager.requestLocation()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest

        //solved it but we will probably have to go back to this
        
        self.centerViewOnUserLocation()
    }
        
    @IBAction func backButtonTapped(_ sender: Any) {
        
        self.transitionBackToCreateEventVC()
        
    }
        

    func transitionBackToCreateEventVC (){
        let createEventViewController = self.storyboard?.instantiateViewController(identifier: "createEventVC")
        
        self.view.window?.rootViewController = createEventViewController
        self.view.window?.makeKeyAndVisible()
        
        
        
    }
    func centerViewOnUserLocation() {
        
        if let location = locationManager.location?.coordinate {
            let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
            eventLocationMapView.setRegion(region, animated: true)
            //this function centers the map onto the location of the user
        }
        
    }
    
    
    func setUpLocationManager(){
        
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        
    }
    func checkLocationServices(){
        
        if CLLocationManager.locationServicesEnabled(){
            setUpLocationManager()
            checkLocationAuthorization()
        } else {
            //show alert letting the user know they have to turn this on
        }
        
    }
    func checkLocationAuthorization() {
        
        let locationManager = CLLocationManager()
        switch locationManager.authorizationStatus {
        case .authorizedWhenInUse:
            
            eventLocationMapView.showsUserLocation = true
            // We want the users location while the app is in use
            break
        case .denied:
            //show alert instructing how to turn on permissions
            break
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
            return
        case .restricted:
            //show an alert
            break
        case .authorizedAlways:
            // we don't want this
            break
        @unknown default:
            return
            //I dont know if we need this code
        }
        
    }
    /*
    // MARK: - Navigation

    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
    }
    */

}
extension locationViewController: CLLocationManagerDelegate {
    
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedWhenInUse {
              locationManager.requestLocation()
          }
      }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
       
        if let location = locations.first {
            
            let region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 0.05 , longitudinalMeters: 0.05)
                    eventLocationMapView.setRegion(region, animated: true)
            
        }
        
    }
    public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
            print("error:: \(error)")
    }
}
extension locationViewController: HandleMapSearch {
    func dropPinZoomIn(placemark:MKPlacemark){
        // cache the pin
        selectedPin = placemark
        // clear existing pins
        eventLocationMapView.removeAnnotations(eventLocationMapView.annotations)
        let annotation = MKPointAnnotation()
        annotation.coordinate = placemark.coordinate
        annotation.title = placemark.name
        if let city = placemark.locality,
        let state = placemark.administrativeArea {
            annotation.subtitle = "\(city) \(state)"
        }
        eventLocationMapView.addAnnotation(annotation)
        let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        let region = MKCoordinateRegion(center: placemark.coordinate, span: span)
        eventLocationMapView.setRegion(region, animated: true)
    }
}


I think the you should make a property to hold the MKLocalSearch object in your LocationSearchTable class.我认为您应该在LocationSearchTable class 中创建一个属性来保存MKLocalSearch object。

class LocationSearchTable : UITableViewController {
    private var search: MKLocalSearch?
    // ...
}

extension LocationSearchTable : UISearchResultsUpdating {
    func updateSearchResultsForSearchController(searchController: UISearchController) {
        guard let mapView = mapView,
              let searchBarText = searchController.searchBar.text else { return }
        // if there is an ongoing search, cancel it first.
        self.search?.cancel()

        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = searchBarText
        request.region = mapView.region
        let search = MKLocalSearch(request: request)
        search.start { response, error in
            guard let response = response
            else {
                return
            }
            self.matchingItems = response.mapItems
            self.tableView.reloadData()
        }
        // make sure `search` is not released.
        self.search = search
    }

}

The problem of the original code is that, the search object, instance of MKLocalSearch class will be released when finishes executing updateSearchResultsForSearchController method, since there is no strong reference to it, and start 's callback will never be called, so your table view will never be reloaded.原代码的问题是, search object,MKLocalSearch 实例MKLocalSearch将在执行完updateSearchResultsForSearchController方法后被释放,因为没有强引用它,并且start的回调永远不会被调用,所以你的表视图将永远不会重新加载。

What we do is just make a strong ref to it, and make sure it is not released before completion handler is called.我们所做的只是对其进行强引用,并确保在调用完成处理程序之前它不会被释放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM