1035 lines
44 KiB
Swift
1035 lines
44 KiB
Swift
|
/*
|
||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||
|
or more contributor license agreements. See the NOTICE file
|
||
|
distributed with this work for additional information
|
||
|
regarding copyright ownership. The ASF licenses this file
|
||
|
to you under the Apache License, Version 2.0 (the
|
||
|
"License"); you may not use this file except in compliance
|
||
|
with the License. You may obtain a copy of the License at
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
Unless required by applicable law or agreed to in writing,
|
||
|
software distributed under the License is distributed on an
|
||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
KIND, either express or implied. See the License for the
|
||
|
specific language governing permissions and limitations
|
||
|
under the License.
|
||
|
*/
|
||
|
|
||
|
import Flutter
|
||
|
import UIKit
|
||
|
import WebKit
|
||
|
import Foundation
|
||
|
import AVFoundation
|
||
|
|
||
|
let kInAppBrowserTargetSelf = "_self"
|
||
|
let kInAppBrowserTargetSystem = "_system"
|
||
|
let kInAppBrowserTargetBlank = "_blank"
|
||
|
let kInAppBrowserToolbarBarPositionBottom = "bottom"
|
||
|
let kInAppBrowserToolbarBarPositionTop = "top"
|
||
|
let TOOLBAR_HEIGHT: Float = 44.0
|
||
|
let STATUSBAR_HEIGHT: Float = 20.0
|
||
|
let LOCATIONBAR_HEIGHT: Float = 21.0
|
||
|
let FOOTER_HEIGHT: Float = (TOOLBAR_HEIGHT + LOCATIONBAR_HEIGHT)
|
||
|
|
||
|
@objcMembers
|
||
|
class CDVInAppBrowserOptions: NSObject {
|
||
|
var location = false
|
||
|
var toolbar = false
|
||
|
var closebuttoncaption = ""
|
||
|
var closebuttoncolor = ""
|
||
|
var toolbarposition = ""
|
||
|
var toolbarcolor = ""
|
||
|
var toolbartranslucent = false
|
||
|
var hidenavigationbuttons = false
|
||
|
var navigationbuttoncolor = ""
|
||
|
var clearcache = false
|
||
|
var clearsessioncache = false
|
||
|
var hidespinner = false
|
||
|
var presentationstyle = ""
|
||
|
var transitionstyle = ""
|
||
|
var enableviewportscale = false
|
||
|
var mediaplaybackrequiresuseraction = false
|
||
|
var allowinlinemediaplayback = false
|
||
|
var keyboarddisplayrequiresuseraction = false
|
||
|
var suppressesincrementalrendering = false
|
||
|
var hidden = false
|
||
|
var disallowoverscroll = false
|
||
|
|
||
|
override init() {
|
||
|
super.init()
|
||
|
|
||
|
// default values
|
||
|
location = true
|
||
|
toolbar = true
|
||
|
closebuttoncaption = "Done"
|
||
|
closebuttoncolor = "#FFFFFF"
|
||
|
toolbarposition = kInAppBrowserToolbarBarPositionBottom
|
||
|
clearcache = false
|
||
|
clearsessioncache = false
|
||
|
hidespinner = false
|
||
|
enableviewportscale = false
|
||
|
mediaplaybackrequiresuseraction = false
|
||
|
allowinlinemediaplayback = false
|
||
|
keyboarddisplayrequiresuseraction = true
|
||
|
suppressesincrementalrendering = false
|
||
|
hidden = false
|
||
|
disallowoverscroll = false
|
||
|
hidenavigationbuttons = false
|
||
|
toolbarcolor = ""
|
||
|
toolbartranslucent = true
|
||
|
|
||
|
}
|
||
|
|
||
|
class func parseOptions(_ options: String) -> CDVInAppBrowserOptions {
|
||
|
let obj = CDVInAppBrowserOptions()
|
||
|
// NOTE: this parsing does not handle quotes within values
|
||
|
let pairs = options.components(separatedBy: ",")
|
||
|
// parse keys and values, set the properties
|
||
|
for pair: String in pairs {
|
||
|
let keyvalue = pair.components(separatedBy: "=")
|
||
|
if keyvalue.count == 2 {
|
||
|
let key: String = keyvalue[0].lowercased()
|
||
|
let value: String = keyvalue[1]
|
||
|
let value_lc: String = value.lowercased()
|
||
|
let isBoolean: Bool = (value_lc == "yes") || (value_lc == "no")
|
||
|
let numberFormatter = NumberFormatter()
|
||
|
numberFormatter.allowsFloats = true
|
||
|
let isNumber: Bool = numberFormatter.number(from: value_lc) != nil
|
||
|
// set the property according to the key name
|
||
|
if obj.responds(to: NSSelectorFromString(key)) {
|
||
|
if isNumber {
|
||
|
obj.setValue(numberFormatter.number(from: value_lc), forKey: key)
|
||
|
}
|
||
|
else if isBoolean {
|
||
|
obj.setValue(((value_lc == "yes") ? 1 : 0), forKey: key)
|
||
|
}
|
||
|
else {
|
||
|
obj.setValue(value, forKey: key)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return obj
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
||
|
var inAppBrowserViewController: CDVInAppBrowserViewController?
|
||
|
|
||
|
var tmpWindow: UIWindow?
|
||
|
var channel: FlutterMethodChannel
|
||
|
private var previousStatusBarStyle = -1
|
||
|
|
||
|
public init(with registrar: FlutterPluginRegistrar) {
|
||
|
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser", binaryMessenger: registrar.messenger())
|
||
|
}
|
||
|
|
||
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||
|
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappbrowser", binaryMessenger: registrar.messenger())
|
||
|
let instance = SwiftFlutterPlugin(with: registrar)
|
||
|
registrar.addMethodCallDelegate(instance, channel: channel)
|
||
|
}
|
||
|
|
||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||
|
let arguments = call.arguments as? NSDictionary
|
||
|
switch call.method {
|
||
|
case "open":
|
||
|
self.open(arguments: arguments!, result: result)
|
||
|
break
|
||
|
case "close":
|
||
|
self.close()
|
||
|
result(true)
|
||
|
break
|
||
|
case "show":
|
||
|
self.show()
|
||
|
result(true)
|
||
|
break
|
||
|
case "hide":
|
||
|
self.hide()
|
||
|
result(true)
|
||
|
break
|
||
|
case "injectScriptCode":
|
||
|
self.injectScriptCode(arguments: arguments!)
|
||
|
result(true)
|
||
|
break
|
||
|
case "injectScriptFile":
|
||
|
self.injectScriptFile(arguments: arguments!)
|
||
|
result(true)
|
||
|
break
|
||
|
case "injectStyleCode":
|
||
|
self.injectStyleCode(arguments: arguments!)
|
||
|
result(true)
|
||
|
break
|
||
|
case "injectStyleFile":
|
||
|
self.injectStyleFile(arguments: arguments!)
|
||
|
result(true)
|
||
|
break
|
||
|
default:
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func setting(forKey key: String) -> Any {
|
||
|
return ""//commandDelegate.settings[key.lowercased()]!
|
||
|
}
|
||
|
|
||
|
func onReset() {
|
||
|
close()
|
||
|
}
|
||
|
|
||
|
func close() {
|
||
|
if inAppBrowserViewController == nil {
|
||
|
print("IAB.close() called but it was already closed.")
|
||
|
return
|
||
|
}
|
||
|
// Things are cleaned up in browserExit.
|
||
|
inAppBrowserViewController?.close()
|
||
|
}
|
||
|
|
||
|
func isSystemUrl(_ url: URL) -> Bool {
|
||
|
if (url.host == "itunes.apple.com") {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
public func open(arguments: NSDictionary, result: @escaping FlutterResult) {
|
||
|
let url: String? = (arguments["url"] as? String)!
|
||
|
var target: String? = (arguments["target"] as? String)!
|
||
|
target = target != nil ? target : kInAppBrowserTargetSelf
|
||
|
let options = (arguments["options"] as? String)!
|
||
|
|
||
|
if url != nil {
|
||
|
let baseUrl: URL? = nil
|
||
|
let absoluteUrl = URL(string: url!, relativeTo: baseUrl)?.absoluteURL
|
||
|
|
||
|
if isSystemUrl(absoluteUrl!) {
|
||
|
target = kInAppBrowserTargetSystem
|
||
|
}
|
||
|
|
||
|
if (target == kInAppBrowserTargetSelf) {
|
||
|
open(inCordovaWebView: absoluteUrl!, withOptions: options)
|
||
|
}
|
||
|
else if (target == kInAppBrowserTargetSystem) {
|
||
|
open(inSystem: absoluteUrl!)
|
||
|
}
|
||
|
else {
|
||
|
// _blank or anything else
|
||
|
openIn(inAppBrowser: absoluteUrl!, withOptions: options)
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
result(false)
|
||
|
}
|
||
|
result(true)
|
||
|
}
|
||
|
|
||
|
func openIn(inAppBrowser url: URL, withOptions options: String) {
|
||
|
let browserOptions = CDVInAppBrowserOptions.parseOptions(options)
|
||
|
|
||
|
if browserOptions.clearcache {
|
||
|
let _: HTTPCookie?
|
||
|
let storage = HTTPCookieStorage.shared
|
||
|
for cookie in storage.cookies! {
|
||
|
if !(cookie.domain.isEqual(".^filecookies^") ) {
|
||
|
storage.deleteCookie(cookie)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if browserOptions.clearsessioncache {
|
||
|
let _: HTTPCookie?
|
||
|
let storage = HTTPCookieStorage.shared
|
||
|
for cookie in storage.cookies! {
|
||
|
if !(cookie.domain.isEqual(".^filecookies^") && cookie.isSessionOnly) {
|
||
|
storage.deleteCookie(cookie)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if inAppBrowserViewController == nil {
|
||
|
var userAgent: String = UIWebView().stringByEvaluatingJavaScript(from: "navigator.userAgent")!
|
||
|
|
||
|
let overrideUserAgent: String = ""
|
||
|
let appendUserAgent: String = ""
|
||
|
if overrideUserAgent != "" {
|
||
|
userAgent = overrideUserAgent
|
||
|
}
|
||
|
if appendUserAgent != "" {
|
||
|
userAgent = userAgent + (appendUserAgent)
|
||
|
}
|
||
|
if !(self.tmpWindow != nil) {
|
||
|
let frame: CGRect = UIScreen.main.bounds
|
||
|
self.tmpWindow = UIWindow(frame: frame)
|
||
|
}
|
||
|
inAppBrowserViewController = CDVInAppBrowserViewController(userAgent: userAgent, prevUserAgent: userAgent, browserOptions: browserOptions, tmpWindow: tmpWindow)
|
||
|
inAppBrowserViewController?.navigationDelegate = self
|
||
|
}
|
||
|
|
||
|
inAppBrowserViewController?.showLocationBar(browserOptions.location)
|
||
|
inAppBrowserViewController?.showToolBar(browserOptions.toolbar, toolbarPosition: browserOptions.toolbarposition)
|
||
|
if browserOptions.closebuttoncaption != nil || browserOptions.closebuttoncolor != nil {
|
||
|
inAppBrowserViewController?.setCloseButtonTitle(browserOptions.closebuttoncaption, colorString: browserOptions.closebuttoncolor)
|
||
|
}
|
||
|
|
||
|
// Set Presentation Style
|
||
|
var presentationStyle: UIModalPresentationStyle = .fullScreen
|
||
|
// default
|
||
|
if browserOptions.presentationstyle != nil {
|
||
|
if (browserOptions.presentationstyle.lowercased() == "pagesheet") {
|
||
|
presentationStyle = .pageSheet
|
||
|
}
|
||
|
else if (browserOptions.presentationstyle.lowercased() == "formsheet") {
|
||
|
presentationStyle = .formSheet
|
||
|
}
|
||
|
}
|
||
|
inAppBrowserViewController?.modalPresentationStyle = presentationStyle
|
||
|
|
||
|
// Set Transition Style
|
||
|
var transitionStyle: UIModalTransitionStyle = .coverVertical
|
||
|
// default
|
||
|
if browserOptions.transitionstyle != nil {
|
||
|
if (browserOptions.transitionstyle.lowercased() == "fliphorizontal") {
|
||
|
transitionStyle = .flipHorizontal
|
||
|
}
|
||
|
else if (browserOptions.transitionstyle.lowercased() == "crossdissolve") {
|
||
|
transitionStyle = .crossDissolve
|
||
|
}
|
||
|
}
|
||
|
inAppBrowserViewController?.modalTransitionStyle = transitionStyle
|
||
|
|
||
|
// prevent webView from bouncing
|
||
|
if browserOptions.disallowoverscroll {
|
||
|
if inAppBrowserViewController?.webView?.responds(to: #selector(getter: inAppBrowserViewController?.webView?.scrollView)) ?? false {
|
||
|
(inAppBrowserViewController?.webView?.scrollView)?.bounces = false
|
||
|
}
|
||
|
else {
|
||
|
for subview: UIView in (inAppBrowserViewController?.webView?.subviews)! {
|
||
|
if subview is UIScrollView {
|
||
|
(subview as? UIScrollView)?.bounces = false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// UIWebView options
|
||
|
inAppBrowserViewController?.webView?.scalesPageToFit = browserOptions.enableviewportscale
|
||
|
inAppBrowserViewController?.webView?.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction
|
||
|
inAppBrowserViewController?.webView?.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback
|
||
|
//if IsAtLeastiOSVersion("6.0") {
|
||
|
inAppBrowserViewController?.webView?.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction
|
||
|
inAppBrowserViewController?.webView?.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering
|
||
|
//}
|
||
|
|
||
|
inAppBrowserViewController?.navigate(to: url)
|
||
|
if !browserOptions.hidden {
|
||
|
show()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public func show() {
|
||
|
if inAppBrowserViewController == nil {
|
||
|
print("Tried to show IAB after it was closed.")
|
||
|
return
|
||
|
}
|
||
|
if previousStatusBarStyle != -1 {
|
||
|
print("Tried to show IAB while already shown")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
previousStatusBarStyle = UIApplication.shared.statusBarStyle.rawValue
|
||
|
let nav = CDVInAppBrowserNavigationController(rootViewController: inAppBrowserViewController!)
|
||
|
nav.orientationDelegate = inAppBrowserViewController
|
||
|
nav.isNavigationBarHidden = true
|
||
|
nav.modalPresentationStyle = (inAppBrowserViewController?.modalPresentationStyle)!
|
||
|
|
||
|
weak var weakSelf: SwiftFlutterPlugin? = self
|
||
|
|
||
|
// Run later to avoid the "took a long time" log message.
|
||
|
DispatchQueue.main.async(execute: {() -> Void in
|
||
|
if weakSelf?.inAppBrowserViewController != nil {
|
||
|
if !(self.tmpWindow != nil) {
|
||
|
let frame: CGRect = UIScreen.main.bounds
|
||
|
self.tmpWindow = UIWindow(frame: frame)
|
||
|
}
|
||
|
let tmpController = UIViewController()
|
||
|
let baseWindowLevel = UIApplication.shared.keyWindow?.windowLevel
|
||
|
self.tmpWindow?.rootViewController = tmpController
|
||
|
self.tmpWindow?.windowLevel = UIWindowLevel(baseWindowLevel! + 1)
|
||
|
self.tmpWindow?.makeKeyAndVisible()
|
||
|
tmpController.present(nav, animated: true) { () -> Void in }
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
public func hide() {
|
||
|
if inAppBrowserViewController == nil {
|
||
|
print("Tried to hide IAB after it was closed.")
|
||
|
return
|
||
|
}
|
||
|
if previousStatusBarStyle == -1 {
|
||
|
print("Tried to hide IAB while already hidden")
|
||
|
return
|
||
|
}
|
||
|
previousStatusBarStyle = UIApplication.shared.statusBarStyle.rawValue
|
||
|
// Run later to avoid the "took a long time" log message.
|
||
|
DispatchQueue.main.async(execute: {() -> Void in
|
||
|
if self.inAppBrowserViewController != nil {
|
||
|
self.previousStatusBarStyle = -1
|
||
|
self.inAppBrowserViewController?.presentingViewController?.dismiss(animated: true, completion: {() -> Void in
|
||
|
self.tmpWindow?.windowLevel = 0.0
|
||
|
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func open(inCordovaWebView url: URL, withOptions options: String) {
|
||
|
_ = URLRequest(url: url)
|
||
|
openIn(inAppBrowser: url, withOptions: options)
|
||
|
}
|
||
|
|
||
|
func open(inSystem url: URL) {
|
||
|
if UIApplication.shared.openURL(url) == false {
|
||
|
NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "CDVPluginHandleOpenURLNotification"), object: url))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
|
||
|
// provides a consistent method for injecting JavaScript code into the document.
|
||
|
//
|
||
|
// If a wrapper string is supplied, then the source string will be JSON-encoded (adding
|
||
|
// quotes) and wrapped using string formatting. (The wrapper string should have a single
|
||
|
// '%@' marker).
|
||
|
//
|
||
|
// If no wrapper is supplied, then the source string is executed directly.
|
||
|
func injectDeferredObject(_ source: String, withWrapper jsWrapper: String) {
|
||
|
if jsWrapper != nil {
|
||
|
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: [source], options: [])
|
||
|
let sourceArrayString = String(data: jsonData!, encoding: String.Encoding.utf8)
|
||
|
if sourceArrayString != nil {
|
||
|
let sourceString: String? = (sourceArrayString! as NSString).substring(with: NSRange(location: 1, length: (sourceArrayString?.characters.count ?? 0) - 2))
|
||
|
let jsToInject = String(format: jsWrapper, sourceString!)
|
||
|
inAppBrowserViewController?.webView?.stringByEvaluatingJavaScript(from: jsToInject)
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
inAppBrowserViewController?.webView?.stringByEvaluatingJavaScript(from: source)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public func injectScriptCode(arguments: NSDictionary) {
|
||
|
let jsWrapper = "(function(){JSON.stringify([eval(%@)])})()"
|
||
|
injectDeferredObject(arguments["source"] as! String, withWrapper: jsWrapper)
|
||
|
}
|
||
|
|
||
|
public func injectScriptFile(arguments: NSDictionary) {
|
||
|
let jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)"
|
||
|
injectDeferredObject(arguments["urlFile"] as! String, withWrapper: jsWrapper)
|
||
|
}
|
||
|
|
||
|
public func injectStyleCode(arguments: NSDictionary) {
|
||
|
let jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)"
|
||
|
injectDeferredObject(arguments["source"] as! String, withWrapper: jsWrapper)
|
||
|
}
|
||
|
|
||
|
public func injectStyleFile(arguments: NSDictionary) {
|
||
|
let jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)"
|
||
|
injectDeferredObject(arguments["urlFile"] as! String, withWrapper: jsWrapper)
|
||
|
}
|
||
|
|
||
|
func webViewDidStartLoad(_ webView: UIWebView) {
|
||
|
let url: String = inAppBrowserViewController!.currentURL!.absoluteString
|
||
|
channel.invokeMethod("loadstart", arguments: ["type": "loadstart", "url": url])
|
||
|
}
|
||
|
|
||
|
func webViewDidFinishLoad(_ webView: UIWebView) {
|
||
|
let url: String = inAppBrowserViewController!.currentURL!.absoluteString
|
||
|
channel.invokeMethod("loadstop", arguments: ["type": "loadstop", "url": url])
|
||
|
}
|
||
|
|
||
|
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
|
||
|
let url: String = inAppBrowserViewController!.currentURL!.absoluteString
|
||
|
let arguments = ["type": "loaderror", "url": url, "code": error._code, "message": error.localizedDescription] as [String : Any]
|
||
|
channel.invokeMethod("loaderror", arguments: arguments)
|
||
|
}
|
||
|
|
||
|
func browserExit() {
|
||
|
|
||
|
channel.invokeMethod("exit", arguments: ["type": "exit"])
|
||
|
|
||
|
// Set navigationDelegate to nil to ensure no callbacks are received from it.
|
||
|
inAppBrowserViewController?.navigationDelegate = nil
|
||
|
// Don't recycle the ViewController since it may be consuming a lot of memory.
|
||
|
// Also - this is required for the PDF/User-Agent bug work-around.
|
||
|
inAppBrowserViewController = nil
|
||
|
|
||
|
//if (IsAtLeastiOSVersion(@"7.0")) {
|
||
|
if previousStatusBarStyle != -1 {
|
||
|
UIApplication.shared.statusBarStyle = UIStatusBarStyle(rawValue: previousStatusBarStyle)!
|
||
|
}
|
||
|
//}
|
||
|
|
||
|
previousStatusBarStyle = -1
|
||
|
// this value was reset before reapplying it. caused statusbar to stay black on ios7
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class CDVInAppBrowserViewController: UIViewController, UIWebViewDelegate {
|
||
|
@IBOutlet var webView: UIWebView!
|
||
|
@IBOutlet var closeButton: UIBarButtonItem!
|
||
|
@IBOutlet var addressLabel: UILabel!
|
||
|
@IBOutlet var backButton: UIBarButtonItem!
|
||
|
@IBOutlet var forwardButton: UIBarButtonItem!
|
||
|
@IBOutlet var spinner: UIActivityIndicatorView!
|
||
|
@IBOutlet var toolbar: UIToolbar!
|
||
|
weak var orientationDelegate: CDVInAppBrowserViewController?
|
||
|
weak var navigationDelegate: SwiftFlutterPlugin?
|
||
|
var currentURL: URL?
|
||
|
var tmpWindow: UIWindow?
|
||
|
|
||
|
private var userAgent = ""
|
||
|
private var prevUserAgent = ""
|
||
|
private var userAgentLockToken: Int = 0
|
||
|
private var browserOptions: CDVInAppBrowserOptions?
|
||
|
|
||
|
init(userAgent: String, prevUserAgent: String, browserOptions: CDVInAppBrowserOptions, tmpWindow: UIWindow?) {
|
||
|
|
||
|
super.init(nibName: nil, bundle: nil)
|
||
|
|
||
|
self.userAgent = userAgent
|
||
|
self.prevUserAgent = prevUserAgent
|
||
|
self.browserOptions = browserOptions
|
||
|
self.tmpWindow = tmpWindow
|
||
|
createViews()
|
||
|
|
||
|
}
|
||
|
|
||
|
required init?(coder aDecoder: NSCoder) {
|
||
|
fatalError("init(coder:) has not been implemented")
|
||
|
}
|
||
|
|
||
|
// Prevent crashes on closing windows
|
||
|
deinit {
|
||
|
webView?.delegate = nil
|
||
|
}
|
||
|
|
||
|
func createViews() {
|
||
|
// We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
let toolbarIsAtBottom: Bool = !(browserOptions!.toolbarposition == kInAppBrowserToolbarBarPositionTop)
|
||
|
webViewBounds.size.height -= CGFloat((browserOptions?.location)! ? FOOTER_HEIGHT : TOOLBAR_HEIGHT)
|
||
|
webView = UIWebView(frame: webViewBounds)// as WKWebView
|
||
|
webView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||
|
|
||
|
view.addSubview(webView!)
|
||
|
view.sendSubview(toBack: webView!)
|
||
|
webView?.delegate = self
|
||
|
webView?.backgroundColor = UIColor.white
|
||
|
webView?.clearsContextBeforeDrawing = true
|
||
|
webView?.clipsToBounds = true
|
||
|
webView?.contentMode = .scaleToFill
|
||
|
webView?.isMultipleTouchEnabled = true
|
||
|
webView?.isOpaque = true
|
||
|
webView?.scalesPageToFit = false
|
||
|
webView?.isUserInteractionEnabled = true
|
||
|
|
||
|
spinner = UIActivityIndicatorView(activityIndicatorStyle: .gray)
|
||
|
spinner.alpha = 1.000
|
||
|
spinner.autoresizesSubviews = true
|
||
|
spinner.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin, .flexibleBottomMargin, .flexibleRightMargin]
|
||
|
spinner.clearsContextBeforeDrawing = false
|
||
|
spinner.clipsToBounds = false
|
||
|
spinner.contentMode = .scaleToFill
|
||
|
spinner.frame = CGRect(x: (webView?.frame.midX)!, y: (webView?.frame.midY)!, width: 20.0, height: 20.0)
|
||
|
spinner.isHidden = false
|
||
|
spinner.hidesWhenStopped = true
|
||
|
spinner.isMultipleTouchEnabled = false
|
||
|
spinner.isOpaque = false
|
||
|
spinner.isUserInteractionEnabled = false
|
||
|
spinner.stopAnimating()
|
||
|
|
||
|
closeButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.close))
|
||
|
closeButton.isEnabled = true
|
||
|
let flexibleSpaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||
|
let fixedSpaceButton = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
|
||
|
fixedSpaceButton.width = 20
|
||
|
let toolbarY: Float = toolbarIsAtBottom ? Float(view.bounds.size.height) - TOOLBAR_HEIGHT : 0.0
|
||
|
let toolbarFrame = CGRect(x: 0.0, y: CGFloat(toolbarY), width: view.bounds.size.width, height: CGFloat(TOOLBAR_HEIGHT))
|
||
|
|
||
|
toolbar = UIToolbar(frame: toolbarFrame)
|
||
|
toolbar.alpha = 1.000
|
||
|
toolbar.autoresizesSubviews = true
|
||
|
toolbar.autoresizingMask = toolbarIsAtBottom ? ([.flexibleWidth, .flexibleTopMargin]) : .flexibleWidth
|
||
|
toolbar.barStyle = .blackOpaque
|
||
|
toolbar.clearsContextBeforeDrawing = false
|
||
|
toolbar.clipsToBounds = false
|
||
|
toolbar.contentMode = .scaleToFill
|
||
|
toolbar.isHidden = false
|
||
|
toolbar.isMultipleTouchEnabled = false
|
||
|
toolbar.isOpaque = false
|
||
|
toolbar.isUserInteractionEnabled = true
|
||
|
if browserOptions?.toolbarcolor != nil {
|
||
|
// Set toolbar color if user sets it in options
|
||
|
toolbar.barTintColor = color(fromHexString: (browserOptions?.toolbarcolor)!)
|
||
|
}
|
||
|
if !(browserOptions?.toolbartranslucent)! {
|
||
|
// Set toolbar translucent to no if user sets it in options
|
||
|
toolbar.isTranslucent = false
|
||
|
}
|
||
|
let labelInset: CGFloat = 5.0
|
||
|
let locationBarY: Float = toolbarIsAtBottom ? Float(view.bounds.size.height) - FOOTER_HEIGHT : Float(view.bounds.size.height) - LOCATIONBAR_HEIGHT
|
||
|
|
||
|
addressLabel = UILabel(frame: CGRect(x: labelInset, y: CGFloat(locationBarY), width: view.bounds.size.width - labelInset, height: CGFloat(LOCATIONBAR_HEIGHT)))
|
||
|
addressLabel.adjustsFontSizeToFitWidth = false
|
||
|
addressLabel.alpha = 1.000
|
||
|
addressLabel.autoresizesSubviews = true
|
||
|
addressLabel.autoresizingMask = [.flexibleWidth, .flexibleRightMargin, .flexibleTopMargin]
|
||
|
addressLabel.backgroundColor = UIColor.clear// as? CGColor
|
||
|
addressLabel.baselineAdjustment = .alignCenters
|
||
|
addressLabel.clearsContextBeforeDrawing = true
|
||
|
addressLabel.clipsToBounds = true
|
||
|
addressLabel.contentMode = .scaleToFill
|
||
|
addressLabel.isEnabled = true
|
||
|
addressLabel.isHidden = false
|
||
|
addressLabel.lineBreakMode = .byTruncatingTail
|
||
|
|
||
|
if addressLabel.responds(to: NSSelectorFromString("setMinimumScaleFactor:")) {
|
||
|
addressLabel.setValue((10.0 / UIFont.labelFontSize), forKey: "minimumScaleFactor")
|
||
|
}
|
||
|
else if addressLabel.responds(to: NSSelectorFromString("setMinimumFontSize:")) {
|
||
|
addressLabel.setValue(10.0, forKey: "minimumFontSize")
|
||
|
}
|
||
|
|
||
|
addressLabel.isMultipleTouchEnabled = false
|
||
|
addressLabel.numberOfLines = 1
|
||
|
addressLabel.isOpaque = false
|
||
|
addressLabel.shadowOffset = CGSize(width: 0.0, height: -1.0)
|
||
|
addressLabel.text = NSLocalizedString("Loading...", comment: "")
|
||
|
addressLabel.textAlignment = .left
|
||
|
addressLabel.textColor = UIColor(white: 1.000, alpha: 1.000)
|
||
|
addressLabel.isUserInteractionEnabled = false
|
||
|
|
||
|
let frontArrowString = NSLocalizedString("►", comment: "")
|
||
|
// create arrow from Unicode char
|
||
|
forwardButton = UIBarButtonItem(title: frontArrowString, style: .plain, target: self, action: #selector(self.goForward))
|
||
|
forwardButton.isEnabled = true
|
||
|
forwardButton.imageInsets = UIEdgeInsets.zero as? UIEdgeInsets ?? UIEdgeInsets()
|
||
|
if browserOptions?.navigationbuttoncolor != nil {
|
||
|
// Set button color if user sets it in options
|
||
|
forwardButton.tintColor = color(fromHexString: (browserOptions?.navigationbuttoncolor)!)
|
||
|
}
|
||
|
|
||
|
let backArrowString = NSLocalizedString("◄", comment: "")
|
||
|
// create arrow from Unicode char
|
||
|
backButton = UIBarButtonItem(title: backArrowString, style: .plain, target: self, action: #selector(self.goBack))
|
||
|
backButton.isEnabled = true
|
||
|
backButton.imageInsets = UIEdgeInsets.zero as? UIEdgeInsets ?? UIEdgeInsets()
|
||
|
if browserOptions?.navigationbuttoncolor != nil {
|
||
|
// Set button color if user sets it in options
|
||
|
backButton.tintColor = color(fromHexString: (browserOptions?.navigationbuttoncolor)!)
|
||
|
}
|
||
|
|
||
|
// Filter out Navigation Buttons if user requests so
|
||
|
if (browserOptions?.hidenavigationbuttons)! {
|
||
|
toolbar.items = [closeButton, flexibleSpaceButton]
|
||
|
}
|
||
|
else {
|
||
|
toolbar.items = [closeButton, flexibleSpaceButton, backButton, fixedSpaceButton, forwardButton]
|
||
|
}
|
||
|
view.backgroundColor = UIColor.gray// as? CGColor
|
||
|
view.addSubview(toolbar)
|
||
|
view.addSubview(addressLabel)
|
||
|
view.addSubview(spinner)
|
||
|
}
|
||
|
|
||
|
func setWebViewFrame(_ frame: CGRect) {
|
||
|
print("Setting the WebView's frame to \(NSStringFromCGRect(frame))")
|
||
|
webView?.frame = frame
|
||
|
}
|
||
|
|
||
|
func setCloseButtonTitle(_ title: String, colorString: String) {
|
||
|
// the advantage of using UIBarButtonSystemItemDone is the system will localize it for you automatically
|
||
|
// but, if you want to set this yourself, knock yourself out (we can't set the title for a system Done button, so we have to create a new one)
|
||
|
closeButton = nil
|
||
|
// Initialize with title if title is set, otherwise the title will be 'Done' localized
|
||
|
closeButton = title != nil ? UIBarButtonItem(title: title, style: .bordered, target: self, action: #selector(self.close)) : UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.close))
|
||
|
closeButton.isEnabled = true
|
||
|
// If color on closebutton is requested then initialize with that that color, otherwise use initialize with default
|
||
|
closeButton.tintColor = colorString != nil ? color(fromHexString: colorString) : UIColor(red: 60.0 / 255.0, green: 136.0 / 255.0, blue: 230.0 / 255.0, alpha: 1)
|
||
|
|
||
|
var items = toolbar.items
|
||
|
items![0] = closeButton
|
||
|
toolbar.items = items //as? [AVMetadataItem] ?? [AVMetadataItem]()
|
||
|
}
|
||
|
|
||
|
func showLocationBar(_ show: Bool) {
|
||
|
var locationbarFrame: CGRect = addressLabel.frame
|
||
|
let toolbarVisible: Bool = !toolbar.isHidden
|
||
|
// prevent double show/hide
|
||
|
if show == !addressLabel.isHidden {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if show {
|
||
|
addressLabel.isHidden = false
|
||
|
if toolbarVisible {
|
||
|
// toolBar at the bottom, leave as is
|
||
|
// put locationBar on top of the toolBar
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
webViewBounds.size.height -= CGFloat(FOOTER_HEIGHT)
|
||
|
setWebViewFrame(webViewBounds)
|
||
|
locationbarFrame.origin.y = webViewBounds.size.height
|
||
|
addressLabel.frame = locationbarFrame
|
||
|
}
|
||
|
else {
|
||
|
// no toolBar, so put locationBar at the bottom
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
webViewBounds.size.height -= CGFloat(LOCATIONBAR_HEIGHT)
|
||
|
setWebViewFrame(webViewBounds)
|
||
|
locationbarFrame.origin.y = webViewBounds.size.height
|
||
|
addressLabel.frame = locationbarFrame
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
addressLabel.isHidden = true
|
||
|
if toolbarVisible {
|
||
|
// locationBar is on top of toolBar, hide locationBar
|
||
|
// webView take up whole height less toolBar height
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
webViewBounds.size.height -= CGFloat(TOOLBAR_HEIGHT)
|
||
|
setWebViewFrame(webViewBounds)
|
||
|
}
|
||
|
else {
|
||
|
// no toolBar, expand webView to screen dimensions
|
||
|
setWebViewFrame(view.bounds)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func showToolBar(_ show: Bool, toolbarPosition: String) {
|
||
|
var toolbarFrame: CGRect = toolbar.frame
|
||
|
var locationbarFrame: CGRect = addressLabel.frame
|
||
|
let locationbarVisible: Bool = !addressLabel.isHidden
|
||
|
// prevent double show/hide
|
||
|
if show == !toolbar.isHidden {
|
||
|
return
|
||
|
}
|
||
|
if show {
|
||
|
toolbar.isHidden = false
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
|
||
|
if locationbarVisible {
|
||
|
// locationBar at the bottom, move locationBar up
|
||
|
// put toolBar at the bottom
|
||
|
webViewBounds.size.height -= CGFloat(FOOTER_HEIGHT)
|
||
|
locationbarFrame.origin.y = webViewBounds.size.height
|
||
|
addressLabel.frame = locationbarFrame
|
||
|
toolbar.frame = toolbarFrame
|
||
|
}
|
||
|
else {
|
||
|
// no locationBar, so put toolBar at the bottom
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
webViewBounds.size.height -= CGFloat(TOOLBAR_HEIGHT)
|
||
|
toolbar.frame = toolbarFrame
|
||
|
}
|
||
|
|
||
|
if (toolbarPosition == kInAppBrowserToolbarBarPositionTop) {
|
||
|
toolbarFrame.origin.y = 0
|
||
|
webViewBounds.origin.y += toolbarFrame.size.height
|
||
|
setWebViewFrame(webViewBounds)
|
||
|
}
|
||
|
else {
|
||
|
toolbarFrame.origin.y = webViewBounds.size.height + CGFloat(LOCATIONBAR_HEIGHT)
|
||
|
}
|
||
|
setWebViewFrame(webViewBounds)
|
||
|
}
|
||
|
else {
|
||
|
toolbar.isHidden = true
|
||
|
if locationbarVisible {
|
||
|
// locationBar is on top of toolBar, hide toolBar
|
||
|
// put locationBar at the bottom
|
||
|
// webView take up whole height less locationBar height
|
||
|
var webViewBounds: CGRect = view.bounds
|
||
|
webViewBounds.size.height -= CGFloat(LOCATIONBAR_HEIGHT)
|
||
|
setWebViewFrame(webViewBounds)
|
||
|
// move locationBar down
|
||
|
locationbarFrame.origin.y = webViewBounds.size.height
|
||
|
addressLabel.frame = locationbarFrame
|
||
|
}
|
||
|
else {
|
||
|
// no locationBar, expand webView to screen dimensions
|
||
|
setWebViewFrame(view.bounds)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override func viewDidLoad() {
|
||
|
super.viewDidLoad()
|
||
|
}
|
||
|
|
||
|
// override func viewDidUnload() {
|
||
|
// webView?.loadHTMLString(nil, baseURL: nil)
|
||
|
// CDVUserAgentUtil.releaseLock(userAgentLockToken)
|
||
|
// super.viewDidUnload()
|
||
|
// }
|
||
|
|
||
|
// func preferredStatusBarStyle() -> UIStatusBarStyle {
|
||
|
// return .default
|
||
|
// }
|
||
|
//
|
||
|
// func prefersStatusBarHidden() -> Bool {
|
||
|
// return false
|
||
|
// }
|
||
|
|
||
|
@objc func close() {
|
||
|
currentURL = nil
|
||
|
if (navigationDelegate != nil) {
|
||
|
navigationDelegate?.browserExit()
|
||
|
}
|
||
|
// if (navigationDelegate != nil) && navigationDelegate?.responds(to: #selector(self.browserExit)) {
|
||
|
// navigationDelegate?.browserExit()
|
||
|
// }
|
||
|
weak var weakSelf = self// as? UIViewController
|
||
|
// Run later to avoid the "took a long time" log message.
|
||
|
|
||
|
DispatchQueue.main.async(execute: {() -> Void in
|
||
|
if (weakSelf?.responds(to: #selector(getter: self.presentingViewController)))! {
|
||
|
weakSelf?.presentingViewController?.dismiss(animated: true, completion: {() -> Void in
|
||
|
self.tmpWindow?.windowLevel = 0.0
|
||
|
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
|
||
|
})
|
||
|
}
|
||
|
else {
|
||
|
weakSelf?.parent?.dismiss(animated: true, completion: {() -> Void in
|
||
|
self.tmpWindow?.windowLevel = 0.0
|
||
|
UIApplication.shared.delegate?.window??.makeKeyAndVisible()
|
||
|
})
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func navigate(to url: URL) {
|
||
|
let request = URLRequest(url: url)
|
||
|
currentURL = url
|
||
|
webView?.loadRequest(request)
|
||
|
// if userAgentLockToken != 0 {
|
||
|
// webView?.load(request, mimeType: <#String#>, textEncodingName: <#String#>, baseURL: <#URL#>)
|
||
|
// }
|
||
|
// else {
|
||
|
// weak var weakSelf: CDVInAppBrowserViewController? = self
|
||
|
// CDVUserAgentUtil.acquireLock({(_ lockToken: Int) -> Void in
|
||
|
// userAgentLockToken = lockToken
|
||
|
// CDVUserAgentUtil.setUserAgent(userAgent, lockToken: lockToken)
|
||
|
// weakSelf?.webView?.load(request)
|
||
|
// })
|
||
|
// }
|
||
|
}
|
||
|
|
||
|
@objc func goBack(_ sender: Any) {
|
||
|
webView?.goBack()
|
||
|
}
|
||
|
|
||
|
@objc func goForward(_ sender: Any) {
|
||
|
webView?.goForward()
|
||
|
}
|
||
|
|
||
|
override func viewWillAppear(_ animated: Bool) {
|
||
|
//if IsAtLeastiOSVersion("7.0") {
|
||
|
UIApplication.shared.statusBarStyle = preferredStatusBarStyle
|
||
|
//}
|
||
|
rePositionViews()
|
||
|
super.viewWillAppear(animated)
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// On iOS 7 the status bar is part of the view's dimensions, therefore it's height has to be taken into account.
|
||
|
// The height of it could be hardcoded as 20 pixels, but that would assume that the upcoming releases of iOS won't
|
||
|
// change that value.
|
||
|
//
|
||
|
|
||
|
func getStatusBarOffset() -> Float {
|
||
|
let statusBarFrame: CGRect = UIApplication.shared.statusBarFrame
|
||
|
let statusBarOffset: Float = Float(min(statusBarFrame.size.width, statusBarFrame.size.height))//IsAtLeastiOSVersion("7.0") ? min(statusBarFrame.size.width, statusBarFrame.size.height) : 0.0
|
||
|
return statusBarOffset
|
||
|
}
|
||
|
|
||
|
func rePositionViews() {
|
||
|
if (browserOptions?.toolbarposition == kInAppBrowserToolbarBarPositionTop) {
|
||
|
webView?.frame = CGRect(x: (webView?.frame.origin.x)!, y: CGFloat(TOOLBAR_HEIGHT), width: (webView?.frame.size.width)!, height: (webView?.frame.size.height)!)
|
||
|
toolbar.frame = CGRect(x: toolbar.frame.origin.x, y: CGFloat(getStatusBarOffset()), width: toolbar.frame.size.width, height: toolbar.frame.size.height)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Helper function to convert hex color string to UIColor
|
||
|
// Assumes input like "#00FF00" (#RRGGBB).
|
||
|
// Taken from https://stackoverflow.com/questions/1560081/how-can-i-create-a-uicolor-from-a-hex-string
|
||
|
|
||
|
func color(fromHexString: String, alpha:CGFloat? = 1.0) -> UIColor {
|
||
|
|
||
|
// Convert hex string to an integer
|
||
|
let hexint = Int(self.intFromHexString(hexStr: fromHexString))
|
||
|
let red = CGFloat((hexint & 0xff0000) >> 16) / 255.0
|
||
|
let green = CGFloat((hexint & 0xff00) >> 8) / 255.0
|
||
|
let blue = CGFloat((hexint & 0xff) >> 0) / 255.0
|
||
|
let alpha = alpha!
|
||
|
|
||
|
// Create color object, specifying alpha as well
|
||
|
let color = UIColor(red: red, green: green, blue: blue, alpha: alpha)
|
||
|
return color
|
||
|
}
|
||
|
|
||
|
func intFromHexString(hexStr: String) -> UInt32 {
|
||
|
var hexInt: UInt32 = 0
|
||
|
// Create scanner
|
||
|
let scanner: Scanner = Scanner(string: hexStr)
|
||
|
// Tell scanner to skip the # character
|
||
|
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
|
||
|
// Scan hex value
|
||
|
scanner.scanHexInt32(&hexInt)
|
||
|
return hexInt
|
||
|
}
|
||
|
|
||
|
// MARK: UIWebViewDelegate
|
||
|
|
||
|
func webViewDidStartLoad(_ theWebView: UIWebView) {
|
||
|
// loading url, start spinner, update back/forward
|
||
|
addressLabel.text = NSLocalizedString("Loading...", comment: "")
|
||
|
backButton.isEnabled = theWebView.canGoBack
|
||
|
forwardButton.isEnabled = theWebView.canGoForward
|
||
|
print((browserOptions?.hidespinner)! ? "Yes" : "No")
|
||
|
if !(browserOptions?.hidespinner)! {
|
||
|
spinner.startAnimating()
|
||
|
}
|
||
|
return (navigationDelegate?.webViewDidStartLoad(theWebView))!
|
||
|
}
|
||
|
|
||
|
// func webView(_ theWebView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
|
||
|
// let isTopLevelNavigation: Bool? = request.url == request.mainDocumentURL
|
||
|
// if isTopLevelNavigation ?? false {
|
||
|
// currentURL = request.url
|
||
|
// }
|
||
|
//
|
||
|
// return (navigationDelegate?.webView(theWebView, shouldStartLoadWith: request, navigationType: navigationType))!
|
||
|
// }
|
||
|
|
||
|
func webViewDidFinishLoad(_ theWebView: UIWebView) {
|
||
|
// update url, stop spinner, update back/forward
|
||
|
currentURL = theWebView.request?.url
|
||
|
addressLabel.text = currentURL?.absoluteString
|
||
|
backButton.isEnabled = theWebView.canGoBack
|
||
|
forwardButton.isEnabled = theWebView.canGoForward
|
||
|
spinner.stopAnimating()
|
||
|
|
||
|
// Work around a bug where the first time a PDF is opened, all UIWebViews
|
||
|
// reload their User-Agent from NSUserDefaults.
|
||
|
// This work-around makes the following assumptions:
|
||
|
// 1. The app has only a single Cordova Webview. If not, then the app should
|
||
|
// take it upon themselves to load a PDF in the background as a part of
|
||
|
// their start-up flow.
|
||
|
// 2. That the PDF does not require any additional network requests. We change
|
||
|
// the user-agent here back to that of the CDVViewController, so requests
|
||
|
// from it must pass through its white-list. This *does* break PDFs that
|
||
|
// contain links to other remote PDF/websites.
|
||
|
// More info at https://issues.apache.org/jira/browse/CB-2225
|
||
|
let isPDF: Bool = "true" == theWebView.stringByEvaluatingJavaScript(from: "document.body==null")
|
||
|
if isPDF {
|
||
|
//CDVUserAgentUtil.setUserAgent(prevUserAgent, lockToken: userAgentLockToken)
|
||
|
}
|
||
|
navigationDelegate?.webViewDidFinishLoad(theWebView)
|
||
|
}
|
||
|
|
||
|
func webView(_ theWebView: UIWebView, didFailLoadWithError error: Error) {
|
||
|
// log fail message, stop spinner, update back/forward
|
||
|
print("webView:didFailLoadWithError - \(Int(error._code)): \(error.localizedDescription)")
|
||
|
backButton.isEnabled = theWebView.canGoBack
|
||
|
forwardButton.isEnabled = theWebView.canGoForward
|
||
|
spinner.stopAnimating()
|
||
|
addressLabel.text = NSLocalizedString("Load Error", comment: "")
|
||
|
navigationDelegate?.webView(theWebView, didFailLoadWithError: error)
|
||
|
}
|
||
|
|
||
|
// MARK: CDVScreenOrientationDelegate
|
||
|
// func shouldAutorotate() -> Bool {
|
||
|
// if (orientationDelegate != nil) && (orientationDelegate?.responds(to: #selector(getter: self.shouldAutorotate)))! {
|
||
|
// return orientationDelegate!.shouldAutorotate
|
||
|
// }
|
||
|
// return true
|
||
|
// }
|
||
|
//
|
||
|
// func supportedInterfaceOrientations() -> Int {
|
||
|
// if (orientationDelegate != nil) && (orientationDelegate?.responds(to: #selector(getter: self.supportedInterfaceOrientations)))! {
|
||
|
// return Int(orientationDelegate!.supportedInterfaceOrientations.rawValue)
|
||
|
// }
|
||
|
// //return Int(1 << .portrait)
|
||
|
// return 1
|
||
|
// }
|
||
|
|
||
|
// override func shouldAutorotate(to interfaceOrientation: UIInterfaceOrientation) -> Bool {
|
||
|
// if (orientationDelegate != nil) && (orientationDelegate?.responds(to: #selector(self.shouldAutorotateToInterfaceOrientation)))! {
|
||
|
// return orientationDelegate!.shouldAutorotate(to: interfaceOrientation)
|
||
|
// }
|
||
|
// return true
|
||
|
// }
|
||
|
|
||
|
}
|
||
|
|
||
|
class CDVInAppBrowserNavigationController: UINavigationController {
|
||
|
weak var orientationDelegate: AnyObject?
|
||
|
|
||
|
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||
|
if presentedViewController != nil {
|
||
|
super.dismiss(animated: flag, completion: completion)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override func viewDidLoad() {
|
||
|
var statusBarFrame: CGRect = invertFrameIfNeeded(UIApplication.shared.statusBarFrame)
|
||
|
statusBarFrame.size.height = CGFloat(STATUSBAR_HEIGHT)
|
||
|
// simplified from: http://stackoverflow.com/a/25669695/219684
|
||
|
let bgToolbar = UIToolbar(frame: statusBarFrame)
|
||
|
bgToolbar.barStyle = .default
|
||
|
bgToolbar.autoresizingMask = .flexibleWidth
|
||
|
view.addSubview(bgToolbar)
|
||
|
super.viewDidLoad()
|
||
|
}
|
||
|
|
||
|
func invertFrameIfNeeded(_ rect: CGRect) -> CGRect {
|
||
|
// We need to invert since on iOS 7 frames are always in Portrait context
|
||
|
// if !IsAtLeastiOSVersion("8.0") {
|
||
|
// if UIInterfaceOrientationIsLandscape(UIApplication.shared.statusBarOrientation) {
|
||
|
// let temp: CGFloat = rect.size.width
|
||
|
// rect.size.width = rect.size.height
|
||
|
// rect.size.height = temp
|
||
|
// }
|
||
|
// rect.origin = CGPoint.zero
|
||
|
// }
|
||
|
return rect
|
||
|
}
|
||
|
|
||
|
// MARK: CDVScreenOrientationDelegate
|
||
|
override var shouldAutorotate: Bool {
|
||
|
if (orientationDelegate != nil) && (orientationDelegate?.responds(to: #selector(getter: self.shouldAutorotate)))! {
|
||
|
return orientationDelegate!.shouldAutorotate
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||
|
if (orientationDelegate != nil) && (orientationDelegate?.responds(to: #selector(getter: self.supportedInterfaceOrientations)))! {
|
||
|
return orientationDelegate!.supportedInterfaceOrientations
|
||
|
}
|
||
|
//return Int(1 << .portrait)
|
||
|
return UIInterfaceOrientationMask(rawValue: 1)
|
||
|
}
|
||
|
|
||
|
// override func shouldAutorotate(to interfaceOrientation: UIInterfaceOrientation) -> Bool {
|
||
|
// if (orientationDelegate != nil) && (orientationDelegate?.responds(to: #selector(self.shouldAutorotateToInterfaceOrientation)))! {
|
||
|
// return orientationDelegate!.shouldAutorotate(to: interfaceOrientation)
|
||
|
// }
|
||
|
// return true
|
||
|
// }
|
||
|
}
|