【问题标题】:Whats the best way to get SwiftUI Picker() selection to update on app state change?让 SwiftUI Picker() 选择更新应用程序状态更改的最佳方法是什么?
【发布时间】:2021-04-20 03:13:48
【问题描述】:

我正在尝试使用 SwiftUI 制作一个 UI,其中列出了可以是不同种类并且可以更新的东西的集合。我想使用 Picker 在 UI 中设置类型,并且我还希望在以其他方式修改项目时更新视图(例如,从 UI 的另一部分或通过网络)。

我喜欢“redux”风格的设置,我不想放弃它。

这是一个简单的示例,它显示了两个项目,每个项目都有一个“随机化”按钮可以随机更改项目,还有一个 Picker 可以让您选择新的项目类型。后者按预期工作:Picker 更改 @State var,存储更新等。“随机”按钮更新存储和 let 属性标签,但 @StatePicker 不不更新。

我很想得到一些关于如何让这个以我想要的方式工作的好方法的建议。

import SwiftUI
import Combine

@main
struct PuffedWastApp: App {
    var store = Store()
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(store)
        }
    }
}

enum ItemState:String, CaseIterable {
    case apple
    case bannana
    case coconut
}

enum Action {
    case set(Int,ItemState)
}

class Store: ObservableObject {

    @Published var state:[ItemState] = [.apple, .apple]

    func reduce(_ action:Action) {
        print("do an action")
        switch (action) {
        case let .set(index,new_state):
            print("set \(index) to \(new_state)")
            state[index] = new_state
            self.objectWillChange.send()
        }
    }
    
}

struct ContentView: View {
    @EnvironmentObject var store: Store

    var body: some View {
        VStack {
            ForEach(store.state.indices) { index in
                ItemContainer(index: index)
                //Text("\(index)")
            }
        }
        .padding()
    }
}

struct ItemContainer: View {
    @EnvironmentObject var store: Store
    let index: Int

    
    var body: some View {
        ItemView(
            index: index,
            label: store.state[index],   // let property:  updates on change in the Store
            localLabel: store.state[index],  //@State variable; doesn't update on change in the Store
            dispatch: store.reduce
        )
    .padding()
    }
}

struct ItemView: View {
    let index: Int
    
    let label: ItemState
    @State var localLabel: ItemState
    
    let dispatch: (Action)->()
    
    var body: some View {
        HStack{
            //Text("\(index)")
            Text(label.rawValue)
            Text(localLabel.rawValue)
            Button("Randomize") { dispatch( .set(index, ItemState.allCases.randomElement() ?? .apple ) ) }
            Picker("Item type", selection: $localLabel ) {
                ForEach( ItemState.allCases , id: \.self ) {
                    Text($0.rawValue).tag($0)
                }
            }.onChange(of: localLabel) { dispatch(.set(index, $0)) }
        }
        .padding()
    }
}

【问题讨论】:

    标签: swiftui reactive-programming swiftui-state swiftui-picker


    【解决方案1】:

    尝试更改此行:

    @State var localLabel: ItemState

    @Binding var localLabel: ItemState

    并将其传递给您的 ItemView init 为:

    ItemView(
        index: index,
        label: store.state[index],
        localLabel: $store.state[index],
        dispatch: store.reduce
    )
    

    【讨论】:

    • 谢谢——就这样。在我的项目代码中,我使用函数调用从商店中检索东西,并发现我可以通过显式初始化 Binding<ItemState> 而不是使用 $ 来使这种方法在该上下文中工作,如下所示:localLabel: Binding<ItemState>( get: {store.getItem(id: index)}, set: {value,_ in store.reduce(.set(index,value)) } )
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-23
    • 2020-04-04
    • 2010-09-20
    相关资源
    最近更新 更多