SwiftUI,不能从委托回调中更改状态? (附代码)

Posted

技术标签:

【中文标题】SwiftUI,不能从委托回调中更改状态? (附代码)【英文标题】:SwiftUI, can't change state from within delegate callback? (code attached) 【发布时间】:2021-09-14 11:28:22 【问题描述】:

任何人都能发现为什么当我在“updateUIView”回调中设置“firstPass”状态变量时它没有设置状态?作为输出我看到:

First Pass1:  true
First Pass2:  true. // <= not set to false as expected

我也没有在 Xcode 中设置这个状态“firstPass = false”,这里有一个 Xcode 警告:“在视图更新期间修改状态,这将导致未定义的行为。”

import SwiftUI
import MapKit

struct GCMapView 
    @State var firstPass : Bool = true
   
    func makeCoordinator() -> Coordinator 
        return Coordinator(self)
    
    class Coordinator: NSObject, MKMapViewDelegate 
        var parent: GCMapView
        init(_ parent: GCMapView) 
            self.parent = parent
            super.init()
        
    


extension GCMapView : UIViewRepresentable 
    func makeUIView(context: Context) -> MKMapView 
        let map = MKMapView()
        map.delegate = context.coordinator
        map.showsUserLocation = true
        return map
    

    func updateUIView(_ view: MKMapView, context: Context) 
        print("--- updateUIView ---")
        if firstPass 
            print("First Pass1: ", firstPass)
            firstPass = false.      // <<=== *** THIS DOES NOT WORK ****
            print("First Pass2: ", firstPass)
         else 
            print("Subsequent Passes")
        
    

【问题讨论】:

我很惊讶你能编译这个。删除firstPass = false.之后的点UIViewRepresentable从extension GCMapView移动到struct GCMapView 【参考方案1】:

此操作导致循环,因为它使 SwiftUI 视图无效,从而导致调用 updateUIView 等等,因此 SwiftUI 渲染引擎将其丢弃(自动修复)。

不太确定您是否需要这样的if,但可能的解决方案是在下一个事件循环中单独更新,例如

if firstPass 
    print("First Pass1: ", firstPass)
    DispatchQueue.main.async 
       firstPass = false
       print("First Pass2: ", firstPass)
    
 else 

使用 Xcode 13 / ios 15 测试

【讨论】:

非常感谢 - 所以我猜这是处理这个问题的唯一方法吗?我想在第一次通过时在地图上设置一个新的视点(矩形和中心位置),然后再不移动它,以便用户可以滚动。我必须在 SwiftUI 中使用协调器模式方法,因此 updateUIView 回调在这里为委托提供。因此,您认为似乎没有其他方法可以更新@State 吗?您似乎需要为“struct GCMapView”使用结构(而不​​是类),所以想不出另一种方法,而是在此处的结构中使用 @State 变量

以上是关于SwiftUI,不能从委托回调中更改状态? (附代码)的主要内容,如果未能解决你的问题,请参考以下文章

将数据从委托 Swift 类传递到 SwiftUI 结构中的 EnvironmentObject

如何在 SwiftUI 中禁用子视图上的特定状态更改动画

SwiftUI - 没有初始值的状态

SwiftUI如何从视图调用或通知scenedelegate

watchOS - 如何从扩展委托更新 SwiftUI 视图

SwiftUI - 从回调中分配一个值,显示在视图中