#ios #swift #cocoa-touch #uiviewcontroller #iboutlet
#iOS #swift #cocoa-touch #uiviewcontroller #iboutlet
Вопрос:
В настоящее время я создаю приложение для отслеживания маршрутов, чтобы научить себя быстрому. У меня была базовая функциональность, работающая с отслеживаемым маршрутом и полилиниями, которые рисовались из одного класса SessionController, но я хотел разделить класс на отдельные объекты.
Вот тут у меня возникли проблемы. Я поместил весь код, который обновляет местоположение и рисует полилинии и т.д., В его собственный класс под названием MapView.swift и оставил @IBOutlets для кнопок, MapView и т.д. В SessionController.swift но теперь я не могу получить доступ к @IBOutlet для MapView, чтобы разрешить новому классу MapView.swift обновлять текущее местоположение, полилинии обновления и т.д.
Когда я пытаюсь ctrl перетащить MapView @IBOutlet в MapView.swift ViewController, ничего не происходит.
Итак, я спрашиваю, как мне связать два класса, чтобы разрешить доступ к MapView в SessionController для обновления текущего местоположения.
Я просматриваю протоколы и делегатов, но я не слишком уверен, как их реализовать, или даже если это правильный путь.
Код для SessionController можно увидеть ниже:
class SessionController: UIViewController
{
// Creates an outlet link to the corrosponding interfaces on the storyBoard
@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var stopButton: UIButton!
var mapScreen: MapView! = nil
// Had to change from viewOnLoad so that the custom alert class could be utilised.
override func viewDidAppear(_ animated: Bool) {
super.viewDidLoad()
startButton.layer.cornerRadius = startButton.frame.size.height/2
stopButton.layer.cornerRadius = stopButton.frame.size.height/2
mapScreen = (storyboard?.instantiateViewController(withIdentifier: "mapScreen") as! MapView)
// does the initial check whether the location services are enabled
mapScreen.checkLocationServices()
}
// ends the current session, resets buttons, stops updating location and changes to history tab.
func endSession() {
stopButton.isHidden = true
startButton.isHidden = false
mapScreen.stopUpdatingLocation()
}
// action for when the start button is presssed.
@IBAction func startButtonPressed(_ sender: Any) {
mapScreen!.startUpdatingLocation()
startButton.isHidden = true
stopButton.isHidden = false
}
// action for when the stop button is pressed.
@IBAction func stopButtonPressed(_ sender: Any) {
presentAlertWithTitle(title: "End Session?", message: "Do you wish to end your session?", options: "Cancel", "Yes") { (option) in
switch(option) {
case 0:
break
case 1:
self.endSession()
default:
break
}
}
}
}
Код для MapView можно увидеть ниже:
class MapView: UIViewController
{
let locationManager = CLLocationManager()
var myLocations: [CLLocation] = []
let regionInMeters: Double = 500
var mapView: MKMapView!
// Checks the users location services are enabled otherwise give them an alert.
func checkLocationServices() {
if CLLocationManager.locationServicesEnabled() {
setupLocationManager()
checkLocationAuthorisation()
} else {
// show an alert instructing on how to enable location services.
presentAlertWithTitle(title: "Location Services Disabled", message: "Please enable your location services by navigating to Settings/Privacy/Location Services and turning on.", options: "OK") { _ in }
}
}
// Setup the location manager
func setupLocationManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
// Checks whether the user has authorised location tracking via permissions.
func checkLocationAuthorisation() {
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
centerViewOnUserLocation()
case .denied:
// show alert detailing that the user has denied access to the location services.
presentAlertWithTitle(title: "Access Denied", message: "Request of access has been denied to the location services", options: "OK") { _ in }
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
// show an alert instructing that the location services are restricted i.e. child account
presentAlertWithTitle(title: "Access Restricted", message: "Access has been restricted to the location services", options: "OK") { _ in }
break
case .authorizedAlways:
break
}
}
// Centers the position onto the at the specified height by using the regionInMeters variable
func centerViewOnUserLocation() {
if let location = locationManager.location?.coordinate {
let region = MKCoordinateRegion.init(center: location, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
}
}
func updatePolyLine() {
if (myLocations.count > 1){
// get my old location
let sourceIndex = myLocations.count - 1
// get the new location
let destinationIndex = myLocations.count - 2
// get the coordinates for both the old and the new locations
let sourceIndexCoordinate = myLocations[sourceIndex].coordinate
let destinationIndexCoordinate = myLocations[destinationIndex].coordinate
// put these coordinates in to a new array so that we can get a reference to a pointer so that the MKPolyline can make use of it's position in the registry.
var sourcePlusDestination = [sourceIndexCoordinate, destinationIndexCoordinate]
// pass the reference of the array pointer into the constructor of MKPolyline so that a line can be drawn between the two points.
let polyline = MKPolyline(coordinates: amp;sourcePlusDestination, count: sourcePlusDestination.count)
// adds and then updates the polyline on the map.
mapView.addOverlay(polyline)
}
}
func startUpdatingLocation()
{
locationManager.startUpdatingLocation()
}
func stopUpdatingLocation()
{
locationManager.stopUpdatingLocation()
tabBarController?.selectedIndex = 1
for overlay in mapView.overlays {
mapView.removeOverlay(overlay)
}
}
}
// an extension for SessionController that uses delegates to listen for changes in the location and authorisation
extension MapView: CLLocationManagerDelegate
{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
guard let location = locations.last else { return }
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion.init(center: center, latitudinalMeters: regionInMeters, longitudinalMeters: regionInMeters)
mapView.setRegion(region, animated: true)
myLocations.append(locations[0] as CLLocation)
updatePolyLine()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)
{
checkLocationAuthorisation()
}
}
extension MapView: MKMapViewDelegate
{
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
if overlay is MKPolyline
{
let polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.strokeColor = UIColor.blue
polylineRenderer.lineWidth = 4
return polylineRenderer
}
return MKPolygonRenderer()
}
}
Комментарии:
1. Неясно, о чем вы спрашиваете.
2. Ваша идея протокола / делегирования кажется разумной, за исключением: почему ваш
MapView
класс должен бытьUIViewController
?3. Честно говоря, я не слишком уверен! Какой была бы рекомендация?
Ответ №1:
Я немного сбит с толку вашим кодом и вопросом, но, надеюсь, это может направить вас по правильному пути…
Вы создаете свой MapScreen здесь:
mapScreen = (storyboard?.instantiateViewController(withIdentifier: "mapScreen") as! MapView)
но никогда не устанавливайте это mapView
свойство таким образом:
mapScreen = (storyboard?.instantiateViewController(withIdentifier: "mapScreen") as! MapView)
mapScreen.mapView = mapView
Также похоже, что вы также хотите установить делегат вашего MKMapView
для MapScreen, учитывая, что вы его реализовали ( extension MapView: MKMapViewDelegate
)
например
mapScreen = (storyboard?.instantiateViewController(withIdentifier: "mapScreen") as! MapView)
mapScreen.mapView = mapView
mapView.delegate = mapScreen
Кроме того, я думаю, вам нужна слабая ссылка на MKMapView
inside MapView
.
Я также не вижу, как вы представляете mapScreen
, вы, кажется, создаете его экземпляр, но на самом деле не представляете его. Я думаю, что скриншот вашей раскадровки или еще какой-то части вашего кода может помочь.
Комментарии:
1. Хотя это останавливает выдачу любых исключений, местоположение не обновляется. Начальный центр в положении пользователя работает, но не обновляется при перемещении
2. @Stimo кое-что добавил к моему ответу. Я не вижу, где представлен ваш
mapScreen