簡體   English   中英

在 macOS 上使用 swiftUI 實現 webkit(並創建網頁預覽)

[英]Implement webkit with swiftUI on macOS (And create a preview of a webpage)

我正在嘗試創建一個顯示該站點收件箱的菜單欄應用程序。 我想制作一個簡單的 function,它會打開一個帶有項目 url 的小彈出窗口(無需打開 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)
    }

}

我想,當有人長按該項目時,它應該像在 macOS 上的默認郵件應用程序中一樣預覽相關網頁,如下所示:

現在彈出窗口工作

打開網站預覽

我嘗試在SafariView中添加一個wkwebview和一個NSViewRepresentable ,但是我使用這個 SO post中的代碼得到了(類似的)以下錯誤消息

Use of undeclared type 'UIViewRepresentable'

TIA!

編輯:

該項目的最基本版本可以在 github 上找到

編輯 2:

更加關注問題

我也試圖想出一個解決方案。 由於我無法在網上找到任何文檔,因此我將提供我通過反復試驗找到的解決方案。

首先,事實證明UI...在 macOS 上有對應的NS... 因此UIViewRepresentable在 macOS 上將是NSViewRepresentable 接下來我發現了這個 SO 問題,它有一個 macOS 上的WKWebview示例。 通過將該代碼與另一個 SO 問題的答案相結合,我還可以檢測到 url 更改以及視圖加載完成的時間。

macOS 上的 SwiftUI WebView

這導致了以下代碼。 為清楚起見,我建議將其放在不同的文件中,例如WebView.swift

首先,導入需要的包:

import SwiftUI
import WebKit
import Combine

然后創建一個 model,其中包含您希望能夠在 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 = ""
    }
}

最后,使用NSViewRepresentable創建struct ,它將成為WebView()的 HostingViewController,如下所示:

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)
        }

    }

}

此代碼可以按如下方式使用:

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)
    }
}

創建一個 Safari 預覽

所以現在我們有了這些知識,重新創建方便的 safari 預覽相對容易。

為此,請確保將@State private var showSafari = false (當您要顯示預覽時將切換)添加到將調用預覽的視圖中。

同時添加.popover(isPresented: self.$showSafari) {...來顯示預覽

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/")
        }
    }
}

現在SafariPreview struct將如下所示:

struct SafariPreview: View {
    @ObservedObject var model: WebViewModel
    init(mesgURL: String) {
        self.model = WebViewModel(link: mesgURL)
    }
    
    var body: some View {
        //Create a VStack that contains the buttons in a preview as well a the webpage itself
        VStack {
            HStack(alignment: .center) {
                Spacer()
                Spacer()
                //The title of the webpage
                Text(self.model.didFinishLoading ? self.model.pageTitle : "")
                Spacer()
                //The "Open with Safari" button on the top right side of the preview
                Button(action: {
                    if let url = URL(string: self.model.link) {
                        NSWorkspace.shared.open(url)
                    }
                }) {
                    Text("Open with Safari")
                }
            }
            //The webpage itself
            SwiftUIWebView(viewModel: model)
        }.frame(width: 800, height: 450, alignment: .bottom)
            .padding(5.0)
    }
}

結果如下所示:

使用 swiftUI 預覽 macOS Safari 網頁

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)
    }
}

用法:

@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)

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM