无法使用 mapKit 从 ViewController 将坐标发送到具有委托的另一个 viewController

Posted

技术标签:

【中文标题】无法使用 mapKit 从 ViewController 将坐标发送到具有委托的另一个 viewController【英文标题】:Unable to send coordinates from ViewController with mapKit to another viewController with delegate 【发布时间】:2020-10-29 20:58:35 【问题描述】:

我是 Swift 和 ios 开发的新手。我有一个问题,我通过标签栏导航控制器连接了三个 viewController。第一个 viewController 现在是空的并且没有被使用。在 secondViewController 中,我使用来自天气 API 的数据更新 UI。在thirdViewController 我有一张地图。当我访问地图时,我需要坐标来更新 secondViewController 中的 url。

我想要实现的是在 thirdViewController 中检索当前的纬度和经度(没问题),但我无法通过委托发送这些值以触发 secondViewController 中的功能,从而更新里面的变量。 secondViewController 中的委托函数只是没有被触发。我究竟做错了什么?或者甚至可以这样做吗?

两个viewController的代码如下:

secondViewController(这是我需要在thirdViewController 中的地图值的地方)

import UIKit

class SecondViewController: UIViewController, MapViewControllerDelegate 

    var forecastData: [ForecastData] = []
    var currentLat: String = "59.911166"
    var currentLon: String = "10.744810"
    
    @IBOutlet weak var tableView: UITableView!
    
    var weatherManager = WeatherManager()
    var mapViewController = ThirdViewController()
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        mapViewController.delegate = self
        weatherManager.delegate = self
        weatherManager.fetchWeather(latitude: currentLat, longitude: currentLon)
        tableView.backgroundView = UIImageView(image: UIImage(named: "background"))
        tableView.dataSource = self
        tableView.register(UINib(nibName: "WeatherCell", bundle: nil), forCellReuseIdentifier: "WeatherCell")
        
    
    
    func viewDidAppear() 
        weatherManager.fetchWeather(latitude: currentLat, longitude: currentLon)
        
    
    
    
    // THIS FUNCTION SHOULD BE TRIGGERED BUT DOESN'T
    
    func mapViewController(latitude: String, longitude: String) 
        currentLat = latitude
        currentLon = longitude
        print(currentLat)
        print(currentLon)
        print("DELEGATE METHOD TRIGGERED")
    



//MARK: - WeatherManagerDelegate

extension SecondViewController: WeatherManagerDelegate 
    
    func didUpdateWeather(_ weatherManager: WeatherManager, weather: WeatherModel) 
        
        forecastData.append(ForecastData(timespan: "Nå", precipitation: "", tempOrWeather: "Temperatur", tempWeatherLabel: "\(weather.temperatureNow)" + " " + "\(weather.tempUnits)"))
        
        forecastData.append(ForecastData(timespan: "Neste Time", precipitation: "\(weather.precipitationNext1H)" + " " + "\(weather.precipUnits)", tempOrWeather: "vær", tempWeatherLabel: String(weather.conditionNext1H) ))
        
        forecastData.append(ForecastData(timespan: "Neste 6 timer", precipitation: "\(weather.precipitationNext6H)" + " " + "\(weather.precipUnits)", tempOrWeather: "vær", tempWeatherLabel: String(weather.conditionNext6H)))
        
        forecastData.append(ForecastData(timespan: "Neste 12 timer", precipitation: "", tempOrWeather: "vær",  tempWeatherLabel: String(weather.conditionNext12H)))
        
        DispatchQueue.main.async 
            self.tableView.reloadData()
        
        
    
    
    func didFailWithError(error: Error) 
        print(error)
    


extension SecondViewController: UITableViewDataSource 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return forecastData.count
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath)
        as! WeatherCell
        cell.TimeframeLabel.text = forecastData[indexPath.row].TimeframeLabel
        cell.TempLabel.text = forecastData[indexPath.row].TempLabel
        cell.WeatherCodeLabel.text = forecastData[indexPath.row].WeatherCodeLabel
        cell.PrecipitationLabel.text = forecastData[indexPath.row].PrecipitationLabel
        return cell
    


