使用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
相关产品推荐
相关产品推荐

