SwiftUI mapkit 将区域设置为用户的当前位置 [英] SwiftUI mapkit set region to user's current location

查看:46
本文介绍了SwiftUI mapkit 将区域设置为用户的当前位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是 Xcode 12.

I am using Xcode 12.

我能够显示带有区域但带有硬编码值的地图.

I am able to show a map with a region, but with hard-coded values.

相反,我想根据用户的当前位置设置地图区域.

Instead, I want to set the region of the map based on the user's current location.

我有一个 LocationManager 类,它获取用户的位置并发布它.

I have a LocationManager class, which gets the user's location and publish it.

我有一个 ShowMapView SwiftUI 视图,它根据 LocationManager 类观察对象以获取用户的位置.但是,我不知道如何使用 locationManager 对象中的数据来设置地图使用的区域.

I have a ShowMapView SwiftUI View that observes an object based on the LocationManager class to get the user's location. But, I don't know how to use the data from the locationManager object to set the region used by the map.

这里是 LocationManager 类,它获取用户的位置并发布它.

Here is the LocationManager class, which gets the user's location and publishes it.

import Foundation
import MapKit

final class LocationManager: NSObject, ObservableObject {
  @Published var location: CLLocation?
  
  private let locationManager = CLLocationManager()
   
  override init() {
    super.init()
    
    self.locationManager.delegate = self
    
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
    self.locationManager.distanceFilter = kCLDistanceFilterNone
    self.locationManager.requestWhenInUseAuthorization()
    self.locationManager.startUpdatingLocation()
  }
}

extension LocationManager: CLLocationManagerDelegate {
  func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let location = locations.last {
      self.location = location
    }
  }
}

这里是ShowMapView SwiftUI View,需要获取用户发布的位置并设置地图使用的区域.如您所见,这些值目前是硬编码的.

Here is the ShowMapView SwiftUI View, which needs to get the user's location that's published and set the region used by the map. As you can see, the values are hard-coded for now.

import Combine
import MapKit
import SwiftUI

struct AnnotationItem: Identifiable {
    let id = UUID()
    let name: String
    let coordinate: CLLocationCoordinate2D
}

struct ShowMapView: View {
  @ObservedObject private var locationManager = LocationManager()
  
  @State private var region = MKCoordinateRegion(
    center: CLLocationCoordinate2D(latitude: 38.898150, longitude: -77.034340),
    span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
  )

  var body: some View {
            Map(coordinateRegion: $region, annotationItems: [AnnotationItem(name: "Home", coordinate: CLLocationCoordinate2D(latitude: self.locationManager.location!.coordinate.latitude, longitude: self.locationManager.location!.coordinate.longitude))]) {
               MapPin(coordinate: $0.coordinate)
            }
            .frame(height: 300)
  }
}

推荐答案

这是一个可能的解决方案:

Here's one possible solution to this:

final class LocationManager: NSObject, ObservableObject {
    @Published var location: CLLocation?
    @Published var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 38.898150, longitude: -77.034340),
        span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
    )
    private var hasSetRegion = false
    
    private let locationManager = CLLocationManager()
    
    override init() {
        super.init()
        
        self.locationManager.delegate = self
        
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.distanceFilter = kCLDistanceFilterNone
        self.locationManager.requestWhenInUseAuthorization()
        self.locationManager.startUpdatingLocation()
    }
}

extension LocationManager: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            self.location = location
            
            if !hasSetRegion {
                self.region = MKCoordinateRegion(center: location.coordinate,
                                                 span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
                hasSetRegion = true
            }
        }
    }
}

struct ShowMapView: View {
    @ObservedObject private var locationManager = LocationManager()
    
    var homeLocation : [AnnotationItem] {
        guard let location = locationManager.location?.coordinate else {
            return []
        }
        return [.init(name: "Home", coordinate: location)]
    }
    
    var body: some View {
        Map(coordinateRegion: $locationManager.region, annotationItems: homeLocation) {
            MapPin(coordinate: $0.coordinate)
        }
        .frame(height: 300)
    }
}

在此解决方案中,区域由位置管理器发布.一旦接收到位置,该区域就会以该位置为中心(在 didUpdateLocations 中).然后,设置一个布尔标志,表示该区域最初已居中.设置该布尔值后,它不再更新区域.这将让用户仍然拖动/缩放等.

In this solution, the region is published by the location manager. As soon as a location is received, the region is centered on that spot (in didUpdateLocations). Then, a boolean flag is set saying the region has been centered initially. After that boolean is set, it no longer updates the region. This will let the user still drag/zoom, etc.

我还稍微更改了您的代码以放下别针.您正在强制解包 location,在位置管理器设置第一个位置之前该位置为零,从而导致崩溃.在我的编辑中,如果还没有位置,它只会返回一个空的注释项数组.

I also changed your code for putting down the pin a little bit. You were force-unwrapping location, which is nil until the first location is set by the location manager, causing a crash. In my edit, it just returns an empty array of annotation items if there isn't a location yet.

这篇关于SwiftUI mapkit 将区域设置为用户的当前位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