切换 swiftUI toggle() 时如何触发操作?

Bre*_*ger 39 toggle swift swiftui

在我的 SwiftUI 视图中,当 Toggle() 更改其状态时,我必须触发一个操作。切换本身只需要一个绑定。因此,我尝试在 @State 变量的 didSet 中触发操作。但是 didSet 永远不会被调用。

是否有任何(其他)方式来触发一个动作?或者有什么方法可以观察@State 变量的值变化?

我的代码如下所示:

struct PWSDetailView : View {

    @ObjectBinding var station: PWS
    @State var isDisplayed: Bool = false {
        didSet {
            if isDisplayed != station.isDisplayed {
                PWSStore.shared.toggleIsDisplayed(station)
            }
        }
    }

    var body: some View {
            VStack {
                ZStack(alignment: .leading) {
                    Rectangle()
                        .frame(width: UIScreen.main.bounds.width, height: 50)
                        .foregroundColor(Color.lokalZeroBlue)
                    Text(station.displayName)
                        .font(.title)
                        .foregroundColor(Color.white)
                        .padding(.leading)
                }

                MapView(latitude: station.latitude, longitude: station.longitude, span: 0.05)
                    .frame(height: UIScreen.main.bounds.height / 3)
                    .padding(.top, -8)

                Form {
                    Toggle(isOn: $isDisplayed)
                    { Text("Wetterstation anzeigen") }
                }

                Spacer()
            }.colorScheme(.dark)
    }
}
Run Code Online (Sandbox Code Playgroud)

所需的行为是当 Toggle() 更改其状态时触发操作“PWSStore.shared.toggleIsDisplayed(station)”。

paw*_*222 57

SwiftUI 2

如果您使用的是SwiftUI 2 / iOS 14,您可以使用onChange

struct ContentView: View {
    @State private var isDisplayed = false
    
    var body: some View {
        Toggle("", isOn: $isDisplayed)
            .onChange(of: isDisplayed) { value in
                // action...
                print(value)
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是最重要的答案。 (7认同)
  • 您好,使用此方法时,如果切换值保存在数据库中,则将调用两次获取操作。一旦在 init() {} 处,当我们从视图模型中更改 isDisplayed 布尔值时,onChange 就会再次被激活。有什么办法可以缓解吗? (4认同)

The*_*d27 37

这是一个不使用 tapGesture 的版本。

@State private var isDisplayed = false
Toggle("", isOn: $isDisplayed)
   .onReceive([self.isDisplayed].publisher.first()) { (value) in
        print("New value is: \(value)")           
   }
Run Code Online (Sandbox Code Playgroud)

  • 这很好!您还可以执行 `.onReceive(Just(isDisplayed)) { value in ... }` (9认同)
  • 我相信每当状态变量因任何原因更改时都会触发此代码? (2认同)

Ene*_*man 27

iOS13+

这是一种更通用的方法,您可以将其Binding应用于几乎所有内置的Views,如 Pickers、Textfields、Toggle..

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)

用法很简单;

@State var isOn: Bool = false
Toggle("Title", isOn: $isOn.didSet { (state) in
   print(state)
})
Run Code Online (Sandbox Code Playgroud)

iOS14+

@State private var isOn = false

var body: some View {
    Toggle("Title", isOn: $isOn)
        .onChange(of: isOn) { _isOn in
            /// use _isOn here..
        }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是最干净的实现。对我来说,当视图中的任何其他状态变量发生更改时,就会触发 onReceive 。使用此解决方案,该操作仅在附加状态变量更改时运行。 (6认同)

小智 14

我认为最干净的方法是使用自定义绑定。有了这个,您可以完全控制切换何时实际切换

import SwiftUI

struct ToggleDemo: View {
    @State private var isToggled = false

    var body: some View {

        let binding = Binding(
            get: { self.isToggled },
            set: {
                potentialAsyncFunction($0)
            }
        )

        func potentialAsyncFunction(_ newState: Bool) {
            //something async
            self.isToggled = newState
        }

        return Toggle("My state", isOn: binding)
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是此问题的正确解决方案。没有理由去研究 KVO 通知。 (2认同)

小智 11

我觉得没问题

struct ToggleModel {
    var isWifiOpen: Bool = true {
        willSet {
            print("wifi status will change")
        }
    }
}

struct ToggleDemo: View {
    @State var model = ToggleModel()

    var body: some View {
        Toggle(isOn: $model.isWifiOpen) {
            HStack {
                Image(systemName: "wifi")
                Text("wifi")
            }
       }.accentColor(.pink)
       .padding()
   }
}
Run Code Online (Sandbox Code Playgroud)


Leg*_*ang 6

我找到了一个更简单的解决方案,只需使用 onTapGesture:D

Toggle(isOn: $stateChange) {
  Text("...")
}
.onTapGesture {
  // Any actions here.
}
Run Code Online (Sandbox Code Playgroud)

  • 即使点击文本,它也会触发。我认为这不是一个好的解决方案。 (7认同)

jos*_*ori 6

.init是Binding构造函数

@State var isDisplayed: Bool

Toggle("some text", isOn: .init(
    get: { isDisplayed },
    set: {
        isDisplayed = $0
        print("changed")
    }
))
Run Code Online (Sandbox Code Playgroud)


z33*_*z33 5

这就是我编码的方式:

Toggle("Title", isOn: $isDisplayed)
.onReceive([self.isDisplayed].publisher.first()) { (value) in
    //Action code here
}
Run Code Online (Sandbox Code Playgroud)

更新代码(Xcode 12、iOS14):

Toggle("Enabled", isOn: $isDisplayed.didSet { val in
        //Action here        
})
Run Code Online (Sandbox Code Playgroud)