thirdViewController(这是我尝试通过委托传递坐标值的地方)

import UIKit
import MapKit
import CoreLocation

protocol MapViewControllerDelegate 
    func mapViewController(latitude: String, longitude: String)


class ThirdViewController: UIViewController, MKMapViewDelegate 
    
    var currentLat = ""
    var currentLon = ""
    @IBOutlet weak var mMapView: MKMapView!
    fileprivate let locationManager: CLLocationManager = CLLocationManager()
    var delegate: MapViewControllerDelegate?
    
    override func viewDidLoad() 
        super.viewDidLoad()
        locationManager.requestWhenInUseAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = kCLDistanceFilterNone
        locationManager.startUpdatingLocation()
        
        mMapView.showsUserLocation = true
               
        currentLat = String((locationManager.location?.coordinate.latitude)!)
        currentLon = String((locationManager.location?.coordinate.longitude)!)
        
        print(currentLat)
        print(currentLon)
        
        //delegate?.mapViewController(latitude: "TESTlon", longitude: "TESTlati")
    
    
    override func viewDidAppear(_ animated: Bool) 
        updateWeatherView()
    
    
    // THIS SHOULD TRIGGER THE DELEGATE FUNCTION IN SECOND VIEW CONTROLLER AND PASS THE DATA ALONG
    func updateWeatherView() 
        delegate?.mapViewController(latitude: "TESTlon", longitude: "TESTlati")
    
    



我尝试了不同的方法,但似乎没有任何效果,secondViewController 中的委托函数没有被触发。同样,我对此很陌生,所以也许这是一件简单的事情。提前感谢您的帮助!另外,如果有帮助,这里是我的故事板的屏幕截图:

【问题讨论】:

【参考方案1】:

正如@Duncan C 所说,做

var weatherManager = WeatherManager()
var mapViewController = ThirdViewController()

只需创建这些视图控制器的新实例——这些与您已有的完全无关。当然,如果您执行mapViewController.delegate = self,您正在设置mapViewController 的委托...但mapViewController 不是您想要的视图控制器。

要获得您想要的视图控制器,您可以访问标签栏控制器的视图控制器(当您在情节提要中创建它们时,它们会自动为您创建),然后选择正确的:

if 
    let viewControllers = self.tabBarController?.viewControllers, 
    viewControllers.count >= 3,
    let thirdViewController = viewControllers[2] as? ThirdViewController /// [2] because that's the third view controller in the array, which is the one you want

    thirdViewController.delegate = self

self.tabBarController 可能为零,因此您需要将其解包。然后,您应该确保有 3 个以上的视图控制器。最后,您需要将所需的视图控制器 (self.tabBarController?.viewControllers[2]) 转换为 ThirdViewController。然后,您可以将委托设置为该视图控制器。

【讨论】:

作为一个小提示,如果标签栏控制器只包含 2 个项目,上面的代码将会崩溃。将 if 语句分解为多个部分会更安全,其中它取消 tabBarController 并获取其 viewControllers 属性,验证它是否包含至少 3 个项目,然后提取第 3 个项目并尝试将其转换为 ThirdViewController。鉴于选项卡栏控制器的设置是在编译时确定的,您可以争辩说让它因数组越界错误而崩溃是可以的,因为如果您破坏了它,您会立即意识到。 为安全起见重写它,if 语句可能如下所示:if let viewControllers = self.tabBarController?.viewControllers, viewControllers.count >=3, let thirdViewController = viewControllers[2] as? ThirdViewController

以上是关于无法使用 mapKit 从 ViewController 将坐标发送到具有委托的另一个 viewController的主要内容,如果未能解决你的问题,请参考以下文章

iOS MapKit,细节较少

未调用 iOS MapKit completerDidUpdateResults

使用 Alamofire 和 Mapkit Swift 3 请求

SwiftUI MapKit UIViewRepresentable 无法显示警报

为啥 CoreLocation 无法获取新的 GPS 数据并且 Mapkit 显示这个旧的 GPS 数据

如何使用 MapKit 从地图中删除所有注释