【发布时间】:2019-09-27 02:07:55
【问题描述】:
我正在尝试使用 MKMapView 和一个按钮来创建一个 SwiftUI 视图以导航到用户位置。以下是我的全部代码。我创建了一个名为MapView 的视图,它符合UIViewRepresentable 来保存我的MKMapView。我有一个位置按钮,点击时将shouldNavigateToUserLocation 设置为true。这会导致 UI 重新加载,如果 shouldNavigateToUserLocation 为真,我的 MapView 会导航到用户位置。然后,它将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
}
}
}
【问题讨论】:
-
我在另一个帖子中回答了这个问题:stackoverflow.com/questions/58398437/…,或者你可以参考我的github中的演示项目:github.com/zhihuitang/UserLocationDemo