为什么在 swiftui 中绑定到 Picker 不再起作用?

Cel*_*ina 11 swiftui

当我在模拟器或画布中运行选择器代码时,选择器总是返回第一个带有动画的选项或只是冻结。这发生在上周四/周五。所以我检查了一些旧的简单代码,在那之前它可以工作,但对我来说也不起作用。这是简单的旧代码。它在 beta 3、4 和 5 中不再起作用。

struct PickerView : View {
    @State var selectedOptionIndex = 0

    var body: some View {
        VStack {
            Text("Option: \(selectedOptionIndex)")
            Picker(selection: $selectedOptionIndex, label: Text("")) {
                Text("Option 1")
                Text("Option 2")
                Text("Option 3")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我较新的代码中,我使用了@ObservedObject,但在这里它也不起作用。此外,我没有收到任何错误,并且它可以构建和运行。感谢您的任何指点。

---- EDIT -----请先看答案

在帮助之后,我可以使用.tag()后面的所有Text()like Text("Option 1").tag(),它现在采用初始值并在视图内更新它。如果我@ObservedObject在这里使用:

struct PickerView: View {
    @ObservedObject var data: Model

    let width: CGFloat
    let height: CGFloat

    var body: some View {
        VStack(alignment: .leading) {
            Picker(selection: $data.exercise, label: Text("select exercise")) {
                    ForEach(data.exercises, id: \.self) { exercise in
                        Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise))
                    }
                }
.frame(width: width, height: (height/2), alignment: .center)
            }
       }
    }
}  
Run Code Online (Sandbox Code Playgroud)

不幸的是,它没有反映值的变化,如果我在另一个视图中进行这些更改,一个导航链接。而且它似乎不适用于我上面使用的代码firstIndex(of: exercise)

---编辑--- 现在,如果我更改Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise)) 为 上面的代码 Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise)!) 就可以工作,因为它不能与可选的一起工作。

Cel*_*ina 15

答案总结如下:

  1. 使用.tag()后面的选项它可以工作。它看起来像下面这样:
Picker(selection: $selectedOptionIndex, label: Text("")) {
                ForEach(1...3) { index in
                    Text("Option \(index)").tag(index)
                }
            }
Run Code Online (Sandbox Code Playgroud)
  1. 如果您使用一系列对象,它可能如下所示:
Picker(selection: $data.exercises, label: Text("")) {
                ForEach(0..<data.exercises.count) { index in
                    Text("\(data.exercises[index])").tag(index)
                }
            }
Run Code Online (Sandbox Code Playgroud)

我不确定它是否是有意的,.tag()需要在这里使用,但这至少是一种解决方法。


Bar*_*ski 0

我找到了一种稍微简化代码的方法,而不需要对索引和标签进行操作。

首先,确保您的模型符合Identifiable这样的协议(这实际上是一个关键部分,因为它使 SwiftUI 能够区分元素):

public enum EditScheduleMode: String, CaseIterable, Identifiable {
    case closeSchedule
    case openSchedule
    
    public var id: EditScheduleMode { self }
    
    var localizedTitle: String { ... }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样声明viewModel:

public class EditScheduleViewModel: ObservableObject {
    @Published public var editScheduleMode = EditScheduleMode.closeSchedule
    public let modes = EditScheduleMode.allCases
}
Run Code Online (Sandbox Code Playgroud)

和用户界面:

struct ModeSelectionView: View {
    private let elements: [EditScheduleMode]
    @Binding private var selectedElement: EditScheduleMode
    
    internal init?(elements: [EditScheduleMode],
                   selectedElement: Binding<EditScheduleMode>) {
        self.elements = elements
        _selectedElement = selectedElement
    }
    
    internal var body: some View {
        VStack {
            Picker("", selection: $selectedElement) {
                ForEach(elements) { element in
                    Text(element.localizedTitle)
                }
            }
            .pickerStyle(.segmented)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过所有这些,您可以创建如下视图:

ModeSelectionView(elements: viewModel.modes, selectedElement: $viewModel.editScheduleMode)
Run Code Online (Sandbox Code Playgroud)