问题描述
我已经实现了一个加载器,当我的 web 视图加载页面时向用户显示一个旋转的圆圈。当我第一次在应用程序中加载 webview 并在 webview 中的页面之间浏览时,这非常有效。
然而,有一种情况下 Loader 永远不会消失:如果应用程序是通过通用链接启动的:在这种情况下,即使我浏览 WebView 中的其他几个页面,Loader 也会永远停留。就像没有检测到 WebView 完成加载一样。
这是我的代码:
WebsiteView.swift
//
// WebsiteView.swift
//
import SwiftUI
struct WebsiteView: View {
@EnvironmentObject var viewRouter: ViewRouter
@Observedobject var viewmodel = viewmodel()
@State var showLoader = false
var body: some View {
ZStack {
vstack(spacing: 0) {
WebView(viewmodel: viewmodel).overlay (
RoundedRectangle(cornerRadius: 4,style: .circular)
.stroke(Color.gray,linewidth: 0.5)
).padding(.leading,0).padding(.trailing,0).padding(.top,0)
//webViewNavigationBar
}.onReceive(self.viewmodel.showLoader.receive(on: RunLoop.main)) { value in
self.showLoader = value
}
// A simple loader that is shown when WebView is loading any page and hides when loading is finished.
if showLoader {
Loader()
}
}
}
}
struct websiteView_Previews: PreviewProvider {
static var previews: some View {
WebsiteView().environmentObject(ViewRouter())
}
}
Loader.swift
//
// Loader.swift
//
import SwiftUI
struct Loader: View {
@State var spinCircle = false
var body: some View {
ZStack {
Circle()
.stroke(Color.white,linewidth: 18)
.frame(width: 70,height: 70)
Circle()
.stroke(Color(.systemGray5),linewidth: 14)
.frame(width: 70,height: 70)
Circle()
.trim(from: 0,to: 0.2)
.stroke(Color.green,linewidth: 7)
.frame(width: 70,height: 70)
.rotationEffect(Angle(degrees: spinCircle ? 360 : 0))
.animation(Animation.linear(duration: 1).repeatForever(autoreverses: false))
.onAppear() {
self.spinCircle = true
}
}
}
}
struct Loader_Previews: PreviewProvider {
static var previews: some View {
Loader()
}
}
WebView.swift
//
// WebView.swift
//
import Foundation
import UIKit
import SwiftUI
import Combine
import WebKit
import CoreLocation
// MARK: - WebViewHandlerDelegate
// For printing values received from web app
protocol WebViewHandlerDelegate {
func receivedJsonValueFromWebView(value: [String: Any?])
func receivedStringValueFromWebView(value: String)
}
// MARK: - WebView
struct WebView: UIViewRepresentable,WebViewHandlerDelegate {
@EnvironmentObject var viewRouter: ViewRouter
func receivedJsonValueFromWebView(value: [String : Any?]) {
print("JSON value received from web is: \(value)")
}
func receivedStringValueFromWebView(value: String) {
print("String value received from web is: \(value)")
}
// viewmodel object
@Observedobject var viewmodel: viewmodel
// Make a coordinator to co-ordinate with WKWebView's default delegate functions
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
// Customize the webView configuration
let preferences = WKPreferences()
preferences.javaScriptCanopenWindowsAutomatically = true
let configuration = WKWebViewConfiguration()
configuration.userContentController.add(self.makeCoordinator(),name: "iOSNative")
configuration.preferences = preferences
configuration.allowsInlinemediaplayback = true
// Create the webView
let webView = WKWebView(frame: CGRect.zero,configuration: configuration)
webView.navigationDelegate = context.coordinator
webView.allowsBackForwardNavigationGestures = true
webView.scrollView.isScrollEnabled = true
viewRouter.navigatorGeolocation.setUserContentController(webViewConfiguration: configuration)
viewRouter.navigatorGeolocation.setWebView(webView: webView)
return webView
}
func updateUIView(_ webView: WKWebView,context: Context) {
if !viewRouter.nextPost.isEmpty {
let urlString = viewRouter.nextDomain + "/" + viewRouter.nextPage
var request = URLRequest(url: URL(string: urlString)!)
request.addValue("application/x-www-form-urlencoded",forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = viewRouter.nextPost.data(using: .utf8)
webView.load(request)
} else {
let urlString = viewRouter.nextDomain + "/" + viewRouter.nextPage
let request = URLRequest(url: URL(string: urlString)!)
webView.load(request)
}
}
// Function to go back to login page
func gotoLoginView(webView: WKWebView)
{
viewRouter.currentPage = .loginPage
}
class Coordinator : NSObject,WKNavigationDelegate {
var parent: WebView
var delegate: WebViewHandlerDelegate?
var valueSubscriber: AnyCancellable? = nil
var webViewNavigationSubscriber: AnyCancellable? = nil
init(_ WKWebView: WebView) { // was uiWebview
self.parent = WKWebView // was uiWebview
self.delegate = parent
}
deinit {
valueSubscriber?.cancel()
webViewNavigationSubscriber?.cancel()
}
func webView(_ webView: WKWebView,didFinish navigation: WKNavigation!) {
// Page loaded so no need to show loader anymore
self.parent.viewmodel.showLoader.send(false)
}
/* Here I implemented most of the WKWebView's delegate functions so that you can kNow them and
can use them in different necessary purposes */
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
// Hides loader
self.parent.viewmodel.showLoader.send(false)
}
func webView(_ webView: WKWebView,didFail navigation: WKNavigation!,withError error: Error) {
// Hides loader
self.parent.viewmodel.showLoader.send(false)
}
func webView(_ webView: WKWebView,didCommit navigation: WKNavigation!) {
// Shows loader
self.parent.viewmodel.showLoader.send(true)
}
func webView(_ webView: WKWebView,didStartProvisionalNavigation navigation: WKNavigation!) {
// Shows loader
self.parent.viewmodel.showLoader.send(true)
}
// This function is essential for intercepting every navigation in the webview
func webView(_ webView: WKWebView,decidePolicyFor navigationAction: WKNavigationAction,decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
// Suppose you don't want your user to go a restricted site
// Here you can get many information about new url from 'navigationAction.request.description'
decisionHandler(.allow)
return
}
}
}
// MARK: - Extensions
extension WebView.Coordinator: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController,didReceive message: WKScriptMessage) {
// Make sure that your passed delegate is called
if message.name == "iOSNative" {
if let body = message.body as? [String: Any?] {
delegate?.receivedJsonValueFromWebView(value: body)
} else if let body = message.body as? String {
delegate?.receivedStringValueFromWebView(value: body)
}
}
}
}
appMyApp.swift
//
// appApp.swift
//
import SwiftUI
@main
struct appApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject var viewRouter = ViewRouter()
var body: some Scene {
WindowGroup {
MotherView().environmentObject(viewRouter)
.onopenURL { url in
var urlPage = ""
let urlString = url.absoluteString
let patternUrl = "^(http|https):\\/\\/(.*).example.com\\/(?<page>.*)$"
let regex = try! NSRegularExpression(pattern: patternUrl,options: .caseInsensitive)
if let match = regex.firstMatch(in: urlString,options: [],range: NSRange(location: 0,length: urlString.utf16.count)) {
if let pageRange = Range(match.range(withName: "page"),in: urlString) {
urlPage = String(urlString[pageRange])
}
}
viewRouter.nextPage = urlPage
viewRouter.currentPage = .websitePage
}
}
}
}
ViewRouter.swift
//
// ViewRouter.swift
//
import SwiftUI
import WebKit
class ViewRouter: ObservableObject {
@Published var currentPage: Page = .loginPage
// Language
// URL String and Post data that will be called next time WebsiteView is loaded
var nextDomain: String = "https://www.example.com"
var nextPage: String = ""
// Manages Geolocation
var navigatorGeolocation = NavigatorGeolocation()
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)