SwiftUI Picker选择错误及搜索框致列表项消失问题求助
问题分析与修复方案
问题1:无效选择错误
原因:Picker的selection绑定的是Parent/Child类型对象,但tag设置的是String类型的title,类型不匹配。SwiftUI无法将选中的字符串映射回对应的对象,触发无效选择错误。
修复:将tag的类型改为和selection一致的对象类型:
// 父Picker修改 ForEach(vm.searchParents, id: \.self) { parent in Text(parent.title) .tag(parent) // 替换为Parent对象 } // 子Picker修改 ForEach(vm.searchChildren, id: \.self) { child in Text(child.title) .tag(child) // 替换为Child对象 }
问题2:搜索时列表消失
原因:searchable修饰符被错误添加到Text视图上,而非Picker的导航页面容器或更高层级。错误的层级导致搜索状态无法正确关联到列表,引发异常消失。
修复:将searchable移到Picker外层:
// 父Picker修改 Picker("Parents", selection: $vm.selectedParent) { ForEach(vm.searchParents, id: \.self) { parent in Text(parent.title) .tag(parent) } } .pickerStyle(.navigationLink) .searchable(text: $vm.searchParentText) // 移到Picker外层 // 子Picker修改 Picker("Children", selection: $vm.selectedChild) { ForEach(vm.searchChildren, id: \.self) { child in Text(child.title) .tag(child) } } .pickerStyle(.navigationLink) .searchable(text: $vm.searchChildText) // 移到Picker外层
父/子关联Picker的更佳实现方式
- 实现Identifiable协议:给Parent和Child添加唯一标识,简化ForEach的使用,同时明确对象唯一性:
struct Parent: Hashable, Identifiable { let id = UUID() let title: String let children: [Child] } struct Child: Hashable, Identifiable { let id = UUID() let title: String }
- ViewModel内处理关联逻辑:利用属性观察者替代外部onChange,让逻辑更内聚:
@MainActor @Observable final class TestViewModel { var parents: [Parent] = Parent.mock var selectedParent: Parent { didSet { selectedChild = selectedParent.children[0] } } // 其余属性保持不变 }
- 优化搜索体验:添加大小写不敏感匹配,提升用户使用感受:
var searchParents: [Parent] { searchParentText.isEmpty ? parents : parents.filter { $0.title.localizedCaseInsensitiveContains(searchParentText) } } var searchChildren: [Child] { searchChildText.isEmpty ? children : children.filter { $0.title.localizedCaseInsensitiveContains(searchChildText) } }
- 完整修复代码:
import SwiftUI struct Parent: Hashable, Identifiable { let id = UUID() let title: String let children: [Child] } struct Child: Hashable, Identifiable { let id = UUID() let title: String } extension Parent { static var mock: [Parent] = [ .init(title: "Parent 1", children: [.init(title: "Child 1"), .init(title: "Child 2")]), .init(title: "Parent 2", children: [.init(title: "Child 3"), .init(title: "Child 4"), .init(title: "Child 5")]) ] } @MainActor @Observable final class TestViewModel { var parents: [Parent] = Parent.mock var selectedParent: Parent { didSet { selectedChild = selectedParent.children[0] } } var searchParentText = "" var searchParents: [Parent] { searchParentText.isEmpty ? parents : parents.filter { $0.title.localizedCaseInsensitiveContains(searchParentText) } } var children: [Child] { selectedParent.children } var selectedChild: Child var searchChildText = "" var searchChildren: [Child] { searchChildText.isEmpty ? children : children.filter { $0.title.localizedCaseInsensitiveContains(searchChildText) } } init() { self.selectedParent = Parent.mock[0] self.selectedChild = Parent.mock[0].children[0] } } @MainActor struct TestSwiftUIView: View { @State var vm = TestViewModel() var body: some View { NavigationStack { Form { Picker("Parents", selection: $vm.selectedParent) { ForEach(vm.searchParents) { parent in Text(parent.title) .tag(parent) } } .pickerStyle(.navigationLink) .searchable(text: $vm.searchParentText) Picker("Children", selection: $vm.selectedChild) { ForEach(vm.searchChildren) { child in Text(child.title) .tag(child) } } .pickerStyle(.navigationLink) .searchable(text: $vm.searchChildText) } } } }
内容的提问来源于stack exchange,提问作者miltenkot
相关产品推荐
相关产品推荐

