首页 > 解决方案 > UIViewControllerRepresentable 目前仅在 iOS 14 中不更新

问题描述

我创建了 UIViewControllerRepresentable 来呈现弹出框。

我确实在 Xcode 13 和 iOS 15 上对其进行了测试,它在此链接上的视频效果很好

但它在 Xcode 12.5.1 和 iOS 14.8 上无法按预期工作作为此链接上的视频

我不知道为什么它不能在 iOS 14.8 上运行,是否有其他解决方案可以让它在两个 iOS 版本上运行?

我的代码:

struct PopoverViewModifier<PopoverContent>: ViewModifier where PopoverContent: View {
    @Binding var isPresented: Bool
    let onDismiss: (() -> Void)?
    let content: () -> PopoverContent

    func body(content: Content) -> some View {
        content
            .background(
                Popover(
                    isPresented: self.$isPresented,
                    onDismiss: self.onDismiss,
                    content: self.content
                )
            )
    }
}

extension View {
    func cocoaPopover<Content>(
        isPresented: Binding<Bool>,
        onDismiss: (() -> Void)? = nil,
        content: @escaping () -> Content
    ) -> some View where Content: View {
        ModifiedContent(
            content: self,
            modifier: PopoverViewModifier(
                isPresented: isPresented,
                onDismiss: onDismiss,
                content: content
            )
        )
    }
}

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

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

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

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        
        
        let host = context.coordinator.host
        host.rootView = content()
        
        if host.viewIfLoaded?.window == nil && self.isPresented {
            host.preferredContentSize = host.sizeThatFits(in: CGSize(width: Int.max , height: Int.max))
            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, completion: nil)

        } else if self.isPresented == false {
            host.dismiss(animated: false, completion: nil)
        }
    }

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

        init(parent: Popover, content: Content) {
            self.parent = parent
            self.host = UIHostingController(rootView: content)
        }

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

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

标签: swiftswiftuiuikituiviewcontrollerrepresentable

解决方案


您的问题与代表无关。似乎 iOS14 和它处理工具栏上的按钮的方式可能存在错误。当您在工具栏内放置一个按钮时,它的几何行为有点奇怪(在 iOS14 上)。运行以下代码并检查两个 iOS 版本的结果。

struct ContentView: View {
    @State var show = false
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, world!")
            }
            .toolbar {
                Button("Show") {
                    show = true
                }
                .border(Color.green)
            }
        }
    }
}

在 iOS 15 上,它将如下所示:

在此处输入图像描述

但在 iOS 14 上,它看起来像这样:

在此处输入图像描述

那么绿色边框是怎么回事呢?任何放置在边框、叠加层或背景中的东西都会被绘制在其他地方(就我目前所见,在屏幕之外)。

请注意,此问题仅发生工具栏内的按钮上。

这种糟糕的几何形状对您的弹出框有直接影响。因此,如果我们找到修复该几何图形的方法,弹出框的问题也会消失。我可以想到两种解决方法:

  1. 用可点击的文本替换您的按钮:
Text("Show").onTapGesture { ... }

这行得通,但如果你想模拟真实按钮的外观,你需要做更多的工作。幸运的是,有第二个解决方法。

  1. 使用透明的 Text 作为工具栏项,并将按钮放在它上面:
struct ContentViewx: View {
    @State var show = false
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, world!")
            }
            .toolbar {
                Text("Show")
                    .opacity(0)
                    .overlay(
                        Button("Show") {
                            show = true
                        }
                    )
                    .cocoaPopover(isPresented: $show) {
                        print("dismiss!")
                    } content: {
                        Text("Hello, this is the popover!")
                    }
            }
        }
    }
}

推荐阅读