How can I run an action when a state changes?

Nat*_*cks 8 state ios swift swiftui

enum SectionType: String, CaseIterable {
    case top = "Top"
    case best = "Best"
}

struct ContentView : View {
    @State private var selection: Int = 0

    var body: some View {
        SegmentedControl(selection: $selection) {
            ForEach(SectionType.allCases.identified(by: \.self)) { type in
                Text(type.rawValue).tag(type)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

How do I run code (e.g print("Selection changed to \(selection)") when the $selection state changes? I looked through the docs and I couldn't find anything.

Geo*_*e_E 48

iOS 14.0+

您可以使用onChange(of:perform:)修饰符,如下所示:

struct ContentView: View {
    
    @State private var isLightOn = false

    var body: some View {
        Toggle("Light", isOn: $isLightOn)
            .onChange(of: isLightOn) { value in
                if value {
                    print("Light is now on!")
                } else {
                    print("Light is now off.")
                }
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

iOS 13.0+

以下是 的扩展Binding,因此您可以在值更改时执行闭包。

extension Binding {
    
    /// When the `Binding`'s `wrappedValue` changes, the given closure is executed.
    /// - Parameter closure: Chunk of code to execute whenever the value changes.
    /// - Returns: New `Binding`.
    func onUpdate(_ closure: @escaping () -> Void) -> Binding<Value> {
        Binding(get: {
            wrappedValue
        }, set: { newValue in
            wrappedValue = newValue
            closure()
        })
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用例如:

struct ContentView: View {
    
    @State private var isLightOn = false
    
    var body: some View {
        Toggle("Light", isOn: $isLightOn.onUpdate(printInfo))
    }
    
    private func printInfo() {
        if isLightOn {
            print("Light is now on!")
        } else {
            print("Light is now off.")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个例子不需要使用单独的函数。你只需要一个闭包。


P. *_*Ent 23

如果您有一个更新@Binding. 而不是这样做:

Component(selectedValue: self.$item, ...)
Run Code Online (Sandbox Code Playgroud)

你可以这样做,并有更大的控制权:

Component(selectedValue: Binding(
    get: { self.item },
    set: { (newValue) in
              self.item = newValue
              // now do whatever you need to do once this has changed
    }), ... )
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以获得绑定的好处以及检测何时Component更改了值。


Chr*_*ris 18

在 iOS 14 中,现在有一个onChange修饰符,您可以像这样使用:

SegmentedControl(selection: $selection) {
    ForEach(SectionType.allCases.identified(by: \.self)) { type in
        Text(type.rawValue).tag(type)
    }
}
.onChange(of: selection) { value in
    print("Selection changed to \(selection)")
}
Run Code Online (Sandbox Code Playgroud)


paw*_*222 18

SwiftUI 1 和 2(iOS 13 和 14)

您可以使用onReceive

import Combine
import SwiftUI

struct ContentView: View {
    @State private var selection = false

    var body: some View {
        Toggle("Selection", isOn: $selection)
            .onReceive(Just(selection)) { selection in
                // print(selection)
            }
    }
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*cks 16

您不能在属性上使用didSet观察器,@State但可以在ObservableObject属性上使用。

import SwiftUI
import Combine

final class SelectionStore: ObservableObject {
    var selection: SectionType = .top {
        didSet {
            print("Selection changed to \(selection)")
        }
    }

    // @Published var items = ["Jane Doe", "John Doe", "Bob"]
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

import SwiftUI

enum SectionType: String, CaseIterable {
    case top = "Top"
    case best = "Best"
}

struct ContentView : View {
    @ObservedObject var store = SelectionStore()

    var body: some View {
        List {
            Picker("Selection", selection: $store.selection) {
                ForEach(FeedType.allCases, id: \.self) { type in
                    Text(type.rawValue).tag(type)
                }
            }.pickerStyle(SegmentedPickerStyle())

            // ForEach(store.items) { item in
            //     Text(item)
            // }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您还可以使用“onReceive” (3认同)