// // BaseWebViewUIViewController.swift // O2Platform // // Created by 刘振兴 on 2016/10/11. // Copyright © 2016年 zoneland. All rights reserved. // import UIKit import WebKit import CocoaLumberjack import QuickLook protocol BaseWebViewUIViewControllerJSDelegate { func closeUIViewWindow() func actionBarLoaded(show: Bool) } protocol O2WKScriptMessageHandlerImplement { func userController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) } class BaseWebViewUIViewController: O2BaseForRotateUIViewController { var webView:WKWebView! var delegate: BaseWebViewUIViewControllerJSDelegate? var o2WKScriptHandlers: [String : O2WKScriptMessageHandlerImplement] = [:] //定位用 var locationCallBack: (String?) -> Void = {(result) in } private var locService: BMKLocationManager? private var searchAddress: BMKGeoCodeSearch? //继承的子类如果要添加MessageHandler必须在theWebView方法前使用 func addScriptMessageHandler(key: String, handler: O2WKScriptMessageHandlerImplement) { o2WKScriptHandlers[key] = handler } open func theWebView(){ let baseJsHandler = O2BaseJsMessageHandler(viewController: self) // bbs 回复 addScriptMessageHandler(key: "ReplyAction", handler: baseJsHandler) // protal已经存在ActionBar addScriptMessageHandler(key: "actionBarLoaded", handler: baseJsHandler) // 打开工作 {"work":"", "workCompleted":"", "title":""} addScriptMessageHandler(key: "openO2Work", handler: baseJsHandler) // 4个分类 task taskCompleted read readCompleted addScriptMessageHandler(key: "openO2WorkSpace", handler: baseJsHandler) // 打开cms appId addScriptMessageHandler(key: "openO2CmsApplication", handler: baseJsHandler) // 打开cms docId docTitle addScriptMessageHandler(key: "openO2CmsDocument", handler: baseJsHandler) // 打开meeting addScriptMessageHandler(key: "openO2Meeting", handler: baseJsHandler) // 打开 calendar addScriptMessageHandler(key: "openO2Calendar", handler: baseJsHandler) // 打开扫一扫 addScriptMessageHandler(key: "openScan", handler: baseJsHandler) addScriptMessageHandler(key: "openO2Alert", handler: baseJsHandler) // 打开钉钉 addScriptMessageHandler(key: "openDingtalk", handler: baseJsHandler) // 关闭当前UIViewController addScriptMessageHandler(key:"closeNativeWindow", handler: baseJsHandler) // 表单的图片控件 addScriptMessageHandler(key: "uploadImage2FileStorage", handler: baseJsHandler) // 打印日志 addScriptMessageHandler(key: "o2mLog", handler: baseJsHandler) //o2m.notification let o2Notification = O2JsApiNotification(viewController: self) addScriptMessageHandler(key: "o2mNotification", handler: o2Notification) //o2m.util let o2Util = O2JsApiUtil(viewController: self) addScriptMessageHandler(key: "o2mUtil", handler: o2Util) // o2m.biz let biz = O2JsApiBizUtil(viewController: self) addScriptMessageHandler(key: "o2mBiz", handler: biz) setupWebView() } public func setupWebView() { let userContentController = WKUserContentController() //cookie脚本 if let cookies = HTTPCookieStorage.shared.cookies { let script = getJSCookiesString(cookies: cookies) let cookieScript = WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: false) userContentController.addUserScript(cookieScript) } // console.log 打印到ios log debug用 let consoleLog = "console.log = (function(oriLogFunc){return function(str){window.webkit.messageHandlers.o2mLog.postMessage(str);oriLogFunc.call(console,str);} })(console.log);" let logScript = WKUserScript(source: consoleLog, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: false) userContentController.addUserScript(logScript) let webViewConfig = WKWebViewConfiguration() webViewConfig.userContentController = userContentController //加入js-app message handler for item in o2WKScriptHandlers { userContentController.add(self, name: item.key) } self.webView = WKWebView(frame: self.view.frame, configuration: webViewConfig) /// 设置 userAgent self.webView.evaluateJavaScript("navigator.userAgent") { (result, err) in if let ua = result as? String { self.webView.customUserAgent = "\(ua) o2oa" } } } ///Generates script to create given cookies public func getJSCookiesString(cookies: [HTTPCookie]) -> String { var result = "" let dateFormatter = DateFormatter() dateFormatter.timeZone = NSTimeZone(abbreviation: "UTC") as TimeZone? dateFormatter.dateFormat = "EEE, d MMM yyyy HH:mm:ss zzz" for cookie in cookies { result += "document.cookie='\(cookie.name)=\(cookie.value); domain=\(cookie.domain); path=\(cookie.path); " if let date = cookie.expiresDate { result += "expires=\(dateFormatter.string(from: date)); " } if (cookie.isSecure) { result += "secure; " } result += "'; " } return result } // MARK: - 文件预览 //预览文件 private lazy var previewVC: CloudFilePreviewController = { return CloudFilePreviewController() }() // 文件预览 func previewDoc(path: String) { let currentURL = NSURL(fileURLWithPath: path) if QLPreviewController.canPreview(currentURL) { self.previewVC.currentFileURLS.removeAll() self.previewVC.currentFileURLS.append(currentURL) self.previewVC.reloadData() self.pushVC(self.previewVC) } else { self.showError(title: "当前文件类型不支持预览!") } } // MARK: - 旋转屏幕 // 此参数由外部传入,并且在要在构造控制器时传入 fileprivate var _isLandscape = false override var _prefersStatusBarHidden_: Bool? { return false } override var _supportedInterfaceOrientations_: UIInterfaceOrientationMask? { return _isLandscape ? .landscapeRight: .portrait } override var _preferredInterfaceOrientationForPresentation_: UIInterfaceOrientation? { return _isLandscape ? .landscapeRight: .portrait } override var isForbidInteractivePopGesture: Bool { return _isLandscape } // MARK: - 控制旋转 fileprivate func updateOrientationIfNeeded(_ force: Bool = false) { if _isLandscape { toLandscapeOrientation(force) } else { toPortraitOrientation(force) } } fileprivate func toLandscapeOrientation(_ force: Bool = false) { guard force || !_isLandscape else { return } UIRotateUtils.shared.rotateToLandscape() } fileprivate func toPortraitOrientation(_ force: Bool = false) { guard force || _isLandscape else { return } UIRotateUtils.shared.rotateToPortrait() } // 点击 “旋转” 按钮 @objc func onChangeOrientationBtnTapped(_ any: Any?) { DDLogDebug("旋转屏幕 \(_isLandscape)") // 核心控制 _isLandscape = !_isLandscape if _isLandscape { toLandscapeOrientation(true) } else { toPortraitOrientation(true) } } } extension BaseWebViewUIViewController: WKScriptMessageHandler { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let name = message.name if let handler = o2WKScriptHandlers[name] { handler.userController(userContentController, didReceive: message) }else { let err = "没有找到对应的key,key:\(name)" self.showError(title: err) } } } extension BaseWebViewUIViewController: BMKLocationManagerDelegate, BMKGeoCodeSearchDelegate { public func startLocation() { //开始定位 locService = BMKLocationManager() if locService == nil { locService?.desiredAccuracy = kCLLocationAccuracyBest //设置返回位置的坐标系类型 locService?.coordinateType = .BMK09LL //设置距离过滤参数 locService?.distanceFilter = kCLDistanceFilterNone; //设置预期精度参数 locService?.desiredAccuracy = kCLLocationAccuracyBest; //设置应用位置类型 locService?.activityType = .automotiveNavigation //设置是否自动停止位置更新 locService?.pausesLocationUpdatesAutomatically = false //定位返回geo地址信息 locService?.locatingWithReGeocode = true //后台定位 locService?.allowsBackgroundLocationUpdates = true } if searchAddress == nil { searchAddress = BMKGeoCodeSearch() } searchAddress?.delegate = self locService?.delegate = self locService?.startUpdatingLocation() } public func stopLocation() { locService?.stopUpdatingLocation() locService?.delegate = nil searchAddress?.delegate = nil } public func bmkLocationManager(_ manager: BMKLocationManager, didUpdate location: BMKLocation?, orError error: Error?) { if let loc = location?.location { DDLogDebug("当前位置,\(loc.coordinate.latitude),\(loc.coordinate.longitude)") //根据经纬度搜索到地址 let re = BMKReverseGeoCodeSearchOption() re.location = CLLocationCoordinate2D(latitude: loc.coordinate.latitude, longitude: loc.coordinate.longitude) let _ = searchAddress?.reverseGeoCode(re) } else { DDLogError("没有获取到定位信息!!!!!") } } public func bmkLocationManager(_ manager: BMKLocationManager, didFailWithError error: Error?) { DDLogError("定位错误:\(String(describing: error?.localizedDescription))") } public func onGetReverseGeoCodeResult(_ searcher: BMKGeoCodeSearch?, result: BMKReverseGeoCodeSearchResult?, errorCode error: BMKSearchErrorCode) { //发送定位的实时位置及名称信息 if let location = result?.location { var r = O2DeviceLocationResult() r.latitude = location.latitude r.longitude = location.longitude r.address = result?.address ?? "没有获取到地址!" locationCallBack(r.toJSONString()) }else { var r = O2DeviceLocationResult() r.address = "没有获取到地址!" locationCallBack(r.toJSONString()) DDLogError("搜索地址失败, \(error)") } //结束定位 self.stopLocation() } public func onGetGeoCodeResult(_ searcher: BMKGeoCodeSearch!, result: BMKGeoCodeSearchResult!, errorCode error: BMKSearchErrorCode) { if Int(error.rawValue) == 0 { DDLogDebug("result \(String(describing: result))") } else { DDLogDebug("result error errorCode = \(Int(error.rawValue))") } } }