はじめに
先日,WKWebViewのestimatedProgressが1.0になったら
自然にUIProgressViewが非表示になるように見せる1 という
記事を投稿しました。挙動の違いでモヤモヤ感は残ったものの
fatal ではないのでそのままアプリに移植し,運用していました。
今日,急に思い立って移植したプロジェクトに
Swift Lint を導入し,リファクタリングをしていたら
下記の warning が出ていました。
warning: Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo)
Swift 3.2 以降では keypaths を持つ新しいブロックベースの KVO APIを推奨します。
とのことなので Swift 4 用に書き直して見ました。
実装
環境
- Xcode 9.2
- Swift 4 (API 自体は Swift 3.2 以上)
- macOS 10.12.6 以降
サンプルコード
以前の記事のサンプルに変更加えました。
サンプルは下記になります。動作的には同じに見えます。
これまでの実装
WKWebView
の estimatedProgress
の値の変化を監視して
UIProgressView
に反映する実装部分を抜き出すと下記になります。
一般的な KVO の形で keyPath の文字列比較や
オブザーバの生成・削除などいろいろやることがありました。
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// estimatedProgress を監視
self.webView.addObserver(self, forKeyPath: ObserveKey.estimatedProgress, options: .new, context: nil)
}
deinit {
self.webView.navigationDelegate = nil
self.webView.uiDelegate = nil
// オブザーバを削除
self.webView.removeObserver(self, forKeyPath: ObserveKey.estimatedProgress, context: nil)
}
func setUpUI() {
self.progressView = UIProgressView(frame: CGRect(x: 0.0,
y: (self.navigationController?.navigationBar.frame.size.height)! - 3.0,
width: self.view.frame.size.width,
height: 3.0))
self.progressView.progressViewStyle = .bar
self.progressView.progressTintColor = .red
self.navigationController?.navigationBar.addSubview(self.progressView)
}
// MARK: - KVO
/// WebViewの読み込み時のProgress KVO
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if (keyPath == ObserveKey.estimatedProgress) {
self.progressView.alpha = 1.0
// estimatedProgressが変更されたときにプログレスバーの値を変更
self.progressView.setProgress(Float(self.webView.estimatedProgress), animated: true)
if (self.webView.estimatedProgress >= 1.0) {
UIView.animate(withDuration: 0.3,
delay: 0.3,
options: [.curveEaseOut],
animations: { [weak self] in
self?.progressView.alpha = 0.0
}, completion: { _ in
self.progressView.setProgress(0.0, animated: false)
})
}
}
}
新しい KVO API を使ってみる
新しい Block ベースの KVO API について
warning の文章をそのまま検索してみたところ,
Swift Lint の issue2 に上がっていたので,参考に書き直しました。
下記のようになりました。
// nil を書くと Redundant Optional Initialization Violation で冗長
var observeLoading: NSKeyValueObservation?
func setUpUI() {
self.progressView = UIProgressView(frame: CGRect(x: 0.0,
y: (self.navigationController?.navigationBar.frame.size.height)! - 3.0,
width: self.view.frame.size.width,
height: 3.0))
self.progressView.progressViewStyle = .bar
self.navigationController?.navigationBar.addSubview(self.progressView)
// Swift 3.2 以降の KVO
self.observeKeysFowWebView()
}
func observeKeysFowWebView() {
// WKWebView の estimatedProgress の値監視(中の処理は同じ)
self.observeEstimatedProgress = self.webView.observe(\.estimatedProgress, options: [.new], changeHandler: { (webView, change) in
self.progressView.alpha = 1.0
// estimatedProgressが変更されたときにプログレスバーの値を変更
self.progressView.setProgress(Float(change.newValue!), animated: true)
if (self.webView.estimatedProgress >= 1.0) {
UIView.animate(withDuration: 0.3,
delay: 0.3,
options: [.curveEaseOut],
animations: { [weak self] in
self?.progressView.alpha = 0.0
}, completion: { _ in
self.progressView.setProgress(0.0, animated: false)
})
}
})
}
WKWebView
だと他に title
,URL
,loading
がありますね。
同様にすれば対応可能だと思います。
今回は新しい値だけをみたのですが,
色々対応するなら options
は [.new, .old]
の方がいいのかな。
おわりに
今回は,Swift 3.2 以降で推奨になった Block ベースの KVO について
WKWebView
の estimatedProgress
の値監視を例に記事を書きました。
Swift 3.2 から推奨ということでそれ以下のバージョンに対応するなら
これまで通りの KVO でプロパティ監視することになります。
Swift Lint 導入して警告や warning を修正するにあたって,
気づくことも多く,うまく付き合っていけたらなと思います。
ご覧いただきありがとうございました。
理解を深めたいのでコード間違いやご意見ありましたらよろしくお願いいたします。