SwiftUI 中操作嵌套 Picker 后,外层 Picker 显示未更新,如何修复?
选择 Date 或 Title 时,需要同时更新 selectedSort 和 selectedOption。两个 Picker 分别绑定独立的状态,SwiftUI 不会根据内层的选择自动将外层设为 .sort。
这种分层菜单可以将外层改为 Menu,在子菜单的 Button 中更新两个状态,再通过 selectedOption.rawValue 显示当前选项。这样,即使已经选中 Date,切到 Search 后再次点击 Date,也能正确更新主选项。
为什么外层仍然显示 Search
原代码中的两个绑定是:
// 外层
selection: $selectedOption
// 内层
selection: $selectedSort
选择排序项时,内层只更新 selectedSort,没有给 selectedOption 赋值,所以外层仍是 .search,继续显示 Search。
鼠标悬停只会展开子菜单。状态应在点击 Date 或 Title 确认选择后更新。
修改方式与完整代码
保留两个状态,分别处理菜单点击和标题显示:
- 点击排序项时,保存排序方式,并将主选项设为
.sort。 - 点击其他主选项时,直接更新
selectedOption。 - 菜单标题始终读取
selectedOption.rawValue。
import SwiftUI
struct ContentView: View {
enum MenuOption: String, CaseIterable {
case sort = "Sort"
case search = "Search"
case somethingElse = "Something Else"
}
enum SortOption: String, CaseIterable {
case date = "Date"
case title = "Title"
}
@State private var selectedOption: MenuOption = .sort
@State private var selectedSort: SortOption = .date
var body: some View {
Menu {
Menu(MenuOption.sort.rawValue) {
ForEach(SortOption.allCases, id: \.self) { option in
Button {
selectedSort = option
selectedOption = .sort
} label: {
if selectedSort == option {
Label(option.rawValue, systemImage: "checkmark")
} else {
Text(option.rawValue)
}
}
}
}
ForEach(MenuOption.allCases, id: \.self) { option in
if option != .sort {
Button(option.rawValue) {
selectedOption = option
}
}
}
} label: {
Text(selectedOption.rawValue)
}
.controlSize(.small)
}
}
当主选项为 Search 时,点击 Sort > Date 会执行:
selectedSort = .date
selectedOption = .sort
菜单标题随之显示 Sort。勾选标记表示已保存的排序方式,切换到 Search 后,这个排序方式仍会保留。
为什么只加 onChange 还不够
如果在内层 Picker 上通过 onChange 监听 selectedSort,并在值变化时设置 selectedOption = .sort,就只能处理排序值发生变化的情况。
例如,selectedSort 初始为 .date,切换到 Search 后再次选择 Date,排序值仍是 .date,不会触发依赖值变化的 onChange 回调。Button 的动作则会在点击时执行,因此也能处理这种情况。
.tag(...) 用于将选项与对应 Picker 的选择值匹配起来,不能代替两个状态之间的赋值逻辑,因此仍需显式设置 selectedOption = .sort。
备注:内容仅供参考。
