SwiftUI自定义Popover关闭时displayString重置为初始状态问题
问题解决:iOS15/16下SwiftUI自定义Popover Picker关闭时回显旧值
问题分析
在iOS15、16模拟器中,使用自定义PopoverPicker选择新值后,模型的someInt已更新,但关闭Popover时,Picker内的显示会回退到旧值;iOS17无此问题。这是因为旧系统中,自定义Popover的内容视图在isPresented状态切换时,没有正确同步外部Binding的最新值,导致视图残留旧状态。
解决方案
方案1:通过本地状态同步(稳定可靠)
在PopoverPicker内部维护一个本地选中状态,双向同步外部Binding的值,确保Picker始终基于最新状态渲染:
struct PopoverPicker<Content: View>: View { @Binding var selection: Int let content: () -> Content let displayString: (Int) -> String @State private var isPresented = false @State private var localSelection: Int init(selection: Binding<Int>, content: @escaping () -> Content, displayString: @escaping (Int) -> String) { self._selection = selection self._localSelection = State(initialValue: selection.wrappedValue) self.content = content self.displayString = displayString } var body: some View { content() .customPopover(isPresented: $isPresented) { Picker("", selection: $localSelection) { ForEach(Array(1...30), id: \.self) { value in Text(displayString(value)) .tag(value) } } .pickerStyle(.wheel) .onChange(of: localSelection) { newValue in selection = newValue } .onAppear { localSelection = selection } } .onTapGesture { isPresented.toggle() } .onChange(of: selection) { newValue in localSelection = newValue } } }
方案2:通过ID重建视图(简洁高效)
给Picker添加id(isPresented),每次Popover显示时强制重建Picker视图,确保读取最新的Binding值:
struct PopoverPicker<Content: View>: View { @Binding var selection: Int let content: () -> Content let displayString: (Int) -> String @State private var isPresented = false var body: some View { content() .customPopover(isPresented: $isPresented) { Picker("", selection: $selection) { ForEach(Array(1...30), id: \.self) { value in Text(displayString(value)) .tag(value) } } .pickerStyle(.wheel) .id(isPresented) // 关键:每次Popover切换时重建Picker } .onTapGesture { isPresented.toggle() } } }
原理说明
- 方案1通过本地状态做中间层,在Popover出现、外部值变化、本地选择变化三个时机双向同步,彻底避免状态不一致。
- 方案2利用SwiftUI的
id特性,当isPresented变化时,Picker视图会被重新创建,自动读取最新的selection值,解决旧状态残留问题。
内容的提问来源于stack exchange,提问作者newUser222
相关产品推荐
相关产品推荐

