gpt4 book ai didi

swiftui - UIViewRepresentable : "Modifying state during view update, this will cause undefined behavior"

转载 作者:行者123 更新时间:2023-12-04 04:18:45 27 4
gpt4 key购买 nike

我从 MKMapView 制作了一个简单的 UIViewRepresentable。您可以滚动 map View ,屏幕将使用中间的坐标进行更新。

这是内容 View :

import SwiftUI
import CoreLocation

let london = CLLocationCoordinate2D(latitude: 51.50722, longitude: -0.1275)

struct ContentView: View {
@State private var center = london

var body: some View {
VStack {
MapView(center: self.$center)
HStack {
VStack {
Text(String(format: "Lat: %.4f", self.center.latitude))
Text(String(format: "Long: %.4f", self.center.longitude))
}
Spacer()
Button("Reset") {
self.center = london
}
}.padding(.horizontal)
}
}
}

这是 map View :
struct MapView: UIViewRepresentable {
@Binding var center: CLLocationCoordinate2D

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

func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.centerCoordinate = self.center
}

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

class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView

func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
parent.center = mapView.centerCoordinate
}

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

点击重置按钮应该简单地将 mapView.center 设置为 london。当前的方法会使 map 滚动超慢,并且当点击按钮时,会导致错误“ View 更新期间修改状态,这将导致未定义的行为”。

重置坐标应该如何传达给MKMapView,让 map 滚动又快又修复错误?

最佳答案

上述带有 ObservedObject 的解决方案将不起作用。虽然您不会再看到警告消息,但问题仍然存在。 Xcode 只是无法再警告您它正在​​发生。
ObservableObjects 中发布的属性的行为与@State 和@Binding 几乎相同。也就是说,他们随时触发 View 更新 objectWillUpdate发布者被触发。当@Published 属性更新时,这会自动发生。您也可以使用 objectWillChange.send() 自己手动触发它
因此,可以创建不会自动导致 View 状态更新的属性。我们可以利用这一点来防止 UIViewRepresentable 和 UIViewControllerRepresentable 结构的不必要的状态更新。
这是一个在您从 MKMapViewDelegate 方法更新其 View 模型时不会循环的实现:

struct MapView: UIViewRepresentable {

@ObservedObject var viewModel: Self.ViewModel

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

func updateUIView(_ uiView: MKMapView, context: Context) {
// Stop update loop when delegate methods update state.
guard viewModel.shouldUpdateView else {
viewModel.shouldUpdateView = true
return
}

uiView.centerCoordinate = viewModel.centralCoordinate
}

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

class Coordinator: NSObject, MKMapViewDelegate {
private var parent: MapView

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

func mapViewDidChangeVisibleRegion(_ mapView: MKMapView){
// Prevent the below viewModel update from calling itself endlessly.
parent.viewModel.shouldUpdateView = false
parent.viewModel.centralCoordinate = mapView.centerCoordinate
}
}

class ViewModel: ObservableObject {
@Published var centerCoordinate: CLLocationCoordinate2D = .init(latitude: 0, longitude: 0)
var shouldUpdateView: Bool = true
}
}
如果你真的不想使用 ObservableObject,另一种方法是把 shouldUpdateView属性(property)进入您的协调员。尽管我仍然更喜欢使用 viewModel,因为它可以让您的 UIViewRepresentable 不受多个 @Bindings 的影响。您还可以在外部使用 ViewModel 并通过组合收听它。
老实说,我很惊讶苹果在创建 UIViewRepresentable 时没有考虑这个确切的问题。
如果您需要保持 SwiftUI 状态与 View 更改同步,几乎所有 UIKit View 都会有这个确切的问题。

关于swiftui - UIViewRepresentable : "Modifying state during view update, this will cause undefined behavior",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59981448/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com