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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 20:43:40