@@ -11,11 +11,28 @@ import WebKit
11
11
12
12
public class MHWebViewController : UIViewController {
13
13
14
- public private( set) lazy var webView : WKWebView = WKWebView ( frame: CGRect . zero)
14
+ enum WebViewKeyPath : String {
15
+ case estimatedProgress
16
+ case title
17
+ }
15
18
16
- private lazy var toolbar : UIToolbar = UIToolbar ( frame: CGRect . zero)
17
19
private lazy var container = UIView ( frame: CGRect . zero)
18
- private lazy var progressView = UIProgressView ( progressViewStyle: . default)
20
+ private lazy var progressView = UIProgressView ( progressViewStyle: . bar)
21
+ public private( set) lazy var webView : WKWebView = WKWebView ( frame: CGRect . zero)
22
+
23
+ private lazy var toolbar : UIView = {
24
+ let v = UIView ( frame: CGRect . zero)
25
+ v. isUserInteractionEnabled = true
26
+ v. heightAnchor. constraint ( equalToConstant: 44.0 ) . isActive = true
27
+ v. translatesAutoresizingMaskIntoConstraints = false
28
+
29
+ let blurEffect = UIBlurEffect ( style: . light)
30
+ let blurEffectView = UIVisualEffectView ( effect: blurEffect)
31
+ v. addSubview ( blurEffectView)
32
+ blurEffectView. bindFrameToSuperviewBounds ( )
33
+ return v
34
+ } ( )
35
+
19
36
private lazy var titleLabel : UILabel = {
20
37
let lbl = UILabel ( frame: CGRect ( x: 0.0 , y: 0.0 , width: 250.0 , height: 16.0 ) )
21
38
lbl. adjustsFontSizeToFitWidth = true
@@ -35,9 +52,7 @@ public class MHWebViewController:UIViewController {
35
52
} ( )
36
53
37
54
private let topMargin : CGFloat = 10.0
38
-
39
55
private var lastLocation : CGPoint = . zero
40
-
41
56
public var request : URLRequest !
42
57
43
58
public override var title : String ? {
@@ -56,94 +71,119 @@ public class MHWebViewController:UIViewController {
56
71
57
72
override public func loadView( ) {
58
73
super. loadView ( )
74
+ setupMainLayout ( )
75
+ setupToolbar ( )
76
+ }
77
+
78
+ override public func viewDidLoad( ) {
79
+ super. viewDidLoad ( )
80
+ addPanGestureRecognizer ( )
81
+ titleLabel. text = titleHidden ? " " : NSLocalizedString (
82
+ " Loading... " , comment: " the loading text at the top " )
83
+ webView. navigationDelegate = self
84
+ webView. load ( request)
85
+ }
86
+
87
+ public override func viewDidAppear( _ animated: Bool ) {
88
+ super. viewDidAppear ( animated)
89
+ addWebViewObservers ( )
90
+ }
91
+
92
+ public override func viewDidDisappear( _ animated: Bool ) {
93
+ super. viewDidDisappear ( animated)
94
+ removeWebViewObservers ( )
95
+ }
96
+
97
+ private func setupToolbar( ) {
98
+ // toolbar
99
+ let closeButton = createImageButton ( imageName: " close_button " )
100
+ closeButton. addTarget ( self , action: #selector( dismissMe ( _: ) ) , for: . touchUpInside)
101
+ closeButton. tintColor = . gray
102
+ closeButton. widthAnchor. constraint ( equalTo: closeButton. heightAnchor) . isActive = true
103
+
104
+ let titleStackView = UIStackView ( arrangedSubviews: [ titleLabel, urlLabel] )
105
+ titleStackView. axis = . vertical
106
+
107
+ let toolbarStackView = UIStackView ( arrangedSubviews: [ closeButton, titleStackView] )
108
+ toolbarStackView. spacing = 2.0
109
+ toolbarStackView. axis = . horizontal
110
+ toolbar. addSubview ( toolbarStackView)
111
+
112
+ toolbarStackView. translatesAutoresizingMaskIntoConstraints = false
113
+ toolbarStackView. topAnchor. constraint ( equalTo: toolbar. topAnchor, constant: 5 ) . isActive = true
114
+ toolbarStackView. leadingAnchor. constraint ( equalTo: toolbar. leadingAnchor, constant: 5 ) . isActive = true
115
+ toolbarStackView. bottomAnchor. constraint ( equalTo: toolbar. bottomAnchor, constant: - 5 ) . isActive = true
116
+ toolbarStackView. trailingAnchor. constraint ( equalTo: toolbar. trailingAnchor, constant: - 49 ) . isActive = true
117
+ }
118
+
119
+ private func createImageButton( imageName: String ) -> UIButton {
120
+ guard let closeImage = UIImage (
121
+ named: imageName,
122
+ in: Bundle ( for: MHWebViewController . self) ,
123
+ compatibleWith: nil ) else { fatalError ( " No image named \( imageName) in the MHWebViewController.bundle " ) }
124
+ let closeButton = UIButton ( type: . custom)
125
+ closeButton. setImage ( closeImage, for: . normal)
126
+ return closeButton
127
+ }
128
+
129
+ private func setupMainLayout( ) {
59
130
view = UIView ( )
60
131
view. autoresizingMask = [ . flexibleHeight, . flexibleWidth]
61
- view. backgroundColor = UIColor . clear
62
-
63
- // Setup container
132
+ view. backgroundColor = . clear
64
133
view. addSubview ( container)
65
134
container. translatesAutoresizingMaskIntoConstraints = false
66
135
container. topAnchor. constraint (
67
136
equalTo: view. safeTopAnchor, constant: topMargin) . isActive = true
68
- container. heightAnchor . constraint (
69
- equalTo: view. heightAnchor , constant : - topMargin - 44.0 ) . isActive = true
137
+ container. bottomAnchor . constraint (
138
+ equalTo: view. bottomAnchor ) . isActive = true
70
139
container. leadingAnchor. constraint (
71
140
equalTo: view. safeLeadingAnchor, constant: 0 ) . isActive = true
72
141
container. trailingAnchor. constraint (
73
142
equalTo: view. safeTrailingtAnchor, constant: 0 ) . isActive = true
74
- container. layer. cornerRadius = 10 .0
143
+ container. layer. cornerRadius = 16 .0
75
144
container. clipsToBounds = true
76
-
77
- addPanGestureRecognizer ( )
78
- guard let closeImage = UIImage (
79
- named: " close_button " ,
80
- in: Bundle ( for: MHWebViewController . self) ,
81
- compatibleWith: nil ) else { return }
82
145
83
- let closeButton = UIBarButtonItem (
84
- image: closeImage,
85
- style: . plain,
86
- target: self ,
87
- action: #selector( dismissMe ( _: ) ) )
88
- closeButton. tintColor = UIColor . darkGray
89
-
90
- let titleStackView = UIStackView ( arrangedSubviews: [ titleLabel, urlLabel] )
91
- titleStackView. axis = . vertical
92
- let titleItem = UIBarButtonItem ( customView: titleStackView)
93
-
94
- let flexibleSpace = UIBarButtonItem ( barButtonSystemItem: . flexibleSpace, target: self , action: nil )
95
- toolbar. items = [ closeButton, flexibleSpace, titleItem, flexibleSpace]
96
-
97
146
let mainStackView = UIStackView ( arrangedSubviews: [ toolbar, progressView, webView] )
98
147
mainStackView. axis = . vertical
99
148
container. addSubview ( mainStackView)
100
149
mainStackView. bindFrameToSuperviewBounds ( )
101
150
}
102
151
103
- override public func viewDidLoad( ) {
104
- super. viewDidLoad ( )
105
- titleLabel. text = titleHidden ? " " : NSLocalizedString ( " LOADING... " , comment: " the loading text at the top " )
106
- webView. navigationDelegate = self
107
- webView. load ( request)
108
- }
109
-
110
- public override func viewDidAppear( _ animated: Bool ) {
152
+ private func addWebViewObservers( ) {
111
153
webView. addObserver ( self , forKeyPath: #keyPath( WKWebView . estimatedProgress) , options: . new, context: nil )
112
154
webView. addObserver ( self , forKeyPath: #keyPath( WKWebView . title) , options: . new, context: nil )
113
155
webView. addObserver ( self , forKeyPath: #keyPath( WKWebView . canGoBack) , options: . new, context: nil )
114
156
webView. addObserver ( self , forKeyPath: #keyPath( WKWebView . canGoForward) , options: . new, context: nil )
115
157
}
116
158
117
- public override func viewDidDisappear ( _ animated : Bool ) {
159
+ private func removeWebViewObservers ( ) {
118
160
webView. removeObserver ( self , forKeyPath: #keyPath( WKWebView . estimatedProgress) )
119
161
webView. removeObserver ( self , forKeyPath: #keyPath( WKWebView . title) )
120
162
webView. removeObserver ( self , forKeyPath: #keyPath( WKWebView . canGoBack) )
121
163
webView. removeObserver ( self , forKeyPath: #keyPath( WKWebView . canGoForward) )
122
164
}
123
165
124
- @objc private func dismissMe( _ sender: UIBarButtonItem ) {
166
+ @objc private func dismissMe( _ sender: UIButton ) {
125
167
dismiss ( completion: nil )
126
168
}
127
169
128
170
public func dismiss( completion: ( ( ) -> Void ) ? = nil ) {
129
171
dismiss ( animated: true , completion: completion)
130
172
}
131
173
132
- override public func observeValue(
133
- forKeyPath keyPath: String ? ,
134
- of object: Any ? ,
174
+ override public func observeValue( forKeyPath keyPath: String ? , of object: Any ? ,
135
175
change: [ NSKeyValueChangeKey : Any ] ? ,
136
176
context: UnsafeMutableRawPointer ? ) {
137
177
138
178
switch keyPath {
139
- case " estimatedProgress " :
179
+ case WebViewKeyPath . estimatedProgress. rawValue :
140
180
progressView. progress = Float ( webView. estimatedProgress)
141
181
if progressView. progress == 1.0 {
142
182
progressView. alpha = 0.0
143
183
} else if progressView. alpha != 1.0 {
144
184
progressView. alpha = 1.0
145
185
}
146
- case " title " :
186
+ case WebViewKeyPath . title. rawValue :
147
187
title = titleHidden ? " " : webView. title
148
188
if !titleHidden, let scheme = webView. url? . scheme,
149
189
let host = webView. url? . host {
@@ -225,70 +265,3 @@ extension MHWebViewController:WKNavigationDelegate {
225
265
decisionHandler ( . allow)
226
266
}
227
267
}
228
-
229
- public extension UIViewController {
230
-
231
- // Shortcuts
232
- @objc
233
- func present( urlRequest: URLRequest , titleHidden: Bool = false , completion: ( ( ) -> Void ) ? = nil ) {
234
- let web = MHWebViewController ( )
235
- web. request = urlRequest
236
- web. modalPresentationStyle = . overCurrentContext
237
- web. titleHidden = titleHidden
238
- present ( web, animated: true , completion: completion)
239
- }
240
-
241
- @objc
242
- func present( url: URL , titleHidden: Bool = false , completion: ( ( ) -> Void ) ? = nil ) {
243
- let urlRequest = URLRequest ( url: url)
244
- present ( urlRequest: urlRequest, titleHidden: titleHidden, completion: completion)
245
- }
246
- }
247
-
248
- fileprivate extension UIView {
249
-
250
- var safeTopAnchor : NSLayoutYAxisAnchor {
251
- if #available( iOS 11 . 0 , * ) {
252
- return self . safeAreaLayoutGuide. topAnchor
253
- }
254
- return self . topAnchor
255
- }
256
-
257
- var safeLeadingAnchor : NSLayoutXAxisAnchor {
258
- if #available( iOS 11 . 0 , * ) {
259
- return self . safeAreaLayoutGuide. leadingAnchor
260
- }
261
- return self . leadingAnchor
262
- }
263
-
264
- var safeTrailingtAnchor : NSLayoutXAxisAnchor {
265
- if #available( iOS 11 . 0 , * ) {
266
- return self . safeAreaLayoutGuide. trailingAnchor
267
- }
268
- return self . trailingAnchor
269
- }
270
-
271
- var safeBottomAnchor : NSLayoutYAxisAnchor {
272
- if #available( iOS 11 . 0 , * ) {
273
- return self . safeAreaLayoutGuide. bottomAnchor
274
- }
275
- return self . bottomAnchor
276
- }
277
-
278
- func bindFrameToSuperviewBounds( ) {
279
- guard let superview = self . superview else {
280
- print ( " Error! `superview` was nil – call `addSubview(view: UIView)` " )
281
- return
282
- }
283
-
284
- self . translatesAutoresizingMaskIntoConstraints = false
285
- self . topAnchor. constraint (
286
- equalTo: superview. topAnchor, constant: 0 ) . isActive = true
287
- self . bottomAnchor. constraint (
288
- equalTo: superview. bottomAnchor, constant: 0 ) . isActive = true
289
- self . leadingAnchor. constraint (
290
- equalTo: superview. leadingAnchor, constant: 0 ) . isActive = true
291
- self . trailingAnchor. constraint (
292
- equalTo: superview. trailingAnchor, constant: 0 ) . isActive = true
293
- }
294
- }
0 commit comments