You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的更佳实现方式

  1. 实现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
}
  1. ViewModel内处理关联逻辑:利用属性观察者替代外部onChange,让逻辑更内聚:
@MainActor
@Observable
final class TestViewModel {
    var parents: [Parent] = Parent.mock
    var selectedParent: Parent {
        didSet {
            selectedChild = selectedParent.children[0]
        }
    }
    // 其余属性保持不变
}
  1. 优化搜索体验:添加大小写不敏感匹配,提升用户使用感受:
var searchParents: [Parent] {
    searchParentText.isEmpty ? parents : parents.filter {
        $0.title.localizedCaseInsensitiveContains(searchParentText)
    }
}

var searchChildren: [Child] {
    searchChildText.isEmpty ? children : children.filter {
        $0.title.localizedCaseInsensitiveContains(searchChildText)
    }
}
  1. 完整修复代码:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:23:11