如何在 SwiftUI 中为 WKWebView 创建浏览器选项卡 [英] How to create a browser tab for WKWebView in SwiftUI

查看:33
本文介绍了如何在 SwiftUI 中为 WKWebView 创建浏览器选项卡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试构建一个新的选项卡功能,但我不太确定如何实现这一点.我在设置新的或以前的 WKWebView 时遇到问题.如果 url 无效,我如何显示错误视图?

这是我目前所拥有的.

我不太确定如何初始化或如何创建一个 invalidurl 视图.这有点像我脑子里在想什么

class NavigationState : NSObject, ObservableObject {@Published var url : URL?让 webView = WKWebView()}扩展导航状态:WKNavigationDelegate {func webView(_ webView: WKWebView, didCommit 导航: WKNavigation!) {self.url = webView.url}}结构 WebView : UIViewRepresentable {让请求:URLRequestvar navigationState : NavigationStatefunc makeUIView(context: Context) ->WKWebView {让 webView = navigationState.webViewwebView.navigationDelegate = navigationStatewebView.load(请求)返回网页视图}func updateUIView(_ uiView: WKWebView, context: Context) { }}结构内容视图:查看{@StateObject var navigationState = NavigationState()@State var tablist = [NavigationState]@State var validurl = true;在里面(){//当前不起作用navigationState.createNewWebView(withRequest: URLRequest(url: URL(string: "https://www.google.com")!))}var主体:一些视图{VStack(){按钮(创建新标签"){tablist.append(navigationState)//创建并设置新的webview}文本(navigationState.url?.absoluteString ?? "(none)")如果(有效网址){WebView(请求:URLRequest(url:URL(字符串:https://www.google.com")!),导航状态:导航状态)} else{InvalidURL()}堆栈{按钮(返回"){导航状态.webView.goBack()}按钮(前进"){导航状态.webView.goForward()}文本字段(){onCommit:{navigationState.selectedWebView?.load(URLRequest(url: URL(string: urlInput)!))}}}}列表 {ForEach(tabs, id: \.self) { tab in按钮(动作:{//设置为当前的webview}, 标签: {文本(tab.webView.url)})}.onDelete(执行:删除)}}}

编辑初始化

我在 NavigationState 下面添加了这段代码,但我一直得到一个空白屏幕.

覆盖 init(){超级初始化()让 wv = WKWebView()wv.navigationDelegate = selfself.webViews.append(wv)self.selectedWebView = wvwv.load(URLRequest(url: URL(string: "https://www.google.com")!))}

解决方案

这里是一个比较简单的实现(先写代码,再解释):

<预><代码>类导航状态:NSObject,ObservableObject {@Published var currentURL : URL?@Published var webViews : [WKWebView] = []@Published var selectedWebView : WKWebView?@discardableResult func createNewWebView(withRequest request: URLRequest) ->WKWebView {让 wv = WKWebView()wv.navigationDelegate = selfwebViews.append(wv)selectedWebView = wvwv.load(请求)返回 wv}}扩展导航状态:WKNavigationDelegate {func webView(_ webView: WKWebView, didCommit 导航: WKNavigation!) {如果 webView == selectedWebView {self.currentURL = webView.url}}}结构 WebView : UIViewRepresentable {@ObservedObject var navigationState : NavigationStatefunc makeUIView(context: Context) ->界面视图{返回 UIView()}func updateUIView(_ uiView: UIView, context: Context) {守卫让 webView = navigationState.selectedWebView else {返回}如果 webView != uiView.subviews.first {uiView.subviews.forEach { $0.removeFromSuperview() }webView.frame = CGRect(origin: .zero, size: uiView.bounds.size)uiView.addSubview(webView)}}}结构内容视图:查看{@StateObject var navigationState = NavigationState()var主体:一些视图{VStack(){按钮(创建新标签"){navigationState.createNewWebView(withRequest: URLRequest(url: URL(string: "https://www.google.com")!))}文本(navigationState.currentURL?.absoluteString ?? "(none)")WebView(导航状态:导航状态).剪辑()堆栈{按钮(返回"){navigationState.selectedWebView?.goBack()}按钮(前进"){navigationState.selectedWebView?.goForward()}}列表 {ForEach(navigationState.webViews, id: \.self) { tab in按钮(动作:{navigationState.selectedWebView = tab}) {文本(tab.url?.absoluteString ???")}}}}}}

我没有尝试存储 NavigationState 数组,而是重构了 NavigationState 以保存 Web 视图数组.当前 URL 和选中的 web view 是 @Published 值,这样父视图就可以看到 URL、选中的 view 等

WebView 必须进行重大更改,因为它必须更新在任何给定时间显示的 WKWebView.

这是非常粗略的边缘代码.如果这是我自己的项目,我会做更多的重构,但这应该能让你开始.

关于显示无效 URL 的错误,这实际上是第二个问题,可能需要更清楚地说明(什么构成无效 URL?它来自哪里?您的意思是只要用户输入一个(在 UI 的某些部分)您没有描述)或者他们是否点击了页面上的无效链接?)

I am trying to build a new tab function but I am not too sure how I can accomplish this. I am having trouble setting a new or previous WKWebView. And also how do I display an errorView if the url is invalid?

This is what I have so far.

EDIT: I wasn't too sure how to initialize or how to create a invalidurl view. This is kind of like whats going on through my mind

class NavigationState : NSObject, ObservableObject {
    @Published var url : URL?
    let webView = WKWebView()
}

extension NavigationState : WKNavigationDelegate {
    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        self.url = webView.url
    }
}

struct WebView : UIViewRepresentable {
    
    let request: URLRequest
    var navigationState : NavigationState
    
    func makeUIView(context: Context) -> WKWebView  {
        let webView = navigationState.webView
        webView.navigationDelegate = navigationState
        webView.load(request)
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) { }
}

struct ContentView: View {
    @StateObject var navigationState = NavigationState()
    @State var tablist = [NavigationState]
    @State var validurl = true;

    init(){
      //does not work currently
      navigationState.createNewWebView(withRequest: URLRequest(url: URL(string: "https://www.google.com")!))
    }
    
    var body: some View {
        VStack(){
            Button("create new tab"){
               tablist.append(navigationState)
               //create and set new webview
            }
            Text(navigationState.url?.absoluteString ?? "(none)")
           if(validUrl){
            WebView(request: URLRequest(url: URL(string: "https://www.google.com")!), navigationState: navigationState)
            } else{InvalidURL()}
            HStack {
                Button("Back") {
                    navigationState.webView.goBack()
                }
                Button("Forward") {
                    navigationState.webView.goForward()
                }
              TextField(){onCommit: {    
                navigationState.selectedWebView?.load(URLRequest(url: URL(string: urlInput)!))
             }}
            }
        }
         List {
                ForEach(tabs, id: \.self) { tab in
                    Button(action: {
                        //set to current webview
                    }, label: {
                        Text(tab.webView.url)
                    })
                }.onDelete(perform: delete)
            }
    }
}

EDIT for the initlization

I added this block of code underneath the NavigationState but I keep getting a blank screen.

override init(){
        super.init()
        let wv = WKWebView()
        wv.navigationDelegate = self
        self.webViews.append(wv)
        self.selectedWebView = wv
        wv.load(URLRequest(url: URL(string: "https://www.google.com")!))
    }

解决方案

Here's a relatively simple implementation (code first, then explanation):


class NavigationState : NSObject, ObservableObject {
    @Published var currentURL : URL?
    @Published var webViews : [WKWebView] = []
    @Published var selectedWebView : WKWebView?
    
    @discardableResult func createNewWebView(withRequest request: URLRequest) -> WKWebView {
        let wv = WKWebView()
        wv.navigationDelegate = self
        webViews.append(wv)
        selectedWebView = wv
        wv.load(request)
        return wv
    }
}

extension NavigationState : WKNavigationDelegate {
    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        if webView == selectedWebView {
            self.currentURL = webView.url
        }
    }
}

struct WebView : UIViewRepresentable {
    
    @ObservedObject var navigationState : NavigationState
    
    func makeUIView(context: Context) -> UIView  {
        return UIView()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        guard let webView = navigationState.selectedWebView else {
            return
        }
        if webView != uiView.subviews.first {
            uiView.subviews.forEach { $0.removeFromSuperview() }
            
            webView.frame = CGRect(origin: .zero, size: uiView.bounds.size)
            uiView.addSubview(webView)
        }
    }
}

struct ContentView: View {
    @StateObject var navigationState = NavigationState()
    
    var body: some View {
        VStack(){
            Button("create new tab"){
                navigationState.createNewWebView(withRequest: URLRequest(url: URL(string: "https://www.google.com")!))
            }
            Text(navigationState.currentURL?.absoluteString ?? "(none)")
            WebView(navigationState: navigationState)
                .clipped()
            HStack {
                Button("Back") {
                    navigationState.selectedWebView?.goBack()
                }
                Button("Forward") {
                    navigationState.selectedWebView?.goForward()
                }
            }
            
            List {
                ForEach(navigationState.webViews, id: \.self) { tab in
                    Button(action: {
                        navigationState.selectedWebView = tab
                    }) {
                        Text(tab.url?.absoluteString ?? "?")
                    }
                }
            }
        }
        
    }
}

Instead of trying to store an array of NavigationStates, I refactored NavigationState to hold an array of web views. The current URL and selected web view are @Published values so that the parent view can see the URL, the selected view, etc.

WebView had to be changed significantly since it had to update which WKWebView is being shown at any given time.

This is pretty rough-around-the edges code. I'd do more refactoring if it were my own project, but this should get you started.

Regarding showing errors with invalid URLs, that's really a second question and probably needs more clarity (what constitutes an invalid URL? Where is it coming from? Do you mean just if the user enters one (in some part of the UI that you're not describing) or also if they click on an invalid link on the page?)

这篇关于如何在 SwiftUI 中为 WKWebView 创建浏览器选项卡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