d6aeede2bc
从桌面工程复制而来,作为本仓库的 Xuxucheck/ 子目录。 为控制仓库体积做了两处删减: - 移除 Frameworks/opencv2.xcframework 下的两个 visionOS 切片 (xros-arm64、xros-arm64-simulator,合计约 106 MB),并同步删掉该 xcframework 内 Info.plist 里对应的两条 AvailableLibraries 记录, 只保留 ios-arm64 与 ios-arm64_x86_64-simulator。 - 移除全部 .DS_Store 与 xcuserdata/。 已用 xcodebuild 从本目录完整编译验证:BUILD SUCCEEDED, ProcessXCFramework 以 --platform ios 成功选中框架,产物含 3699 个 cv:: 符号 (OpenCV 确实链入),嵌入 framework 3 个,与移除无用 pod 之后的状态一致。 仓库约 206 MB,其中 OpenCV 203 MB、其余源码与依赖约 3 MB。
240 lines
8.7 KiB
Swift
240 lines
8.7 KiB
Swift
//
|
|
// ScanView.swift
|
|
// XuXuCheck
|
|
//
|
|
// Created by henseycai on 2025/6/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
@preconcurrency
|
|
import WebKit
|
|
|
|
struct WebViewRepresentable: UIViewRepresentable {
|
|
|
|
var currentUrl: String
|
|
|
|
@Environment(WebViewModel.self) var webViewModel
|
|
|
|
// let viewModel: WebViewModel
|
|
let webView = WKWebView()
|
|
@Environment(LoginStatus.self) var loginStatus
|
|
|
|
func makeCoordinator() -> Coordinator {
|
|
Coordinator(self)
|
|
}
|
|
|
|
func makeUIView(context: Context) -> WKWebView {
|
|
webView.navigationDelegate = context.coordinator
|
|
|
|
// 让网页从 webView 自己的顶端开始渲染。默认的 .automatic 会在 webView 铺到状态栏
|
|
// 下面之后再加一份安全区 inset,内容被二次下推,顶部就多出一条空白带。
|
|
// 它同时决定底部:内容区不再被安全区收窄,所以底部也不会自动让出 34pt,
|
|
// 滚动内容会一直用到 webView 的物理底边。
|
|
// 顶部到底留多少由 XuXuWebView 的 topSafeInset 统一控制;推导见 docs/webview-safe-area.md
|
|
webView.scrollView.contentInsetAdjustmentBehavior = .never
|
|
|
|
if(currentUrl.isEmpty == false){
|
|
webViewModel.hasNewCommand = false
|
|
webViewModel.url = URL(string:currentUrl)
|
|
}
|
|
|
|
if let url = webViewModel.url {
|
|
webView.load(URLRequest(url: url))
|
|
}
|
|
webViewModel.assignWebView(webView: webView)
|
|
return webView
|
|
}
|
|
|
|
func updateUIView(_ uiView: WKWebView, context: Context) {
|
|
}
|
|
|
|
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
|
|
var parent: WebViewRepresentable
|
|
|
|
var webView: WKWebView {
|
|
parent.webView
|
|
}
|
|
var webViewModel: WebViewModel {
|
|
parent.webViewModel
|
|
}
|
|
|
|
init(_ parent: WebViewRepresentable) {
|
|
self.parent = parent
|
|
super.init()
|
|
|
|
// Add addScriptMessageHandler in javascript: window.webkit.messageHandlers.MyObserver.postMessage()
|
|
webView.configuration.userContentController.add(self, name: "receiveData")
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
|
if navigationAction.navigationType == .linkActivated {
|
|
if let url = navigationAction.request.url {
|
|
webView.load(URLRequest(url: url))
|
|
decisionHandler(.cancel)
|
|
return
|
|
}
|
|
}
|
|
decisionHandler(.allow)
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
|
webViewModel.isLoading = true
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
webViewModel.isLoading = false
|
|
}
|
|
|
|
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
|
|
webViewModel.isLoading = false
|
|
webViewModel.error = error
|
|
print("loading error: \(error)")
|
|
}
|
|
|
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
|
let messageFromWebPage = message.body as! String
|
|
|
|
let jsonData = messageFromWebPage.data(using: .utf8)!
|
|
|
|
print(jsonData)
|
|
|
|
do {
|
|
let js2obj = try JSONDecoder().decode(JS2SwiftModel.self, from: jsonData)
|
|
webViewModel.jsParameter = js2obj.parameter
|
|
webViewModel.jsCommand = js2obj.command
|
|
|
|
webViewModel.hasNewCommand = true
|
|
|
|
print(js2obj)
|
|
|
|
print(webViewModel.jsCommand?.lowercased())
|
|
|
|
switch webViewModel.jsCommand?.lowercased(){
|
|
case "print":
|
|
|
|
break
|
|
case "tel":
|
|
|
|
break
|
|
case "clearcache":
|
|
|
|
break
|
|
case "setdefaultmember":
|
|
if let member_data = webViewModel.jsParameter?.member_data {
|
|
Storage.shared.member_id = member_data.member_id ?? 0
|
|
Storage.shared.member_name = member_data.member_name ?? ""
|
|
}
|
|
break
|
|
case "changeself":
|
|
if let dataSelf = webViewModel.jsParameter?.jsSelf {
|
|
Storage.shared.userid = dataSelf.user_id ?? 0
|
|
Storage.shared.mobile = dataSelf.user_mobile ?? ""
|
|
Storage.shared.email = dataSelf.user_email ?? ""
|
|
Storage.shared.username = dataSelf.username ?? ""
|
|
}
|
|
break
|
|
case "goback":
|
|
|
|
|
|
|
|
break
|
|
case "scanpaper":
|
|
|
|
|
|
break
|
|
case "logout":
|
|
Storage.shared.email = ""
|
|
Storage.shared.username = ""
|
|
Storage.shared.isLogin = false
|
|
Storage.shared.member_id = 0
|
|
Storage.shared.mobile = ""
|
|
Storage.shared.userid = 0
|
|
Storage.shared.usertoken = ""
|
|
Storage.shared.member_name = ""
|
|
|
|
//can do this
|
|
parent.loginStatus.isLogin = false
|
|
|
|
break
|
|
case "login":
|
|
Storage.shared.email = ""
|
|
Storage.shared.username = ""
|
|
Storage.shared.isLogin = false
|
|
Storage.shared.member_id = 0
|
|
Storage.shared.mobile = ""
|
|
Storage.shared.userid = 0
|
|
Storage.shared.usertoken = ""
|
|
Storage.shared.member_name = ""
|
|
|
|
//can do this
|
|
parent.loginStatus.routeToLoginView = true
|
|
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
|
|
|
|
|
|
}catch{
|
|
print("error decoding json:\(error)")
|
|
}
|
|
|
|
|
|
// if messageFromWebPage == "index" {
|
|
// viewModel.backButtonIsEnabled = false
|
|
// viewModel.loadFile(name: messageFromWebPage)
|
|
// return
|
|
// }
|
|
// if messageFromWebPage == "goPage" {
|
|
// viewModel.backButtonIsEnabled = true
|
|
// return
|
|
// }
|
|
//
|
|
// let alertController = UIAlertController(title: "Javascript said:", message: messageFromWebPage, preferredStyle: .alert)
|
|
// let okAction = UIAlertAction(title: "OK", style: .default) { (_) in
|
|
// print("OK")
|
|
// }
|
|
// alertController.addAction(okAction)
|
|
// guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
// let rootViewController = windowScene.windows.first?.rootViewController else {
|
|
// return
|
|
// }
|
|
// rootViewController.present(alertController, animated: true)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
/// Callback from javascript: window.webkit.messageHandlers.MyObserver.postMessage(message)
|
|
// func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
|
// let messageFromWebPage = message.body as! String
|
|
//
|
|
// if messageFromWebPage == "index" {
|
|
// viewModel.backButtonIsEnabled = false
|
|
// viewModel.loadFile(name: messageFromWebPage)
|
|
// return
|
|
// }
|
|
// if messageFromWebPage == "goPage" {
|
|
// viewModel.backButtonIsEnabled = true
|
|
// return
|
|
// }
|
|
//
|
|
// let alertController = UIAlertController(title: "Javascript said:", message: messageFromWebPage, preferredStyle: .alert)
|
|
// let okAction = UIAlertAction(title: "OK", style: .default) { (_) in
|
|
// print("OK")
|
|
// }
|
|
// alertController.addAction(okAction)
|
|
// guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
|
// let rootViewController = windowScene.windows.first?.rootViewController else {
|
|
// return
|
|
// }
|
|
// rootViewController.present(alertController, animated: true)
|
|
// }
|
|
|
|
|
|
}
|
|
}
|