AppleがCombine移行として用意した記事は
- NotificationCenter
- 非同期処理
- KVO(Key-Value Observation)
- Timer処理
の4本です。
今回の記事では「Combineを利用してNotificationCenterでkeyboard関連通知を受け取る」という解説をします。
旧来の書き方
Combineを使わないものでは、ViewControllerなどで以下のような処理を記述していました。
removeObserver
を実行している記事もありますが、iOS9以降は不要になっています。
old.swift
import UIKit
class ViewController : UIViewController {
override func viewDidAppear() {
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow(notification:)),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide(notification:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
}
@objc func keyboardWillShow(notification: Notification) {
...
}
@objc func keyboardWillHide(notification: Notification) {
...
}
}
Combine利用
Combineではpublisher
を利用して記述します。今回はオペレーターをいくつか繋げて、keyboardのフレームを取得するようなコードにしてみます。
receive(on:)
は、downstreamの実行スレッドや同期・非同期などを指定できるオペレーターですが、UI周りで使うときはmainスレッドで動いてくれないと予期しない挙動を引き起こすので、必ずRunLoop.main
もしくはDispatchQueue.main
を渡すようにします。
combine.swift
import UIKit
import Combine
class ViewController : UIViewController {
private var cancellables: Set<AnyCancellable> = .init()
override func viewDidLoad() {
NotificationCenter
.default
.publisher(for: UIResponder.keyboardDidShowNotification, object: nil)
.compactMap({ $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect })
.receive(on: RunLoop.main)
.sink { (keyboardFrame) in
// 何らかの処理
}
.store(in: &cancellables)
NotificationCenter
.default
.publisher(for: UIResponder.keyboardDidHideNotification, object: nil)
.receive(on: RunLoop.main)
.sink {
// 何らかの処理
}
.store(in: &cancellables)
}
}