SwiftUI打开网页

所属分类:ios | 发布于 2023-06-05 23:47:17

在做iOS开发中,打开网页是个常见的需求,比如,要打开的用户协议和隐私政策等页面是h5页面。

打开浏览器主要有两种场景:

1、通过外部浏览器打开网页

2、在App内部打开网页

本篇我们就基于Swift和SwiftUI,分别实现这两种场景。

通过外部浏览器打开网页

目前看到的是有两种方法来打开外部浏览器

方法一:使用Link

struct ContentView: View {
    var body: some View {
        Link("Baidu", destination: URL(string: "https://baidu.com")!)
    }
}

方法二:使用环境变量openUrl

struct OpenBrowserWithOpenURL: View {
    @Environment(\.openURL) var openURL
    var body: some View {
        Button("Open Baidu") {
            openURL(URL(string: "https://baidu.com")!)
        }
    }
}

在App内部打开网页

在SwiftUI中,没有打开网页的接口,要实现打开网页的功能,需要借助WebKit组件桥接WKWebView。

1、桥接WKWebView初步使用

这里分为两步

1.1、创建WebView结构体

创建WebView结构体,实现UIViewRepresentable协议,用来桥接WKWebView

import SwiftUI
// 1
import WebKit

struct WebView: UIViewRepresentable {
    // 2
    let url: URL

    
    // 3
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    
    // 4
    func updateUIView(_ webView: WKWebView, context: Context) {
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

1.2、使用WebView打开网页

在创建实现了UIViewRepresentable协议的WebView结构体后,就可以向使用其它View组件一样使用它。

struct ContentView: View {
    // 1
    @State private var isPresentWebView = false

    var body: some View {
        Button("Open WebView") {
            // 2
            isPresentWebView = true
        }
        .sheet(isPresented: $isPresentWebView) {
            NavigationStack {
                // 3
                WebView(url: URL(string: "https://baidu.com")!)
                    .ignoresSafeArea()
                    .navigationTitle("Baidu In App")
                    .navigationBarTitleDisplayMode(.inline)
            }
        }
    }
}

1.3、存在的问题

上面的代码是可以打开网页了,但是一个问题:无法关闭sheet。

  1. This method should not be called on the main thread as it may lead to UI unresponsiveness.

  2. 无法关闭sheet

在App内打开网页进阶版

在上面我已经实现了最基础了桥接WebKit下的WKWebView来打开网页,但实际使用的时候,我们还需要一些精细化的控制,大白话就是app与打开的网页的交互。

  • 通过@Binding属性,可以将值从SwiftUI传递到UIView控件
  • 反过来,SwiftUI想要获取UIView控件的数据,那就需要使用Coordinator

关于UIViewRepresentable和Coordinator的介绍和使用,可以看这篇文章:SwiftUI中使用"UIViewRepresentable"桥接UIKit View

进阶版会引入WebViewModel类,这是一个ObservableObject类,进阶版的使用也分为三步:

1、创建WebViewModel类

import Foundation
import WebKit

class WebViewModel: ObservableObject {
    
    @Published var isLoading: Bool = false
    
    @Published var canGoBack: Bool = false
    
    @Published var shouldGoBack: Bool = false
    
    @Published var title: String = ""
    
    var urlString = ""
    
    
    func setUrlString(urlString: String) {
        self.urlString = urlString
    }
}

2、创建WebView结构体

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    
    @ObservedObject var model: WebViewModel
    
    func makeUIView(context: Context) -> WKWebView {
        guard let url = URL(string: self.model.urlString) else {
            return WKWebView()
        }
        let request = URLRequest(url: url)
        let webView = WKWebView()
        webView.navigationDelegate = context.coordinator
        webView.load(request)
        return webView
    }
    
    func updateUIView(_ webView: WKWebView, context: Context) {
        if model.shouldGoBack {
            webView.goBack()
            model.setShouldGoBackToFalse()
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self, model)
    }
    
}

extension WebView {
    class Coordinator: NSObject, WKNavigationDelegate {
        @ObservedObject private var model: WebViewModel
        private let parent: WebView
        
        init(_ parent: WebView, _ model: WebViewModel) {
            self.parent = parent
            self.model = model
        }
        
        func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            model.isLoading = true
        }
        
        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            model.isLoading = false
            model.title = webView.title ?? ""
            model.canGoBack = webView.canGoBack
        }
        
        func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
            model.isLoading = false
        }
    }
}

3、使用WebView打开网页

文哥博客(https://wenge365.com)属于文野个人博客,欢迎浏览使用

联系方式:qq:52292959 邮箱:52292959@qq.com

备案号:粤ICP备18108585号 友情链接