【问题标题】:Show user location on map SwiftUI在地图 SwiftUI 上显示用户位置
【发布时间】:2019-10-26 10:56:36
【问题描述】:

我正在尝试加载地图并在用户位置上显示初始位置,并使用 SwiftUI 在地图上显示用户的位置。我不知道如何在 SwiftUI 中做到这一点。

我尝试将“view.showsUserLocation = true”放入 updateUIView 函数中,但它不起作用。

import SwiftUI
import MapKit
struct MapView : UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }

    func updateUIView(_ view: MKMapView, context: Context) {
        view.showsUserLocation = true
        let coordinate = CLLocationCoordinate2D(
            latitude: 34.011286, longitude: -116.166868)
        let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)

        let region = MKCoordinateRegion(center: view.userLocation.location?.coordinate ?? coordinate, span: span)
        view.setRegion(region, animated: true)
    }
}

当我尝试打印时,位置为零。

【问题讨论】:

  • 你试过在真机上测试这个吗?当涉及到Core Location 时,模拟器可能很繁琐——还要确保当前方案允许位置模拟:Current Scheme -> Edit -> Run -> Options ->Allow Location Simulation
  • 是的,我已经这样做了,并确保我输入了一个位置。我还在移动设备上进行了测试。我还在 info.plist 中添加了权限

标签: swift mapkit swiftui


【解决方案1】:

Info.plist中添加键Privacy - Location When In Use Usage Description和描述

然后尝试更新以下功能:

func updateUIView(_ view: MKMapView, context: Context) {

    view.showsUserLocation = true

    // Ask for Authorisation from the User.
    self.locationManager.requestAlwaysAuthorization()

    // For use in foreground
    self.locationManager.requestWhenInUseAuthorization()

    if CLLocationManager.locationServicesEnabled() {
        //        self.locationManager.delegate = self
         self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
         self.locationManager.startUpdatingLocation()

        //Temporary fix: App crashes as it may execute before getting users current location
        //Try to run on device without DispatchQueue

        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
            let locValue:CLLocationCoordinate2D = self.locationManager.location!.coordinate
            print("CURRENT LOCATION = \(locValue.latitude) \(locValue.longitude)")

            let coordinate = CLLocationCoordinate2D(
                latitude: locValue.latitude, longitude: locValue.longitude)
            let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
            let region = MKCoordinateRegion(center: coordinate, span: span)
            view.setRegion(region, animated: true)

        })
    }

}

【讨论】:

  • 谢谢哈沙尔!这很好用。但是我只能在调度下运行它。我不确定这是否是一个好习惯。
  • 谢谢哈沙尔。我注意到如果我将它放在 makeUIView 方法而不是 updateUIView 中,您的代码也可以工作,因此它只在创建期间执行一次,而不是继续以用户的位置为中心。谢谢你的帖子!!!
【解决方案2】:

这是我发现的最简洁的设置 CoreLocation 和 MapView 的方法。首先,您必须创建一个 UIViewRepresentable 才能在 SwiftUI 中使用 CoreLocation。

不要忘记在您的 Info.plist 文件中启用此隐私并显示一条消息:

隐私 - 始终定位和使用时使用说明

隐私 - 使用时的位置使用说明

隐私 - 位置始终使用说明

import SwiftUI
import MapKit

// MARK: Struct that handle the map
struct MapView: UIViewRepresentable {

  @Binding var locationManager: CLLocationManager
  @Binding var showMapAlert: Bool

  let map = MKMapView()

  ///Creating map view at startup
  func makeUIView(context: Context) -> MKMapView {
    locationManager.delegate = context.coordinator
    return map
  }

  func updateUIView(_ view: MKMapView, context: Context) {
    map.showsUserLocation = true
    map.userTrackingMode = .follow
  }

  ///Use class Coordinator method
  func makeCoordinator() -> MapView.Coordinator {
    return Coordinator(mapView: self)
  }

  //MARK: - Core Location manager delegate
  class Coordinator: NSObject, CLLocationManagerDelegate {

    var mapView: MapView

    init(mapView: MapView) {
      self.mapView = mapView
    }

    ///Switch between user location status
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
      switch status {
      case .restricted:
        break
      case .denied:
        mapView.showMapAlert.toggle()
        return
      case .notDetermined:
        mapView.locationManager.requestWhenInUseAuthorization()
        return
      case .authorizedWhenInUse:
        return
      case .authorizedAlways:
        mapView.locationManager.allowsBackgroundLocationUpdates = true
        mapView.locationManager.pausesLocationUpdatesAutomatically = false
        return
      @unknown default:
        break
      }
      mapView.locationManager.startUpdatingLocation()
    }
   }
  }
 }

这是我在 SwiftUI 视图中使用它的方式。如果 Coordinator 类开关中的权限被拒绝,则会触发警报:

import SwiftUI
import CoreLocation

// MARK: View that shows map to users
struct HomeView: View {

  @State var locationManager = CLLocationManager()
  @State var showMapAlert = false

