SwiftUI:WKWebView 在 macOS 上截断页面内容(NSViewRepresentable)

Posted

技术标签:

【中文标题】SwiftUI:WKWebView 在 macOS 上截断页面内容(NSViewRepresentable)【英文标题】:SwiftUI: WKWebView Cutting Off Page Content on macOS (NSViewRepresentable) 【发布时间】:2019-12-15 20:50:57 【问题描述】:

我有一个基本的 NSViewRepresentable 实现 WKWebView,用于 macOS 上的 SwiftUI 应用程序。 UIViewRepresentable 等效项在 ios 上运行良好,但在 macOS(本机,不是 Catalyst)上,顶部内容总是被切断。

丢失的量总是等于父视图(例如选项卡视图)及其填充的大小,这表明 Web 视图不断将其内容缩放到 window 大小,而不是 em>查看大小。

例如这个页面:

...应该如下(如Chrome中所示)。整个导航栏已被裁剪(尽管两侧似乎没有受到影响)。

关于如何解决此问题的任何建议?有趣的是,如果我在选项卡之间来回切换,内容会正确显示约 1 秒,然后调整内容大小,使其再次被切断。这让我觉得updateNSView 方法需要一些东西,但我不确定是什么。

似乎与here 讨论的问题类似,但这是针对基于 IB 的应用程序,我看不到将其应用于 SwiftUI 的方法。

使用的代码如下。注意:网页视图作为一个属性保存,因此可以被其他方法引用(例如触发页面加载、刷新、返回等)

public struct WebBrowserView 

    private let webView: WKWebView = WKWebView()

    // ...

    public func load(url: URL)         
        webView.load(URLRequest(url: url))
    

    public class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate 

        var parent: WebBrowserView

        init(parent: WebBrowserView) 
            self.parent = parent
        

        public func webView(_: WKWebView, didFail: WKNavigation!, withError: Error) 
            // ...
        

        public func webView(_: WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error) 
            // ...
        

        public func webView(_: WKWebView, didFinish: WKNavigation!) 
            // ...
        

        public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) 
            // ...
        

        public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) 
            decisionHandler(.allow)
        

        public func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? 
            if navigationAction.targetFrame == nil 
                webView.load(navigationAction.request)
            
            return nil
        
    

    public func makeCoordinator() -> Coordinator 
        Coordinator(parent: self)
    



#if os(macOS) // macOS Implementation (iOS version omitted for brevity)
extension WebBrowserView: NSViewRepresentable 

    public typealias NSViewType = WKWebView

    public func makeNSView(context: NSViewRepresentableContext<WebBrowserView>) -> WKWebView 

        webView.navigationDelegate = context.coordinator
        webView.uiDelegate = context.coordinator
        return webView
    

    public func updateNSView(_ nsView: WKWebView, context: NSViewRepresentableContext<WebBrowserView>) 

    

#endif

示例用法:

struct BrowserView: View 

    private let browser = WebBrowserView()

    var body: some View 
        HStack 
            browser
                .onAppear() 
                    self.browser.load(url: URL(string: "https://***.com/tags")!)
                
        
        .padding()
    


struct ContentView: View 

    @State private var selection = 0

    var body: some View  
        TabView(selection: $selection)
            Text("Email View")
                .tabItem 
                    Text("Email")
                
                .tag(0)
            BrowserView()
                .tabItem 
                    Text("Browser")
                
                .tag(1)
        
        .padding()
    

【问题讨论】:

测试您的代码,我得到“'WebBrowserView' 类型的值没有成员'load'。我做错了什么?我将所有这些都粘贴到 ContentView.swift 中? 谢谢@TheNeil!您的建议帮助我获得了我希望运行的浏览器页面,运行。现在我只需要删除“电子邮件/浏览器”拆分,并以某种方式使其显示整个页面而没有任何额外的窗口装饰:) 我非常感谢编辑和建议。我已经在这个问题上摸索了大约一周,试图找到一些“真正有用”的东西,大多数在线教程都是针对 iOS 的 WKWebView。 谢谢! @TheNeil我唯一缺少的基本上是“完整功能”,即当我在浏览器页面上单击“文件浏览器”(上传图像)时,.app 不会向我显示 Finder窗户。如果我能让它运行起来,那就太棒了:) @esaruoho 祝你好运!对不起,但这有点超出我需要的范围,所以我不确定,但如果我遇到解决方案,我会在这里更新。也许检查您应用的所有沙盒设置是否包括文件访问、选择等? 别担心,你帮了大忙!刚刚发现如何使应用程序窗口全尺寸弹出。 :) 只是让它显示网站对我来说是一个巨大的胜利,而你在这方面发挥了重要作用。我想我会在 *** 上提出一些关于其余问题的问题。是的,据我所知,我已经启用了所有沙盒功能。周末好好休息! 【参考方案1】:

我在使用 SwiftUI 的 MacOS 应用程序中遇到了与 WKWebView 完全相同的问题。

对我有用的解决方案是使用 GeometryReader 来获取确切的高度,并将 web 视图放在滚动视图中(我相信它与布局优先级计算有关,但无法触及核心还没有)。

这是对我有用的sn-p,也许它也对你有用

GeometryReader  g in
    ScrollView 
        BrowserView().tabItem 
            Text("Browser")
        
        .frame(height: g.size.height)
        .tag(1)

    .frame(height: g.size.height)

【讨论】:

谢谢!愚蠢的是需要这种解决方法,但至少现在是这样。 希望有一种简单的方法可以在 BrowserView 对象本身中获取它,但不是我能看到的,因为 NSViewRepresentable 不使用任意的 View 返回类型。跨度> 值得注意的是,当满足这两个条件时,通常会出现此问题: - Web 视图不是垂直的第一个视图 - 顶部容器视图大小是根据子视图布局和所需大小计算的至少在 SwiftUI 下一次复习之前我们还有一个工作要做:) 您好,请问这个 sn-p 放在哪里才能正常工作? @esaruoho,用你的 WKWebView NSViewRepresentable 实例替换 BrowserView().tabItem Text("Browser") 【参考方案2】:

webView.edgesIgnoringSafeArea(.all) 似乎可以解决这个问题。

【讨论】:

【参考方案3】:

@Ahmed 和GeometryReader 的解决方案非常优雅,但我找到了一个更短的解决方法。

struct BrowserView: View 
// ...
    var body: some View 
        // ...
        browser.padding(-1.0)
        // ...
    

似乎默认填充(等于 0)会导致此问题。 所以我把它设置为-1。

【讨论】:

以上是关于SwiftUI:WKWebView 在 macOS 上截断页面内容(NSViewRepresentable)的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中关闭 WKWebView

SwiftUI如何监听WKWebView加载网页的进度

如何在 SwiftUI 中观察 WKWebView 的加载进度?

如何在 SwiftUI 中为 WKWebView 创建浏览器选项卡

SwiftUI:使用 WkWebView 突出显示具有不同颜色的文本

在 macOS 中使用 WKWebView 加载 URL