--- /dev/null
+//
+// Created by Jesse Squires
+// http://www.jessesquires.com
+//
+//
+// Documentation
+// http://jessesquires.github.io/JSQWebViewController
+//
+//
+// GitHub
+// https://github.com/jessesquires/JSQWebViewController
+//
+//
+// License
+// Copyright (c) 2015 Jesse Squires
+// Released under an MIT license: http://opensource.org/licenses/MIT
+//
+
+import UIKit
+import WebKit
+
+
+private let titleKeyPath = "title"
+private let estimatedProgressKeyPath = "estimatedProgress"
+
+
+/// An instance of `WebViewController` displays interactive web content.
+open class WebViewController: UIViewController {
+
+ // MARK: Properties
+
+ /// Returns the web view for the controller.
+ public final var webView: WKWebView {
+ get {
+ return _webView
+ }
+ }
+
+ /// Returns the progress view for the controller.
+ public final var progressBar: UIProgressView {
+ get {
+ return _progressBar
+ }
+ }
+
+ /// The URL request for the web view. Upon setting this property, the web view immediately begins loading the request.
+ public final var urlRequest: URLRequest {
+ didSet {
+ webView.load(urlRequest)
+ }
+ }
+
+ /**
+ Specifies whether or not to display the web view title as the navigation bar title.
+ The default is `false`, which sets the navigation bar title to the URL host name of the URL request.
+ */
+ public final var displaysWebViewTitle: Bool = false
+
+ // MARK: Private properties
+
+ private final let configuration: WKWebViewConfiguration
+ private final let activities: [UIActivity]?
+
+ private lazy final var _webView: WKWebView = {
+ let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
+ webView.addObserver(self, forKeyPath: titleKeyPath, options: .new, context: nil)
+ webView.addObserver(self, forKeyPath: estimatedProgressKeyPath, options: .new, context: nil)
+ webView.allowsBackForwardNavigationGestures = true
+ if #available(iOS 9.0, *) {
+ webView.allowsLinkPreview = true
+ }
+ return webView
+ }()
+
+ private lazy final var _progressBar: UIProgressView = {
+ let progressBar = UIProgressView(progressViewStyle: .bar)
+ progressBar.backgroundColor = .clear
+ progressBar.trackTintColor = .clear
+ return progressBar
+ }()
+
+ // MARK: Initialization
+
+ /**
+ Constructs a new `WebViewController`.
+
+ - parameter urlRequest: The URL request for the web view to load.
+ - parameter configuration: The configuration for the web view.
+ - parameter activities: The custom activities to display in the `UIActivityViewController` that is presented when the action button is tapped.
+
+ - returns: A new `WebViewController` instance.
+ */
+ public init(urlRequest: URLRequest, configuration: WKWebViewConfiguration = WKWebViewConfiguration(), activities: [UIActivity]? = nil) {
+ self.configuration = configuration
+ self.urlRequest = urlRequest
+ self.activities = activities
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ /**
+ Constructs a new `WebViewController`.
+
+ - parameter url: The URL to display in the web view.
+
+ - returns: A new `WebViewController` instance.
+ */
+ public convenience init(url: URL) {
+ self.init(urlRequest: URLRequest(url: url))
+ }
+
+ /// :nodoc:
+ public required init?(coder aDecoder: NSCoder) {
+ self.configuration = WKWebViewConfiguration()
+ self.urlRequest = URLRequest(url: URL(string: "http://")!)
+ self.activities = nil
+ super.init(coder: aDecoder)
+ }
+
+ deinit {
+ webView.removeObserver(self, forKeyPath: titleKeyPath, context: nil)
+ webView.removeObserver(self, forKeyPath: estimatedProgressKeyPath, context: nil)
+ }
+
+
+ // MARK: View lifecycle
+
+ /// :nodoc:
+ open override func viewDidLoad() {
+ super.viewDidLoad()
+ title = urlRequest.url?.host
+ view.addSubview(_webView)
+ view.addSubview(_progressBar)
+
+ if presentingViewController?.presentedViewController != nil {
+ navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done,
+ target: self,
+ action: #selector(didTapDoneButton(_:)))
+ }
+
+ navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action,
+ target: self,
+ action: #selector(didTapActionButton(_:)))
+
+ webView.load(urlRequest)
+ }
+
+ /// :nodoc:
+ open override func viewWillAppear(_ animated: Bool) {
+ assert(navigationController != nil, "\(WebViewController.self) must be presented in a \(UINavigationController.self)")
+ super.viewWillAppear(animated)
+ }
+
+ /// :nodoc:
+ open override func viewDidDisappear(_ animated: Bool) {
+ super.viewDidDisappear(animated)
+ webView.stopLoading()
+ }
+
+ /// :nodoc:
+ open override func viewDidLayoutSubviews() {
+ super.viewDidLayoutSubviews()
+ webView.frame = view.bounds
+
+ let isIOS11 = ProcessInfo.processInfo.isOperatingSystemAtLeast(
+ OperatingSystemVersion(majorVersion: 11, minorVersion: 0, patchVersion: 0))
+ let top = isIOS11 ? CGFloat(0.0) : topLayoutGuide.length
+ let insets = UIEdgeInsets(top: top, left: 0, bottom: 0, right: 0)
+ webView.scrollView.contentInset = insets
+ webView.scrollView.scrollIndicatorInsets = insets
+
+ view.bringSubview(toFront: progressBar)
+ progressBar.frame = CGRect(x: view.frame.minX,
+ y: topLayoutGuide.length,
+ width: view.frame.size.width,
+ height: 2)
+ }
+
+
+ // MARK: Actions
+
+ @objc private func didTapDoneButton(_ sender: UIBarButtonItem) {
+ dismiss(animated: true, completion: nil)
+ }
+
+ @objc private func didTapActionButton(_ sender: UIBarButtonItem) {
+ if let url = urlRequest.url {
+ let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: activities)
+ activityVC.popoverPresentationController?.barButtonItem = sender
+ present(activityVC, animated: true, completion: nil)
+ }
+ }
+
+
+ // MARK: KVO
+
+ /// :nodoc:
+ open override func observeValue(forKeyPath keyPath: String?,
+ of object: Any?,
+ change: [NSKeyValueChangeKey : Any]?,
+ context: UnsafeMutableRawPointer?) {
+ guard let theKeyPath = keyPath , object as? WKWebView == webView else {
+ super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
+ return
+ }
+
+ if displaysWebViewTitle && theKeyPath == titleKeyPath {
+ title = webView.title
+ }
+
+ if theKeyPath == estimatedProgressKeyPath {
+ updateProgress()
+ }
+ }
+
+ // MARK: Private
+
+ private final func updateProgress() {
+ let completed = webView.estimatedProgress == 1.0
+ progressBar.setProgress(completed ? 0.0 : Float(webView.estimatedProgress), animated: !completed)
+ UIApplication.shared.isNetworkActivityIndicatorVisible = !completed
+ }
+}