How can I run an action when a state changes?

后端 未结 7 2183
醉话见心
醉话见心 2020-12-05 22:25
enum SectionType: String, CaseIterable {
    case top = \"Top\"
    case best = \"Best\"
}

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

           


        
相关标签:
7条回答
  • 2020-12-05 23:03

    Here is another option if you have a component that updates a @Binding. Rather than doing this:

    Component(selectedValue: self.$item, ...)
    

    you can do this and have a little greater control:

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

    This way you get the benefits of the binding along with the detection of when the Component has changed the value.

    0 讨论(0)
  • 2020-12-05 23:09

    You can't use didSet observer on @State but you can on an ObservableObject property.

    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"]
    }
    

    Then use it like this:

    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)
                // }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 23:09

    In iOS 14 there is now a onChange modifier you can use like so:

    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)")
    }
    
    0 讨论(0)
  • 2020-12-05 23:12

    SwiftUI 1 & 2 (iOS 13 & 14)

    You can use 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)
                }
        }
    }
    
    0 讨论(0)
  • 2020-12-05 23:19

    iOS 14.0+

    You can use the onChange(of:perform:) modifier, like so:

    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.")
                    }
                }
        }
    }
    

    iOS 13.0+

    The following as an extension of Binding, so you can execute a closure whenever the value changes.

    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()
            })
        }
    }
    

    Used like so for example:

    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.")
            }
        }
    }
    

    This example doesn't need to use a separate function. You only need a closure.

    0 讨论(0)
  • 2020-12-05 23:21

    You can use Binding

    let textBinding = Binding<String>(
        get: { /* get */ },
        set: { /* set $0 */ }
    )
    
    0 讨论(0)
提交回复
热议问题