我有以下几点看法:

struct GameView: View {
    @State private var userWon: Bool = false {
        didSet {
            print("userWon: \(userWon)") // not called
            // TODO: Change game state here
        }
    }
    
var body: some View {
    ZStack {
        VStack {
            GameViewRepresentable(userWon: $userWon)
                .onChange(of: userWon) { newValue in
                    print("GameViewRepresentable: \(newValue)") // called
                    // TODO: or change game state here?
                }
        }
        .clipped()
    }
   }
}

可表示的视图如下所示:

struct GameViewRepresentable: UIViewRepresentable {
    typealias UIViewType = GameView
    @Binding var userWon: Bool {
        willSet { newValue
            print("willSet userWon: \(newValue)")
        }
        didSet {
            print("didSet userWon: \(userWon)")
        }
    }
    
    func makeUIView(context: Context) -> GameView {
        let gameView = GameView()
        gameView.delegate = context.coordinator
        return gameView
    }

    func updateUIView(_ uiView: GameView, context: Context) {
    }

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

    class GameViewCoordinator: GameViewDelegate  {
        var parent: GameViewRepresentable
        
        init(_ parent: GameViewRepresentable) {
            self.parent = parent
        }

        func userWon() {
            self.parent.userWon = true
        }
    }
}

我可以看到,在协调器内部正确地调用了userWon方法,该协调器相应地调用了GameViewRepresentable中的willSetdidSet,但是绑定userWon属性的GameView中的didSet从未被调用.然而,Gameview中的onChange确实会被调用.那么,这是应对国有assets资源 变化的正确方式吗?我原以为会调用Gameview中的didSet.

推荐答案

是的,onChange是您检测@State个变化的方式.

@State@Binding上不触发willSetdidSet是非常正常的.SWIFT在userWon属性的setter中插入对willSetdidSet的调用,因此如果userWon通过调用userWon‘S setter来更改not,则不会调用willSetdidSet.

你的userWonassets资源 降到了这样的水平:

private var _userWon = State(initialValue: false)
private var userWon: Bool {
    get { _userWon.wrappedValue }
    set { 
        // <willSet is called here>
        _userWon.wrappedValue = newValue 
        // <didSet is called here>
    }
}
private var $userWon: Binding<Bool> {
    _userWon.projectedValue
}

考虑一下这个非常简单的例子:

@State private var userWon: Bool = false {
    willSet {
        print("willSet")
    }
    didSet {
        print("didSet")
    }
}

var body: some View {
    Toggle(isOn: $userWon) {
        Text("Foo")
    }
}

无论我切换切换多少次,willSetdidSet都不会被调用.这是因为Toggle实际上不会通过字面意思改变userWon的值,例如userWon = true(这将称为它的setter).Toggle根本不知道userWon的存在.

实际发生的情况是,你通过了Binding<Bool>分到Toggle分,这是State分中的projectedValue分.$userWon只是_userWon.projectedValue的一种简短的说法.Toggle将在某一时刻设置Binding<Bool>wrappedValue属性.这将导致State中的wrappedValue被设置,并且视图被更新.

请注意,所有操作都是通过StateBindingwrappedValue个属性完成的,而不是您的userWon属性.

Swift相关问答推荐

音频播放器无法播放URL音频(操作系统状态错误2003334207.)

是否可以循环遍历SWIFT子类并访问它们的静态变量S?

为什么ClosedRange<;Int&>包含的速度比预期慢340万倍?

按数组大小进行类型判断?

如何通过 Enum 属性使用新的 #Predicate 宏获取

SwiftUI 中的同一个 ForEach 中是否可以有 2 个数组?

如果 Swift 无法分配内存会怎样?

Swift-如何接受多个(联合)类型作为参数

令人满意的 var body:协议扩展中的一些视图

Swift初始化具有可变ID的重复值数组

在 Swift 中实现自定义异步序列

Xcode:方法参数的代码完成

Swift 读写图片占用大量内存

.onTapGesture 不适用于 Stepper,仅适用于其文本

组合 flatMap 不会返回预期的上下文结果类型

无法从自定义动态框架(Swift)访问类

在 ViewController 的 UICollectionView 中拉取刷新

协议扩展中的where self是什么

如何使用 Swift 将文本文件逐行加载到数组中?

ISO8601DateFormatter 不解析 ISO 日期字符串