<~操作符
<~的左边是绑定目标(BindingTargetProvider), 右边则是数据源(BindingSource), <~会把右边数据源的发送出的Value直接绑定到左边的目标上。
例如:
let errorLabel: UILabel
let sendButton: UIButton
let phoneNumerTextField: UITextField
var errorText = MutableProperty("")
var validPhoneNumer = MutableProperty("")
errorLabel.reactive.text <~ errorText //绑定错误信息到errorLabel
sendButton.reactive.isEnabled <~ errorText.map{ $0.count == 0 }
sendButton.reactive.backgroundColor <~ errorText.map{ $0.count == 0 ? UIColor.red : UIColor.gray }
phoneNumerTextField.reactive.text <~ validPhoneNumer //绑定有效输入到输入框
对于非现成的绑定目标,可以类比加上拓展,比如给YYLabel加个拓展:
//UILabel的默认拓展
extension Reactive where Base: UILabel {
public var text: BindingTarget {
return makeBindingTarget { $0.text = $1 }//$0表示UI控件本身 $1表示value
}
public var attributedText: BindingTarget {
return makeBindingTarget { $0.attributedText = $1 }
}
}
//给YYLabel也加上拓展
extension Reactive where Base: YYLabel {
public var text: BindingTarget {
return makeBindingTarget { $0.text = $1 }//$0表示UI控件本身 $1表示value
}
public var attributedText: BindingTarget {
return makeBindingTarget { $0.attributedText = $1 }
}
}
那<~是如何实现的呢?
public protocol BindingSource: SignalProducerConvertible {
associatedtype Value
associatedtype Error: Swift.Error
var producer: SignalProducer { get }
}
extension Signal: BindingSource {}
extension SignalProducer: BindingSource {}
//绑定目标提供者
public protocol BindingTargetProvider {
associatedtype Value
var bindingTarget: BindingTarget { get }
}
//绑定目标
public struct BindingTarget: BindingTargetProvider {
public let lifetime: Lifetime //这里定义何时取消订阅数据源
public let action: (Value) -> Void //这里定义了如何利用数据源发送的Value(一般来说就是简单的用Value设置某个属性)
......
}
extension BindingTargetProvider {
@discardableResult
public static func <~
(provider: Self, source: Source) -> Disposable?
where Source.Value == Value, Source.Error == NoError {
//订阅右边的数据源提供的producer, 在订阅回调中执行绑定目标的action闭包
return source.producer
.take(during: provider.bindingTarget.lifetime)
.startWithValues(provider.bindingTarget.action)
}
}
可以看出,订阅右边的数据源提供的producer,在订阅回调中执行绑定目标的action闭包。
我们看看ReactiveSwift是如何给UI控件添加BindingTarget的, 以Label举例:
public protocol ReactiveExtensionsProvider: class {}
extension ReactiveExtensionsProvider {
public var reactive: Reactive {
return Reactive(self)
}
public static var reactive: Reactive.Type {
return Reactive.self
}
}//任何对象获取 someObject.reactive时就返回一个Reactive结构体, Reactive.base即是someObject
public struct Reactive {
public let base: Base
fileprivate init(_ base: Base) {
self.base = base
}
}
extension Reactive where Base: NSObjectProtocol {
//创建一个BindingTarget BindingTarget.action则是在UI线程操作value
public func makeBindingTarget(on scheduler: Scheduler = UIScheduler(), _ action: @escaping (Base, U) -> Void) -> BindingTarget {
return BindingTarget(on: scheduler, lifetime: lifetime) { [weak base = self.base] value in
if let base = base {
action(base, value)
}
}
}
}
extension Reactive where Base: UILabel {
//当获取label.reactive.text时 就创建一个makeBindingTarget makeBindingTarget的action则是直接设置self.text = value
public var text: BindingTarget {
return makeBindingTarget { $0.text = $1 }//$0即是label自己 $1即是value
}
}
Signal常用函数
- KVO
public func signal(forKeyPath keyPath: String) -> Signal<Any?, NoError>
KVO的Reactive版本, 对于NSObject的子类可以直接使用, 对于Swift的原生类需要加上dynamic修饰。
let tableView: UITableView
dynamic var someValue = 0
//对定义的属性KVO
reactive.signal(forKeyPath: "someValue").observeValues { [weak self] (value) in
//code
}
//对tableView的KVO
tableView.reactive.signal(forKeyPath: "contentSize").observeValues {[weak self] (contentSize) in
if let contentSize = contentSize as? CGSize,
let strongSelf = self {
let isHidden = contentSize.height < strongSelf.tableView.height
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now(), execute: {
strongSelf.tableView.mj_footer.isHidden = isHidden
})
}
}
- map
let (signal, innerObserver) = NSignal<Int>.pipe()
signal.map { return "xxx" + String($0) } //对value进一步处理
.observeValues { (value) in
print(value)
}
innerObserver.send(value: 1)
innerObserver.sendCompleted()
输出: xxx1
- on
public func on(
event: ((Event) -> Void)? = nil,
failed: ((Error) -> Void)? = nil,
completed: (() -> Void)? = nil,
interrupted: (() -> Void)? = nil,
terminated: (() -> Void)? = nil,
disposed: (() -> Void)? = nil,
value: ((Value) -> Void)? = nil) -> Signal<Value, Error>
let (signal, innerObserver) = NSignal<Int>.pipe()
signal.on( value: { (value) in
print("on value: \(value)")
}).observeValues { (value) in
print("did received value: \(value)")
}
innerObserver.send(value: 1)
innerObserver.sendCompleted()
输出: on value: 1
did received value: 1
在信号发送事件和订阅者收到事件之间插入一段事件处理逻辑, 你可以把它看做map的简洁版. (这个函数的参数很多, 但默认都有给nil, 所以你只需要关心自己需要的部分即可, 比如这里我只想在Value事件间插入逻辑)。
- take(until:)
public func take(until trigger: Signal<(), NoError>) -> Signal<Value, Error>
let (signal, innerObserver) = NSignal<Int>.pipe()
let (takeSignal, takeObserver) = NSignal<()>.pipe()
signal.take(until: takeSignal).observeValues { (value) in
print("received value: \(value)")
}
innerObserver.send(value: 1)
innerObserver.send(value: 2)
takeObserver.send(value: ())
innerObserver.send(value: 3)
takeObserver.sendCompleted()
innerObserver.sendCompleted()
输出: received value: 1
received value: 2
在takeSignal发送Event之前, signal可以正常发送Event, 一旦takeSignal开始发送Event, signal就停止发送, takeSignal相当于一个停止标志位。
- take(first:)
只取最初N次的Event. 类似的还有signal.take(last: ): 只取最后N次的Event。
public func take(first count: Int) -> Signal<Value, Error>
let (signal, innerObserver) = NSignal<Int>.pipe()
signal.take(first: 2).observeValues { (value) in
print("received value: \(value)")
}
innerObserver.send(value: 1)
innerObserver.send(value: 2)
innerObserver.send(value: 3)
innerObserver.send(value: 4)
innerObserver.sendCompleted()
输出: received value: 1
received value: 2
- merge
把多个信号合并为一个新的信号,任何一个信号有Event的时就会这个新信号就会Event发送出来。
public static func merge(_ signals: Signal<Value, Error>...) -> Signal<Value, Error>
let (signal1, innerObserver1) = NSignal<Int>.pipe()
let (signal2, innerObserver2) = NSignal<Int>.pipe()
let (signal3, innerObserver3) = NSignal<Int>.pipe()
Signal.merge(signal1, signal2, signal3).observeValues { (value) in
print("received value: \(value)")
}
innerObserver1.send(value: 1)
innerObserver1.sendCompleted()
innerObserver2.send(value: 2)
innerObserver2.sendCompleted()
innerObserver3.send(value: 3)
innerObserver3.sendCompleted()
输出: received value: 1
received value: 2
received value: 3
- combineLatest
把多个信号组合为一个新信号,新信号的Event是各个信号的最新的Event的组合.
"组合"意味着每个信号都至少有发送过一次Event, 并且组合的每个部分都要有值. 所以, 如果有某个信号一次都没有发送过Event, 那么这个新信号什么也不会发送, 不论其他信号发送了多少Event。
public static func combineLatest<S: Sequence>(_ signals: S) -> Signal<[Value], Error>
let (signal1, innerObserver1) = NSignal<Int>.pipe()
let (signal2, innerObserver2) = NSignal<Int>.pipe()
let (signal3, innerObserver3) = NSignal<Int>.pipe()
Signal.combineLatest(signal1, signal2, signal3).observeValues { (tuple) in
print("received value: \(tuple)")
}
innerObserver1.send(value: 1)
innerObserver2.send(value: 2)
innerObserver3.send(value: 3)
innerObserver1.send(value: 11)
innerObserver2.send(value: 22)
innerObserver3.send(value: 33)
innerObserver1.sendCompleted()
innerObserver2.sendCompleted()
innerObserver3.sendCompleted()
输出: received value: (1, 2, 3)
received value: (11, 2, 3)
received value: (11, 22, 3)
received value: (11, 22, 33)
- zip
新信号的Event是各个信号的最新的Event的进行拉链式组合,只有各个信号发送Event的次数相同(对齐)时, 新信号才会发送组合值. 同理, 如果有信号未发送那么什么也不会发生。
public static func zip<S: Sequence>(_ signals: S) -> Signal<[Value], Error>
let (signal1, innerObserver1) = NSignal<Int>.pipe()
let (signal2, innerObserver2) = NSignal<Int>.pipe()
let (signal3, innerObserver3) = NSignal<Int>.pipe()
Signal.zip(signal1, signal2, signal3).observeValues { (tuple) in
print("received value: \(tuple)")
}
innerObserver1.send(value: 1)
innerObserver2.send(value: 2)
innerObserver3.send(value: 3)
innerObserver1.send(value: 11)
innerObserver2.send(value: 22)
innerObserver3.send(value: 33)
innerObserver1.send(value: 111)
innerObserver2.send(value: 222)
innerObserver1.sendCompleted()
innerObserver2.sendCompleted()
innerObserver3.sendCompleted()
输出: received value: (1, 2, 3)
received value: (11, 22, 33)
网友评论