UIViewControllerRepresentable 当前仅在 iOS 14 中不更新

UIViewControllerRepresentable doesn't update currently only in iOS 14

我创建了 UIViewControllerRepresentable 来呈现弹出窗口。

我确实在 Xcode 13 和 iOS 15 上测试过它作为视频在 link

上效果很好

但它在 Xcode 12.5.1 和 iOS 14.8 上无法像预期的那样工作 link

我不知道为什么它在 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
        }
    }
}

您的问题与代表无关。 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 { ... }

这可行,但如果您想模拟真实按钮的外观,则需要做更多的工作。幸运的是,有 2 号解决方法。

  1. 使用透明文本作为工具栏项目,并将按钮放在它上面:
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!")
                    }
            }
        }
    }
}