美文网首页
基于Combine的响应式UIControl

基于Combine的响应式UIControl

作者: 双鱼子曰1987 | 来源:发表于2023-04-20 16:33 被阅读0次

一、概述

iOS开发中UIKit中控件的交互方式默认是Target-Action,这种方式简单且直观。不过,一个问题在于编码方式太过于繁琐,需要定义一个方法,然后调用addTartget方式进行绑定;在复杂页面交互,需要跨多级数据传递的时候,就变得异常繁琐。
后面响应式和函数式编程兴起,诞生RxSwift等的响应式框架,全新的开发体验确实提高的开发效率,不过带来的问题就是堆栈太深,排查问题不利于排查。也会有一定的损耗,这么多的堆栈必然占用更多的系统资源,性能的话会有一定影响。
iOS13后,apple要推广swiftUI带来了Combine,其实apple的响应式框架,亲儿子,在框架底层和Swift层面都进行一定的优化,堆栈和性能会比RxSwift等更优。随着iOS13的不断普及,Combine会越来越受欢迎。

不过SwiftUI发展必然不会那么快速,项目中还是有很多的UIKit的代码需要维护。

本文不在于介绍Combine的理论知识,而是在于扩展UIKit的UIControl支持响应式编程方式。

二、如何实现?

自定义 Publisher 和 Subscriber

  • 第一步,自定义Subscription 中介对象
  • 第二步,自定义Publisher 发布者
  • 第三部,扩展第三方支持Publisher
/// 自定义
extension Publishers {
    /// 1、自定义 Subscription
    /// 定义输入类型为UIControl,错误类型为Never
    private final class UIControlSubscription<S:Subscriber, Control:UIControl> : Subscription where S.Input == Control, S.Failure == Never {
        private var subscriber: S?
        private var control: Control
        private var events: Control.Event
        /// Step 1 : 初始化
        init(subscriber: S?, control: Control, events: Control.Event) {
            self.subscriber = subscriber
            self.control = control
            self.events = events
            configControl()
        }
        deinit {
            print("UIControlSubscription deinit~~~")
        }

        
        /// Step 2 : 关联 与 控制
        func configControl() {
            self.control.addTarget(self, action: #selector(eventHandler), for: self.events)
        }
        
        @objc func eventHandler() {
            // 忽略返回值
            _ = self.subscriber?.receive(self.control)
        }
        
        func request(_ demand: Subscribers.Demand) {
            
        }
        
        /// Step 3 : 销毁
        func cancel() {
            // 销毁订阅者
            subscriber = nil
        }

    }
    
    /// 2、自定义 Publisher
    struct UIControlPublisher<Control: UIControl> : Publisher {
        typealias Output = UIControl
        typealias Failure = Never
        
        private var control: Control
        private var events: Control.Event
        /// Step 1 : 初始化
        init(control: Control, events: Control.Event) {
            self.control = control
            self.events = events
        }
        
        /// Step 2 :通过 Subscription 将 订阅者Subscriber 连接到 发布者Publisher
        func receive<S>(subscriber: S) where S : Subscriber, Never == S.Failure, UIControl == S.Input {
            let subscription = UIControlSubscription(subscriber: subscriber, control: self.control, events: self.events)
            subscriber.receive(subscription: subscription)
        }
    }
}

extension UIControl {
    func publisher(events: UIControl.Event) -> Publishers.UIControlPublisher<UIControl> {
        return Publishers.UIControlPublisher(control: self, events: events)
    }
}
extension UISwitch {
    func publisher() -> AnyPublisher<Bool, Never> {
        return Publishers.UIControlPublisher(control: self, events: .valueChanged)
            .map{ ($0 as! UISwitch).isOn }
            .eraseToAnyPublisher()
    }
}
extension UISlider {
    func publisher() -> AnyPublisher<Float, Never> {
        return Publishers.UIControlPublisher(control: self, events: .valueChanged)
            .map{ ($0 as! UISlider).value }
            .eraseToAnyPublisher()
    }
}

extension UITextField {
    
}

三、如何用?

private var cancelList: Set<AnyCancellable> = []

let btn_2 = UIButton.init(type: .custom)
btn_2.setTitle("combine", for: .normal)
btn_2.backgroundColor = .blue
addSubview(btn_2)

btn_2.publisher(events: .touchUpInside)
            .receive(on: RunLoop.main)
            .sink { [weak self] (btn) in
                guard let `self` = self else {
                    return
                }
                print("btn combint click")
            }.store(in: &cancelList)

还可以再简化,只保留闭包即可,这部分封装就留着自由发挥了。
如下看起来清爽的多

btn_2.action(events: .touchUpInside) { [weak self] (btn) in
    guard let `self` = self else {
      return
    }
    print("btn combint click")
}

相关文章

  • SwiftUI 与 Combine(简介)

    SwiftUI 与 Combine(简介)什么是SwiftUI?什么是Combine?响应式编程:异步编程:何时可...

  • Combine 基础知识

    摘自《SwiftUI和Combine编程》---《Combine异步编程》 响应式异步编程模型 将“状态变化”看作...

  • 2021-01-29

    Combine之Subjects 在响应式编程的世界中,Subject不太好翻译。在Combine中,存在两个Su...

  • Combine -- 响应式和指令式的桥梁

    Apple 平台的开发,包括系统级别的 API 和大多数的第三方框架,也都是按照指令式编程的方式来编写的。想要在 ...

  • All-Reative全响应式架构Web应用实现

    什么是响应式 目前响应式是一个极度被使用的名词。牛津词典定义响应式是”对刺激作出响应”,因此,响应式软件基于它接受...

  • RxJava

    响应式编程概述 什么是响应式编程? 是一种基于异步数据流概述的编程模式 响应式编程--关键概念 事件 响应式编程-...

  • iOS知识点

    runloop、响应链 UIResponder、UIControl、UIView的关系

  • 响应式网页设计

    1.响应式网页设计的定义 响应式Web设计(Responsive Web Design)简称:RWD,基于HTML...

  • 背压和响应流标准

    先来说一下背压(back pressure),背压是响应式系统引入的概念,响应式系统是基于消息驱动的,响应式宣言对...

  • iOS基础补完计划--透过堆栈看事件响应机制

    目录 前言 TouchEvent 响应链 UIControl UIGestureRecognizer 结论 前言 ...

网友评论

      本文标题:基于Combine的响应式UIControl

      本文链接:https://www.haomeiwen.com/subject/ddnrjdtx.html