Как правильно добавить кнопку «Показать местоположение пользователя» для MKMapView в SwiftUI? - PullRequest
0 голосов
/ 27 сентября 2019

Я пытаюсь создать представление SwiftUI с MKMapView и кнопкой, чтобы перейти к местоположению пользователя.Ниже приведен весь мой код.Я создал представление с именем MapView, которое соответствует UIViewRepresentable для хранения моего MKMapView.У меня есть кнопка местоположения, которая при нажатии устанавливает shouldNavigateToUserLocation в значение true.Это приводит к перезагрузке пользовательского интерфейса, и мой MapView перемещается в местоположение пользователя, если значение shouldNavigateToUserLocation равно true.Затем он устанавливает shouldNavigateToUserLocation в значение false, чтобы MapView не постоянно перемещался в местоположение пользователя при любых изменениях состояния.

Этот подход работает при работе на реальных устройствах, но я получаю предупреждение «Изменение состояния во время обновления представления, это приведет к неопределенному поведению».на линии 87, которая shouldNavigateToUserLocation = false.Это понятно, но мой вопрос, как я могу избежать этого?Кажется, я не могу найти способ реструктурировать свой код, чтобы не нарушать правило не изменять состояние во время обновления представления, в то время как карта по-прежнему перемещается к местоположению пользователя, когда и только когда пользователь нажимает кнопку.

Я испробовал несколько разных подходов, но в основном я застрял с проблемой того, что ни у моего MapView, ни у моего класса Controller фактически нет прямого доступа к MKMapView.Я понимаю, почему это происходит в SwiftUI, но это действительно ограничивает то, что я могу сделать.

Вот весь мой код:

import SwiftUI
import MapKit

struct ContentView: View {

  @State var currentlyDisplayingLocationAuthorizationRequest = false
  @State var shouldNavigateToUserLocation = false

  let locationManager = CLLocationManager()

  var body: some View {
    ZStack {
      MapView(currentlyDisplayingLocationAuthorizationRequest: $currentlyDisplayingLocationAuthorizationRequest,
              shouldNavigateToUserLocation: $shouldNavigateToUserLocation)

      HStack {
        Spacer()
        VStack {
          Spacer()
          Button(action: {
            self.checkForLocationAuthorizationAndNavigateToUserLocation()
          }) {
            Image(systemName: "location")
              .imageScale(.large)
              .accessibility(label: Text("Locate Me"))
              .padding()
          }
          .background(Color.gray)
          .cornerRadius(10)
          .padding()
        }
      }
    }
    .onAppear {
      self.shouldNavigateToUserLocation = true
    }
  }

  func checkForLocationAuthorizationAndNavigateToUserLocation() {
    currentlyDisplayingLocationAuthorizationRequest = false

    if CLLocationManager.authorizationStatus() == .notDetermined {
      print("location authorization not determined")
      currentlyDisplayingLocationAuthorizationRequest = true
      locationManager.requestWhenInUseAuthorization()
      return
    }

    shouldNavigateToUserLocation = true
  }

}

struct MapView: UIViewRepresentable {

  @Binding var currentlyDisplayingLocationAuthorizationRequest: Bool
  @Binding var shouldNavigateToUserLocation: Bool

  let locationManager = CLLocationManager()

  func makeUIView(context: Context) -> MKMapView {
    let map = MKMapView()
    map.delegate = context.coordinator
    map.showsUserLocation = true
    return map
  }

  func updateUIView(_ uiView: MKMapView, context: Context) {
    if !currentlyDisplayingLocationAuthorizationRequest && shouldNavigateToUserLocation {
      moveToUserLocation(map: uiView)
    }
  }

  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }

  // MARK: Location -

  private func moveToUserLocation(map: MKMapView) {
    guard let location = locationManager.location else { return }

    let region = MKCoordinateRegion(center: location.coordinate,
                                    span: MKCoordinateSpan(latitudeDelta: 0.02,
                                                           longitudeDelta: 0.02))
    map.setRegion(region, animated: true)
    shouldNavigateToUserLocation = false
  }

  // MARK: Coordinator -

  final class Coordinator: NSObject, MKMapViewDelegate {

    var control: MapView

    init(_ control: MapView) {
      self.control = control
    }

  }

}
...