SwiftUI Map 导致“视图更新期间修改状态”

Car*_*arl 9 runtime-error mapkit swiftui

我想实现一个基本的地图视图,当用户点击按钮时,该视图将以用户位置为中心,类似于 Apple 地图应用程序。我尝试了以下操作,但每当我点击按钮时,[SwiftUI] Modifying state during view update, this will cause undefined behavior.都会在控制台中打印。在我看来,更新tracking状态变量导致了错误。但是,我不确定状态变量还有什么用途。尽管打印了错误,但应用程序确实按预期运行。有谁有这方面的经验或知道可能出了什么问题?

struct ContentView: View {
    @State var region: MKCoordinateRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 47.3769, longitude: 8.5417), latitudinalMeters: 2000, longitudinalMeters: 2000)
    @State var tracking = MapUserTrackingMode.follow
    
    var body: some View {
        ZStack {
            Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true, userTrackingMode: $tracking)
                .ignoresSafeArea()
                .task {
                    let locationManager = CLLocationManager()
                    locationManager.requestWhenInUseAuthorization();
                }
            Button {
                tracking = .follow
            } label: {
                Image(systemName: tracking == .follow ? "location.fill" : "location")
                    .padding()
            }
            .background(.white)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

mal*_*hal 10

2023 年 7 月 27 日编辑:

  • Xcode 15.0 beta 5 (15A5209g) 和 iOS 17 beta 中仍然出现错误
  • iOS 17 中已弃用 MapUserTrackingMode
  • FB9990674 更新
    • 近期类似报道:10余起。
    • 解决方案:为未来操作系统更新确定潜在的修复程序。
  • 可以使用新的 MapCameraPosition API 进行解决(可能在测试期间发生变化),例如
struct MapTestView3: View {
   
    //@State var region: MKCoordinateRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 47.3769, longitude: 8.5417), latitudinalMeters: 2000, longitudinalMeters: 2000)
    //@State var tracking = MapUserTrackingMode.follow
    
    static let fallback = MapCameraPosition.region(MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 47.3769, longitude: 8.5417), latitudinalMeters: 2000, longitudinalMeters: 2000))
    @State var position = MapCameraPosition.userLocation(fallback: Self.fallback)
    
    var body: some View {
        //let _ = Self._printChanges()
        VStack {
            Map(position: $position)
            //Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true, userTrackingMode: $tracking)
                .ignoresSafeArea()
                .onAppear {
                    let locationManager = CLLocationManager()
                    locationManager.requestWhenInUseAuthorization();
                }
            Button {
                //tracking = .follow
                position = MapCameraPosition.userLocation(fallback: Self.fallback)
            } label: {
                
                Image(systemName: position.followsUserLocation ? "location.fill" : "location")
                    .padding()
            }
            .background(.white)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

原来的:

在我看来,这是一个错误Map(从 Xcode 版本 13.3.1 (13E500a) 和 iPhone 13 模拟器开始)。如果您切换到“断点”侧栏并单击 + 并添加“所有运行时问题”断点,如果您调试并单击该按钮,您将遇到断点并看到以下内容:

在此输入图像描述

此跟踪显示,当点击按钮来更改跟踪状态时,SwiftUIMKMapView通过调用(第 13 行)更新新状态 _setUserTrackingMode,但这样做的副作用是回调mapLayerDidChangeVisibleRegion(第 9 行),并且它尝试设置 a 的值绑定(第 6 行),很可能是coordinateRegion. 它不应该在从状态更新 MKMapView 时设置 Binding,这就是导致警告的原因。我们都应该报告这个错误 - 我在开发者工具 - SwiftUI 下将其提交为 FB9990674,请随时引用我的号码。


fco*_*llf 10

当我迁移到 iOS 16 (Xcode 14.1) 时,我开始遇到类似的问题。就我而言,我有以下情况:

// LocationView

struct LocationView : View {

    @StateObject private var viewModel : ViewModel

    var body: some View {

        Map(coordinateRegion: $viewModel.region)
            .clipShape(RoundedRectangle(cornerRadius: 10))
    }
}


// View Model

@MainActor final class ViewModel : ObservableObject {

    ...

    @Published var region : MKCoordinateRegion

    ...
}
Run Code Online (Sandbox Code Playgroud)

Binding在本例中,我为删除警告所做的操作是在以下位置View的 Published 属性中创建一个viewModel

private var region : Binding<MKCoordinateRegion> {
        
    Binding {
            
        viewModel.region
            
    } set: { region in
            
        DispatchQueue.main.async {
            viewModel.region = region
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

然后更改视图以使用此绑定而不是以下中的已发布变量viewModel

    Map(coordinateRegion: region)
Run Code Online (Sandbox Code Playgroud)

应用程序继续按预期工作,并且错误/警告不再显示。