在 macOS 上使用 swiftUI 实现 webkit(并创建网页预览)

Chi*_*iel 9 macos webkit swift swiftui nsviewcontrollerrepresentable

我正在尝试创建一个菜单栏应用程序来显示该网站的收件箱。我想制作一个简单的功能,打开一个带有项目网址的小弹出窗口(无需打开 safari)。收件箱项目看起来像这样

struct InboxItem: View {
    @State var MesgSite: String = "https://duckduckgo.com"
    @State private var showSafari = false    
   
    var body: some View {
        VStack(alignment: .leading) {
            Text("some text")
        .background(SelectColor.opacity(0.5))
        .onLongPressGesture {
           //show preview of the MesgSite here
            self.SelectColor = .blue
            self.showSafari.toggle()
        }.popover(isPresented: self.$showSafari) {
            SafariPreview()
        }
    }
}

struct SafariPreview: View {

    var body: some View {
        VStack {
            Text("Display the webpage here")
            .padding()
        }.frame(maxWidth: 533, maxHeight: 300)
    }

}

Run Code Online (Sandbox Code Playgroud)

我想,当一个人长按该项目时,它应该预览关联的网页,就像 macOS 上的默认邮件应用程序一样,如下所示:

现在弹出窗口可以工作了

打开网站预览

我尝试在 a 中添加 awkwebview和 a ,但是我使用此 SO 帖子中的代码收到(类似的)以下错误消息SafariViewNSViewRepresentable

Use of undeclared type 'UIViewRepresentable'

蒂亚!

编辑:

该项目最基本的版本可以在 github 上找到

编辑2:

更加关注问题

Chi*_*iel 13

我也尝试想出一个解决方案。由于我在网上找不到任何有关此内容的文档,因此我将提供通过反复试验找到的解决方案。

首先,事实证明,UI...macOS 上有一个对应的版本,称为NS.... UIViewRepresentable在 macOS 上也是如此NSViewRepresentable。接下来我发现了这个SO问题,其中有一个WKWebview关于macOS的示例。通过将该代码与另一个 SO 问题的答案结合起来,我还可以检测到 url 更改,并知道视图何时完成加载。

macOS 上的 SwiftUI WebView

这导致了以下代码。为了清楚起见,我建议将其放在不同的文件中,例如WebView.swift

首先导入需要的包:

import SwiftUI
import WebKit
import Combine
Run Code Online (Sandbox Code Playgroud)

然后创建一个模型来保存您希望能够在 SwiftUI 视图中访问的数据:

class WebViewModel: ObservableObject {
    @Published var link: String
    @Published var didFinishLoading: Bool = false
    @Published var pageTitle: String
    
    init (link: String) {
        self.link = link
        self.pageTitle = ""
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,创建structHostingViewController NSViewRepresentable,如下所示WebView()

struct SwiftUIWebView: NSViewRepresentable {
    
    public typealias NSViewType = WKWebView
    @ObservedObject var viewModel: WebViewModel

    private let webView: WKWebView = WKWebView()
    public func makeNSView(context: NSViewRepresentableContext<SwiftUIWebView>) -> WKWebView {
        webView.navigationDelegate = context.coordinator
        webView.uiDelegate = context.coordinator as? WKUIDelegate
        webView.load(URLRequest(url: URL(string: viewModel.link)!))
        return webView
    }

    public func updateNSView(_ nsView: WKWebView, context: NSViewRepresentableContext<SwiftUIWebView>) { }

    public func makeCoordinator() -> Coordinator {
        return Coordinator(viewModel)
    }
    
    class Coordinator: NSObject, WKNavigationDelegate {
        private var viewModel: WebViewModel

        init(_ viewModel: WebViewModel) {
           //Initialise the WebViewModel
           self.viewModel = viewModel
        }
        
        public func webView(_: WKWebView, didFail: WKNavigation!, withError: Error) { }

        public func webView(_: WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error) { }

        //After the webpage is loaded, assign the data in WebViewModel class
        public func webView(_ web: WKWebView, didFinish: WKNavigation!) {
            self.viewModel.pageTitle = web.title!
            self.viewModel.link = web.url?.absoluteString as! String
            self.viewModel.didFinishLoading = true
        }

        public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { }

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

    }

}
Run Code Online (Sandbox Code Playgroud)

该代码可以按如下方式使用:

struct ContentView: View {
    var body: some View {
        //Pass the url to the SafariWebView struct.
        SafariWebView(mesgURL: "https://stackoverflow.com/")
    }
}
struct SafariWebView: View {
    @ObservedObject var model: WebViewModel

    init(mesgURL: String) {
        //Assign the url to the model and initialise the model
        self.model = WebViewModel(link: mesgURL)
    }
    
    var body: some View {
        //Create the WebView with the model
        SwiftUIWebView(viewModel: model)
    }
}
Run Code Online (Sandbox Code Playgroud)

创建 Safari 预览

现在我们有了这些知识,重新创建方便的 safari 预览就相对容易了。

为此,请确保添加@State private var showSafari = false(当您想要显示预览时将切换)到将调用预览的视图。

还添加.popover(isPresented: self.$showSafari) { ...来显示预览

struct ContentView: View {
    var body: some View {
        //Pass the url to the SafariWebView struct.
        SafariWebView(mesgURL: "https://stackoverflow.com/")
    }
}
struct SafariWebView: View {
    @ObservedObject var model: WebViewModel

    init(mesgURL: String) {
        //Assign the url to the model and initialise the model
        self.model = WebViewModel(link: mesgURL)
    }
    
    var body: some View {
        //Create the WebView with the model
        SwiftUIWebView(viewModel: model)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在SafariPreview struct将如下所示:

struct ContentView: View {
    @State private var showSafari = false

    var body: some View {
        VStack(alignment: .leading) {
            Text("Press me to get a preview")
            .padding()
        }
        .onLongPressGesture {
            //Toggle to showSafari preview
            self.showSafari.toggle()
        }//if showSafari is true, create a popover
        .popover(isPresented: self.$showSafari) {
            //The view inside the popover is made of the SafariPreview
            SafariPreview(mesgURL: "https://duckduckgo.com/")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

结果如下:

使用 swiftUI 预览 macOS Safari 网页


And*_*rew 5

Chiel的回答很棒!

但是,可能有人在没有完整浏览器功能的情况下搜索 html 渲染器视图

适用于 MacOS

import SwiftUI
import WebKit

struct WebView: View {
    @Binding var html: String
    
    var body: some View {
        WebViewWrapper(html: html)
    }
}

struct WebViewWrapper: NSViewRepresentable {
    let html: String
    
    func makeNSView(context: Context) -> WKWebView {
        return WKWebView()
    }
    
    func updateNSView(_ nsView: WKWebView, context: Context) {
        nsView.loadHTMLString(html, baseURL: nil)
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

@State var text = "<html><body><h1>Hello World</h1><p><strong>hell</strong> yeah!</p></body></html>"
//......

TextField("", text: $text)
        
Divider()

WebView(html: $text)
    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述