Как получить географические координаты и передать их в другой View Controller (Swift)

iOS-приложение имеет 2 View Controller. Первый получает координаты и отображает точку на карте (в файл info.plist нужная строчка добавлена):

import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController, CLLocationManagerDelegate {

    let userDefMap = UserDefaults.standard

    @IBOutlet weak var map: MKMapView!
    @IBOutlet weak var latCoordLabelOnMap: UILabel!
    @IBOutlet weak var lonCoordLabelOnMap: UILabel!
    
    var locationManager: CLLocationManager?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        locationManager = CLLocationManager()
        locationManager?.requestWhenInUseAuthorization()
        locationManager?.delegate = self
        locationManager?.desiredAccuracy = kCLLocationAccuracyBest
        locationManager?.activityType = .fitness
        locationManager?.startUpdatingLocation()
        
        if let coordinate = locationManager?.location?.coordinate {
            
            let xScale:CLLocationDegrees = 0.01
            let yScale:CLLocationDegrees = 0.01
            
            let span:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: yScale, longitudeDelta: xScale)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            
            map.setRegion(region, animated: true)
        }

        map.userTrackingMode = .followWithHeading
        }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    // метки на карте c координатами:
    latCoordLabelOnMap.text = String( (locations[0].coordinate.latitude))
    lonCoordLabelOnMap.text = String( (locations[0].coordinate.longitude))
        
    self.userDefMap.setValue(locations[0].coordinate.latitude, forKey: "lat")
    self.userDefMap.setValue(locations[0].coordinate.longitude, forKey: "lon")   
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        locationManager?.stopUpdatingLocation()
    }
    
}

Второй View Controller имеет в том числе 2 UILabel, на которых нужно отображать координаты - широту и долготу и обновлять их с некой частотой, которую можно задать с помощью переменной.

  • Как правильно передавать координаты во второй View Controller?
  • Как реализовать периодичность получения координат и обновления UILabels?

Нашел в сети несколько примеров с передачей данных между View Controllers, но адаптировать не получается.


Ответы (2 шт):

Автор решения: Pavel Lazarev

Описал два варианта. Вариант 1: работает через протокол, который уведомляет второй котроллер. Вариант 2: просто запрос к locationManager через переменную shared. Если будут вопросы - Задавайте!

// Вариант 1: протокол для получения координат
protocol MapViewControllerDelegate: class {
    func getCoordinate(lat: Double, lon: Double)
}

class MapViewController: UIViewController, CLLocationManagerDelegate {

    let userDefMap = UserDefaults.standard

    @IBOutlet weak var map: MKMapView!
    @IBOutlet weak var latCoordLabelOnMap: UILabel!
    @IBOutlet weak var lonCoordLabelOnMap: UILabel!
    
    // статическая переменная класса дает доступ к классу
    static var shared: MapViewController?
    
    // Вариант 1: делегат используется для отправки данных по протоколу
    var delegate: MapViewControllerDelegate?
    
    var locationManager: CLLocationManager?

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // инициализируем shared
        MapViewController.shared = self
        
        locationManager = CLLocationManager()
        locationManager?.requestWhenInUseAuthorization()
        locationManager?.delegate = self
        locationManager?.desiredAccuracy = kCLLocationAccuracyBest
        locationManager?.activityType = .fitness
        locationManager?.startUpdatingLocation()
        
        if let coordinate = locationManager?.location?.coordinate {
            
            let xScale: CLLocationDegrees = 0.01
            let yScale: CLLocationDegrees = 0.01
            
            let span: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: yScale, longitudeDelta: xScale)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            
            map.setRegion(region, animated: true)
        }

        map.userTrackingMode = .followWithHeading
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        // метки на карте c координатами:
        latCoordLabelOnMap.text = String((locations[0].coordinate.latitude))
        lonCoordLabelOnMap.text = String((locations[0].coordinate.longitude))
        
        // Вариант 1: отправка данных по протоколу
        delegate?.getCoordinate(lat: locations[0].coordinate.latitude, lon: locations[0].coordinate.longitude)
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        locationManager?.stopUpdatingLocation()
    }
    
    // вариант 2: по запросу обращаемся к locationManager для получения координат
    func getCoordinate() -> (lat: Double, lon: Double)? {
        guard let coordinate = locationManager?.location?.coordinate else {
            return nil
        }
        return (lat: coordinate.latitude, lon: coordinate.longitude)
    }
}

