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

使用UIViewControllerRepresentable实现iPhone端Popover更新值时崩溃如何修复

iOS SwiftUI 自定义Popover崩溃问题解决方案

崩溃触发原因

  • 系统报错的核心原因是UIViewControllerRepresentable的updateUIViewController回调会在关联的SwiftUI视图状态变化时重复执行。当你点击Popover内的按钮修改currentFontSize状态时,父视图会触发重绘,updateUIViewController再次运行,此时isPresented仍为true,代码会重复调用present方法展示已经处于弹出状态的控制器,触发系统抛出「尝试present已被展示的控制器」异常。
  • 同时当前实现存在额外逻辑缺陷:Coordinator持有的UIHostingController的rootView仅在初始化时赋值一次,后续父视图状态变化不会同步更新Popover内的内容,即使解决崩溃问题,Popover内的字体数值也不会跟随点击更新。

修复代码

修改Popover结构体的updateUIViewController方法,增加弹出状态判断,同时同步更新Popover的内容即可:

struct Popover<Content: View> : UIViewControllerRepresentable {
    @Binding var isPresented: Bool
    let onDismiss: (() -> Void)?
    @ViewBuilder let content: () -> Content

    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }

    func makeUIViewController(context: Context) -> UIViewController {
        return UIViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        let host = context.coordinator.host
        // 每次更新时同步最新的Popover内容
        host.rootView = content()
        host.preferredContentSize = host.sizeThatFits(in: CGSize(width: Int.max , height: Int.max))
        
        if isPresented {
            // 只有未弹出时才执行present逻辑
            if uiViewController.presentedViewController == nil {
                host.modalPresentationStyle = UIModalPresentationStyle.popover
                host.popoverPresentationController?.delegate = context.coordinator
                host.popoverPresentationController?.sourceView = uiViewController.view
                host.popoverPresentationController?.sourceRect = uiViewController.view.bounds
                uiViewController.present(host, animated: true)
            }
        } else {
            // 只有已弹出时才执行dismiss逻辑
            if uiViewController.presentedViewController != nil {
                host.dismiss(animated: true)
            }
        }
    }

    class Coordinator: NSObject, UIPopoverPresentationControllerDelegate {
        let host: UIHostingController<Content>
        private let parent: Popover

        init(parent: Popover) {
            self.parent = parent
            // 初始化时默认赋值,后续update时会覆盖为最新内容
            self.host = UIHostingController(rootView: parent.content())
        }

        func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
            self.parent.isPresented = false
            self.parent.onDismiss?()
        }

        func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
            return .none
        }
    }
}

修改完成后原调用逻辑无需改动,即可正常运行:点击Popover内的按钮更新状态不会崩溃,Popover内的数值也会同步刷新。

内容的提问来源于stack exchange,提问作者Basel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:00:04