How can I run an action when a state changes?

一世执手 提交于 2019-12-12 08:00:54

问题


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

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.


回答1:


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



回答2:


Not really answering your question, but here's the right way to set up SegmentedControl (didn't want to post that code as a comment, because it looks ugly). Replace your ForEach version with the following code:

ForEach(0..<SectionType.allCases.count) { index in 
    Text(SectionType.allCases[index].rawValue).tag(index)
}

Tagging views with enumeration cases or even strings makes it behave inadequately – selection doesn't work.

You might also want to add the following after the SegmentedControl declaration to ensure that selection works:

Text("Value: \(SectionType.allCases[self.selection].rawValue)")

Full version of body:

var body: some View {
    VStack {
        SegmentedControl(selection: self.selection) {
            ForEach(0..<SectionType.allCases.count) { index in
                Text(SectionType.allCases[index].rawValue).tag(index)
                }
            }

        Text("Value: \(SectionType.allCases[self.selection].rawValue)")
    }
}

Regarding your question – I tried adding didSet observer to selection, but it crashes Xcode editor and generates "Segmentation fault: 11" error when trying to build.



来源:https://stackoverflow.com/questions/56550713/how-can-i-run-an-action-when-a-state-changes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!