为什么绑定到 Picker 在 swiftui 中不再起作用?
Why does binding to the Picker not work anymore in swiftui?
当我在模拟器或 Canvas 中 运行 选择器代码时,选择器总是返回到第一个选项并带有动画或只是冻结。这发生在上次 Thursday/Friday 之后。所以我检查了一些旧的简单代码,在那之前它可以工作,但对我来说也不起作用。
这是简单的旧代码。它在 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")
}
}
}
}
在我较新的代码中,我使用了 @ObservedObject
,但在这里它也不起作用。
我也没有收到任何错误,它会生成 运行s。
谢谢指点。
----编辑----- 请先看答案
在帮助之后,我可以在所有 Text()
后面使用 .tag()
,比如 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)
}
}
}
}
不幸的是,如果我在另一个视图中进行这些更改,则它不会反映值的变化,一个导航链接会更远。而且它似乎不适用于我上面的代码,我在其中使用 firstIndex(of: exercise)
---编辑---
现在如果我改变上面的代码就可以工作
Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise))
进入
Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise)!)
因为它不能与可选的一起使用。
答案总结:
- 选项后面的
.tag()
可以正常工作。它看起来像下面这样:
Picker(selection: $selectedOptionIndex, label: Text("")) {
ForEach(1...3) { index in
Text("Option \(index)").tag(index)
}
}
- 如果您使用一系列对象,它可能看起来像这样:
Picker(selection: $data.exercises, label: Text("")) {
ForEach(0..<data.exercises.count) { index in
Text("\(data.exercises[index])").tag(index)
}
}
我不确定这里是否需要使用 .tag()
,但这至少是一种解决方法。
我找到了一种无需对索引和标签进行操作即可稍微简化代码的方法。
首先,确保你的模型符合 Identifiable
协议(这实际上是一个关键部分,因为它使 SwiftUI 能够区分元素):
public enum EditScheduleMode: String, CaseIterable, Identifiable {
case closeSchedule
case openSchedule
public var id: EditScheduleMode { self }
var localizedTitle: String { ... }
}
然后你可以像这样声明 viewModel:
public class EditScheduleViewModel: ObservableObject {
@Published public var editScheduleMode = EditScheduleMode.closeSchedule
public let modes = EditScheduleMode.allCases
}
和UI:
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)
}
}
}
使用所有这些,您可以创建这样的视图:
ModeSelectionView(elements: viewModel.modes, selectedElement: $viewModel.editScheduleMode)
当我在模拟器或 Canvas 中 运行 选择器代码时,选择器总是返回到第一个选项并带有动画或只是冻结。这发生在上次 Thursday/Friday 之后。所以我检查了一些旧的简单代码,在那之前它可以工作,但对我来说也不起作用。 这是简单的旧代码。它在 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")
}
}
}
}
在我较新的代码中,我使用了 @ObservedObject
,但在这里它也不起作用。
我也没有收到任何错误,它会生成 运行s。
谢谢指点。
----编辑----- 请先看答案
在帮助之后,我可以在所有 Text()
后面使用 .tag()
,比如 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)
}
}
}
}
不幸的是,如果我在另一个视图中进行这些更改,则它不会反映值的变化,一个导航链接会更远。而且它似乎不适用于我上面的代码,我在其中使用 firstIndex(of: exercise)
---编辑---
现在如果我改变上面的代码就可以工作
Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise))
进入
Text("\(exercise)").tag(self.data.exercises.firstIndex(of: exercise)!)
因为它不能与可选的一起使用。
答案总结:
- 选项后面的
.tag()
可以正常工作。它看起来像下面这样:
Picker(selection: $selectedOptionIndex, label: Text("")) {
ForEach(1...3) { index in
Text("Option \(index)").tag(index)
}
}
- 如果您使用一系列对象,它可能看起来像这样:
Picker(selection: $data.exercises, label: Text("")) {
ForEach(0..<data.exercises.count) { index in
Text("\(data.exercises[index])").tag(index)
}
}
我不确定这里是否需要使用 .tag()
,但这至少是一种解决方法。
我找到了一种无需对索引和标签进行操作即可稍微简化代码的方法。
首先,确保你的模型符合 Identifiable
协议(这实际上是一个关键部分,因为它使 SwiftUI 能够区分元素):
public enum EditScheduleMode: String, CaseIterable, Identifiable {
case closeSchedule
case openSchedule
public var id: EditScheduleMode { self }
var localizedTitle: String { ... }
}
然后你可以像这样声明 viewModel:
public class EditScheduleViewModel: ObservableObject {
@Published public var editScheduleMode = EditScheduleMode.closeSchedule
public let modes = EditScheduleMode.allCases
}
和UI:
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)
}
}
}
使用所有这些,您可以创建这样的视图:
ModeSelectionView(elements: viewModel.modes, selectedElement: $viewModel.editScheduleMode)