MapKit地图在不同ViewController内部调用的函数内部返回nil

时间:2019-05-22 22:01:15

标签: ios swift

我目前正在尝试实现与搜索功能关联的地图。对于包含表格视图的叠加层,我决定选择一个名为FloatingPanel的库。

我必须使用ViewControllers,即MapViewControllerSearchTableViewController-顾名思义,MapViewController包含一个mapView。我假设自从FloatingPanel将SearchTableViewController (STVC)添加到MapViewController (MVC)以来,STVC是MVC的子代。

现在,每当我想调用MapViewController的函数在SearchTableViewController内添加注释时,MapViewController的mapView返回nil-在MapViewController内调用很好。

class MapViewController: UIViewController, FloatingPanelControllerDelegate, UISearchBarDelegate {

    var fpc: FloatingPanelController!
    var searchVC = SearchResultTableViewController()

    let locationManager = CLLocationManager()
    let regionInMeters: Double = 10000

    @IBOutlet private var mapView: MKMapView!

    var mapItems: [MKMapItem]?

    override func viewDidLoad() {
        super.viewDidLoad()

        checkLocationServices()
        fpc = FloatingPanelController()
        fpc.delegate = self

        fpc.surfaceView.backgroundColor = .clear
        fpc.surfaceView.cornerRadius = 9.0
        fpc.surfaceView.shadowHidden = false

        searchVC = (storyboard?.instantiateViewController(withIdentifier: "SearchPanel") as! SearchResultTableViewController)

        fpc.set(contentViewController: searchVC)
        fpc.track(scrollView: searchVC.tableView)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        fpc.addPanel(toParent: self, animated: true)
        fpc.move(to: .tip, animated: true)

        searchVC.searchController.searchBar.delegate = self
    }

    func checkLocationServices() {
        if CLLocationManager.locationServicesEnabled() {
            setupLocationManager()
            checkLocationAuthorization()
        } else {
        }
    }

    func setupLocationManager() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
    }

    func checkLocationAuthorization() {
        switch CLLocationManager.authorizationStatus() {
        case .authorizedWhenInUse:
            centerViewOnUserLocation()
            break
        case .denied:
            break
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
            break
        case .restricted:
            break
        case .authorizedAlways:
            break
        @unknown default:
            break
        }
    }

    func centerViewOnUserLocation() {
        if let location = locationManager.location?.coordinate {
            let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
            mapView.setRegion(region, animated: true)
        }
    }

    @IBAction func click(_ sender: Any) {

    }

    func addPin(title: String, subtitle: String, coordinates: CLLocationCoordinate2D) {
        let destination = customPin(pinTitle: title, pinSubTitle: subtitle, location: coordinates)
        mapView.addAnnotation(destination)
    }

    func addAnnotationToMap() {
        guard let item = mapItems?.first else { return }
        guard let coordinates = item.placemark.location?.coordinate else { return }

        addPin(title: item.name!, subtitle: "", coordinates: coordinates)
    }
}

SearchTableViewController的功能

func passData() {
        guard let mapViewController = storyboard?.instantiateViewController(withIdentifier: "map") as? MapViewController else { return }

        guard let mapItem = places?.first else { return }

        mapViewController.mapItems = [mapItem]
        mapViewController.addAnnotationToMap()
}

3 个答案:

答案 0 :(得分:3)

 guard let mapViewController = storyboard?.instantiateViewController(withIdentifier: "map") as? MapViewController else { return } 
 guard let mapItem = places?.first else { return }

创建一个新的单独对象,而不是您必须访问的实际对象,请在此处使用委托进行操作

searchVC = (storyboard?.instantiateViewController(withIdentifier: "SearchPanel") as! SearchResultTableViewController)
searchVC.delegate = self // here
fpc.set(contentViewController: searchVC) 

然后声明

weak var delegate:MapViewController?

SearchViewController内并使用

func passData() {  
    guard let mapItem = places?.first else { return } 
    delegate?.mapItems = [mapItem]
    delegate?.addAnnotationToMap()
}

答案 1 :(得分:3)

几件事:

  1. 当您在项目中使用第三方库并且读者可能想了解该库以了解您的问题时,应在问题中包含指向该库的链接。我进行了一次Google搜索,找到了我认为正确的库。

  2. 该库的示例代码让您在视图控制器的viewDidLoad()中而不是viewDidAppear()中调用fpc.addPanel()。

  3. viewDidLoad()函数在视图控制器的生命周期中仅被调用一次,但是viewDidAppear()在视图控制器每次被重新显示时都被调用(例如在重新显示视图控制器时被重新显示)出于这个原因,两者不可互换。我建议将该呼叫移回viewDidLoad()

  4. 接下来,就像其他人提到的那样,您的SearchTableViewController的passData()函数是错误的。每次调用时,它都会创建一个新的,丢弃的MapViewController。它根本不与托管MapViewController对话。

您应该重构viewDidLoad(),以将MapViewController设置为SearchTableViewController的委托。

定义协议(可能在单独的文件中

protocol SearchTableViewControllerDelegate {
   var mapItems: [MapItem] //Or whatever type
   func addAnnotationToMap()
}

对MapViewController的一些更改

class MapViewController: 
  SearchTableViewControllerDelegate,
  UIViewController, 
  FloatingPanelControllerDelegate, 
  UISearchBarDelegate {

  //Your other code...
}
override func viewDidLoad() {
    super.viewDidLoad()

    checkLocationServices()
    fpc = FloatingPanelController()
    fpc.delegate = self

    fpc.surfaceView.backgroundColor = .clear
    fpc.surfaceView.cornerRadius = 9.0
    fpc.surfaceView.shadowHidden = false

    searchVC = (storyboard?.instantiateViewController(withIdentifier: "SearchPanel") as! SearchResultTableViewController)

    //--------------------------
    searchVC.delegate = self  //This new line is important
    //--------------------------

    fpc.set(contentViewController: searchVC)
    fpc.track(scrollView: searchVC.tableView)
    fpc.addPanel(toParent: self)  //Probably can't be animated at this point
}

在SearchTableViewController中:

class SearchTableViewController: UITableViewController, <Other protocols> {
    weak var delegate: SearchTableViewControllerDelegate?

    // other code...

    func passData() {
        guard let mapItem = places?.first else { return }

        delegate?.mapItems = [mapItem]
        delegate?.addAnnotationToMap()
    }
}

答案 2 :(得分:1)

如果将数据传递到现有的MapViewController,则将实例化。有三种方法可以做到这一点:

  1. 委派
  2. 关闭
  3. 通知

在SearchViewController中使用闭包的示例:

var passData: ((MKMapItem) -> ())?

在MapViewController中为其提供一个闭包:

searchVC = (storyboard?.instantiateViewController(withIdentifier: "SearchPanel") as! SearchResultTableViewController)
searchVC.passData = { mapItem in 
  self.mapItems = [mapItem]
}

在SearchViewController中调用闭包:

passData?(mapItem)