class TwoViewController: UIViewController {
    
    @IBOutlet weak var latCoordLabelOnMap: UILabel!
    @IBOutlet weak var lonCoordLabelOnMap: UILabel!
    
    override func viewDidLoad() {
        // Вариант 1: указывает, что этот класс будет получать уведомления по протоколу
        MapViewController.shared?.delegate = self
    }
    
    
    // вариант 2: запрашиваем координаты
    @IBAction func getCoorginateAction() {
        guard let coordinate = MapViewController.shared?.getCoordinate() else {
            return
        }
        latCoordLabelOnMap.text = String(coordinate.lat)
        lonCoordLabelOnMap.text = String(coordinate.lon)
    }
}

extension TwoViewController: MapViewControllerDelegate {
    
    // Вариант 1: получаем уведомление с делегата по протоколу
    func getCoordinate(lat: Double, lon: Double) {
        latCoordLabelOnMap.text = String(lat)
        lonCoordLabelOnMap.text = String(lon)
    }
}
→ Ссылка
Автор решения: Pavel Lazarev

Попробуйте этот вариант. Здесь два вью контроллера, второй контроллер получает данные в таймере каждую секунду.

class MapViewController: UIViewController, CLLocationManagerDelegate {

    let userDefMap = UserDefaults.standard
    @IBOutlet weak var map: MKMapView!
    @IBOutlet weak var latCoordLabelOnMap: UILabel!
    @IBOutlet weak var lonCoordLabelOnMap: UILabel!
        
    // статическая переменная класса дает доступ к классу
    static var shared: MapViewController?
        
    var locationManager: CLLocationManager?

    override func viewDidLoad() {
        super.viewDidLoad()
            
        // инициализируем shared
        MapViewController.shared = self
            
        locationManager = CLLocationManager()
        locationManager?.requestWhenInUseAuthorization()
        locationManager?.delegate = self
        locationManager?.desiredAccuracy = kCLLocationAccuracyBest
        locationManager?.activityType = .fitness
        locationManager?.startUpdatingLocation()
            
        if let coordinate = locationManager?.location?.coordinate {
                
            let xScale: CLLocationDegrees = 0.01
            let yScale: CLLocationDegrees = 0.01
                
            let span: MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: yScale, longitudeDelta: xScale)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            
            map.setRegion(region, animated: true)
        }

        map.userTrackingMode = .followWithHeading
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        // метки на карте c координатами:
        latCoordLabelOnMap.text = String((locations[0].coordinate.latitude))
        lonCoordLabelOnMap.text = String((locations[0].coordinate.longitude))
    }
        
    override func viewDidDisappear(_ animated: Bool) {
        locationManager?.stopUpdatingLocation()
    }
        
    // по запросу обращаемся к locationManager для получения координат
    func getCoordinate() -> (lat: Double, lon: Double)? {
        guard let coordinate = locationManager?.location?.coordinate else {
            return nil
        }
        return (lat: coordinate.latitude, lon: coordinate.longitude)
    }
}

class TwoViewController: UIViewController {
        
    @IBOutlet weak var latCoordLabelOnMap: UILabel!
    @IBOutlet weak var lonCoordLabelOnMap: UILabel!
                
    override func viewDidLoad() {
        // Timer будет работать через каждую секунду
        let getCoordinateTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { (timer) in
            // запрос на координаты из MapViewController
            guard let coordinate = MapViewController.shared?.getCoordinate() else {
                return
            }
            self.latCoordLabelOnMap.text = String(coordinate.lat)
            self.lonCoordLabelOnMap.text = String(coordinate.lon)
        }
        // Запуск таймера
        getCoordinateTimer.fire()
    }
}

→ Ссылка