昨天介紹完Combine後,想說那今天就來做個小練習吧!
首先 先在ViewModel中宣告一個Set< AnyCancellable >()
var uiObservers = Set<AnyCancellable>()
而就當我在想要在textField開始編輯後新增點擊畫面空白處可以收起鍵盤的功能後發現了一件事。
就是UIKit竟然沒有Event Publisher,所以上網查了一下資料,發現國外有一位大神SwiftLee寫出來了!
因為UIControl的@IBAction生命週期是與Target綁定的,如此以來就無法使用Combine來控制事件,所以我們必須自行建立控制事件的方法,並指定這個Subscription為UIControl的Target,並在事件發生時使Subscriber成功接收。
class InteractionSubscription<S: Subscriber>: Subscription
where S.Input == Void {
private let subscriber: S?
private let control: UIControl
private let event: UIControl.Event
init(subscriber: S,
control: UIControl,
event: UIControl.Event) {
self.subscriber = subscriber
self.control = control
self.event = event
self.control.addTarget(self, action: #selector(handleEvent), for: event)
}
func request(_ demand: Subscribers.Demand) {}
func cancel() {}
@objc func handleEvent(_ sender: UIControl) {
_ = self.subscriber?.receive(())
}
限制了Output和Failure的型別,並且把subscription指定給InteractionSubscription
class InteractionPublisher: Publisher {
typealias Output = Void
typealias Failure = Never
private let control: UIControl
private let event: UIControl.Event
init(control: UIControl, event: UIControl.Event) {
self.control = control
self.event = event
}
func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, Void == S.Input {
let subscription = InteractionSubscription(
subscriber: subscriber,
control: control,
event: event
)
subscriber.receive(subscription: subscription)
}
}
func publisher(for event: UIControl.Event) -> UIControl.InteractionPublisher {
return InteractionPublisher(control: self, event: event)
}
把這兩個class加進UIControl的Extension就能成功使用UIControl Event囉!
myTextField.publisher(for: .editingDidBegin).sink{
[unowned self] _ in
addGestureRecognizer()
}.store(in: &mainViewModel.uiObservers)
參考資料:
Creating a custom Combine Publisher to extend UIKit
30 天了解 Swift 的 Combine: [20] Combine 好文分享: Custom publisher in UIControl