iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Mobile Development

從零開始的Swift開發心路歷程2系列 第 19

【從零開始的Swift開發心路歷程2-Day19】Combine(1)- Custom publisher in UIControl

  • 分享至 

  • xImage
  •  

昨天介紹完Combine後,想說那今天就來做個小練習吧!
首先 先在ViewModel中宣告一個Set< AnyCancellable >()

var uiObservers = Set<AnyCancellable>()

而就當我在想要在textField開始編輯後新增點擊畫面空白處可以收起鍵盤的功能後發現了一件事。

就是UIKit竟然沒有Event Publisher,所以上網查了一下資料,發現國外有一位大神SwiftLee寫出來了!

建立UIControl的Subscription

因為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(())
        }

建立UIControl的Publisher

限制了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


上一篇
【從零開始的Swift開發心路歷程2-Day18】Combine簡介
下一篇
【從零開始的Swift開發心路歷程2-Day20】Combine(2)- Combine小練習
系列文
從零開始的Swift開發心路歷程230
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言