  var body: some View {
    MapView(locationManager: $locationManager, showMapAlert: $showMapAlert)
        .alert(isPresented: $showMapAlert) {
          Alert(title: Text("Location access denied"),
                message: Text("Your location is needed"),
                primaryButton: .cancel(),
                secondaryButton: .default(Text("Settings"),
                                          action: { self.goToDeviceSettings() }))
    }
  }
}

extension HomeView {
  ///Path to device settings if location is disabled
  func goToDeviceSettings() {
    guard let url = URL.init(string: UIApplication.openSettingsURLString) else { return }
    UIApplication.shared.open(url, options: [:], completionHandler: nil)
  }
}

【讨论】:

    【解决方案3】:

    以上代码会因为权限请求不当导致应用崩溃。使用以下代码以获得更好的性能和准时许可。

    struct MapView: UIViewRepresentable {
        let locationManager = CLLocationManager()        
        
        func makeUIView(context: Context) -> MKMapView {
            MKMapView(frame: .zero)
        }
        
        func updateUIView(_ view: MKMapView, context: Context) {
            view.showsUserLocation = true
            let status = CLLocationManager.authorizationStatus()
            locationManager.requestAlwaysAuthorization()
            locationManager.requestWhenInUseAuthorization()
            
            if status == .authorizedAlways || status == .authorizedWhenInUse {
                locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
                locationManager.startUpdatingLocation()
                let location: CLLocationCoordinate2D = locationManager.location!.coordinate
                let span = MKCoordinateSpan(latitudeDelta: 0.009, longitudeDelta: 0.009)
                let region = MKCoordinateRegion(center: location, span: span)
                view.setRegion(region, animated: true)
            }
        }
        
    }
    

    【讨论】:

    • 在 updateUIView 函数中定义 locationManager 时,对话框很快消失。
    • 效果很好,但地图不会保持在标记的中心。有没有办法让标记始终居中?
    • @VolkanPaksoy 听起来这个解决方案有这个问题,因为这里的 CLLocationManager 实例是在方法级别实例化的,而不是在生命周期较长的范围内(如类本身)。当该方法结束时,不再需要 locationManager 引用,并且 locationManager 实例被引用计数器清除。本期回答了类似的问题:stackoverflow.com/questions/7888896/…
    • @VolkanPaksoy 通过更改 locationManager 的范围解决的问题
    【解决方案4】:

    试试这个:

    struct OriginMap : UIViewRepresentable {
    
    
    
    
     func makeUIView(context: UIViewRepresentableContext<OriginMap>) -> MKMapView{
        MKMapView()
    }
    
    
    
    
     func updateUIView(_ mapView: MKMapView, context: Context) {
        let locationManager = CLLocationManager()
    
    
        mapView.showsUserLocation = true
    
        locationManager.requestAlwaysAuthorization()
    
        locationManager.requestWhenInUseAuthorization()
    
        if CLLocationManager.locationServicesEnabled() {
            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.startUpdatingLocation()
    
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
                let locValue:CLLocationCoordinate2D = locationManager.location!.coordinate
    
    
                let coordinate = CLLocationCoordinate2D(
                    latitude: locValue.latitude, longitude: locValue.longitude)
                Datas.currentLocation = coordinate
    
    
                let span = MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002)
                let region = MKCoordinateRegion(center: coordinate, span: span)
                mapView.setRegion(region, animated: true)
    
    
            })
        }}
    

    【讨论】:

    • 这条线对我不起作用:Datas.currentLocation = 坐标这里的“数据”是什么?
    【解决方案5】:

    这是 SwiftUI2 的代码

    import MapKit
    import SwiftUI
    
    struct MapView: View {
        @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.4353, longitude: 35.23442), span: MKCoordinateSpan(latitudeDelta: 0.15, longitudeDelta: 0.15))
        var body: some View {
            Map(coordinateRegion: $region)
        }
    }
    
    struct MapView_Previews: PreviewProvider {
        static var previews: some View {
            MapView()
        }
    }
    

    您也可以查看URL,了解有关 SwiftUI2 添加的其他有用功能的更多信息。

    【讨论】:

      【解决方案6】:
      import SwiftUI
      import MapKit
      
      struct ContentView: View {
          @State private var region = MKCoordinateRegion(
              center: CLLocationCoordinate2D(latitude: -23.506403, longitude: -46.1509555),
              span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
      
          var body: some View {
              Map(coordinateRegion: $region, showsUserLocation: true)
          }
      }
      

      【讨论】:

        【解决方案7】:

        适用于 XCode 12

        import SwiftUI
        import MapKit
        
        struct ContentView: View {
            
            @State private var region = MKCoordinateRegion(
                center: CLLocationCoordinate2D(latitude: 37.321127, longitude: -122.047790),
                span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
            
            var body: some View {
                Map(coordinateRegion: $region)
            }
        }
        

        【讨论】:

        • 问题在于显示用户位置,而不仅仅是地图
        【解决方案8】:

        使用它你可以找到用户区域

        Text(Locale.current.localizedString(forRegionCode: Locale.current.regionCode!) ?? "")
                            .font(.system(size: 30))
                            .fontWeight(.bold)
        

        【讨论】:

        • 不,你不能。这并没有解决 OP 的问题
        猜你喜欢
        • 2023-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-24
        • 1970-01-01
        相关资源
        最近更新 更多