如何从外部更改 SwiftUI Toggle 的状态

sve*_*son 2 swiftui

我正在尝试从视图外部更改切换的状态。我尝试过 ObservableObject 和 EnvironmentObject,但切换需要绑定(@State)。

  • 当用户点击切换时我需要执行回调
  • 我需要以编程方式更改切换的状态,而不执行回调。
  • 我对此视图和其他视图使用共享模型,理想情况下我希望能够将其用于“启用”Bool 来代替 State var isOn。

这段代码确实让我通过扩展执行回调,但我不知道如何在外部更改 State 变量 isOn,如果我能够,我猜测我的回调将被执行,但我不想这样做发生。

import SwiftUI

struct ControlView: View {
    var title: String
    var panel: Int
    var callback: ()-> Void
    @State public var isOn = false // toggle state
    @EnvironmentObject var state: MainViewModel

    //@ViewBuilder
    var body: some View {
        VStack() {
             
            // -- Header
            HStack() {
                Text(" ")
                Image(self.state.panelIcon(panel: panel)).resizable().frame(width: 13.0, height: 13.0)
                Text(title)
                Spacer()
            }.padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
            .background(Color(red: 0.9, green: 0.9, blue: 0.9))
            
            // -- Switch

            Toggle(isOn: $isOn.didSet { (state) in
                // Activate ARC
                callback()
            }) {
                Text("Enable ARC")
            }.padding(EdgeInsets(top: 0, leading: 12, bottom: 10, trailing: 12))

        }.overlay(
            RoundedRectangle(cornerRadius: 10)
                .stroke(Color(red: 0.8, green: 0.8, blue: 0.8), lineWidth: 1.25)
        ).background(Color.white)
    }
}

extension Binding {
    func didSet(execute: @escaping (Value) -> Void) -> Binding {
        return Binding(
            get: { self.wrappedValue },
            set: {
                self.wrappedValue = $0
                execute($0)
            }
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

rob*_*off 7

通过创建具有执行副作用的函数的Binding自定义,您就走在正确的轨道上。set但不要使用 a State,而是创建一个Binding直接enabled修改ObservableObject. 例子:

import PlaygroundSupport
import SwiftUI

class MyModel: ObservableObject {
    @Published var enabled: Bool = false
    @Published var sideEffectCount: Int = 0
}

struct RootView: View {
    @EnvironmentObject var model: MyModel

    var body: some View {
        List {
            Text("Side effect count: \(model.sideEffectCount)")

            Button("Set to false programmatically") {
                model.enabled = false
            }

            Button("Set to true programmatically") {
                model.enabled = true
            }

            Toggle("Toggle without side effect", isOn: $model.enabled)

            Toggle("Toggle WITH side effect", isOn: Binding(
                get: { model.enabled },
                set: { newValue in
                    withAnimation {
                        if newValue {
                            model.sideEffectCount += 1
                        }
                        model.enabled = newValue
                    }
                }
            ))
        }
    }
}

PlaygroundPage.current.setLiveView(
    RootView()
        .environmentObject(MyModel())
)
Run Code Online (Sandbox Code Playgroud)