Adds `UIBindingObserver`.

This commit is contained in:
Krunoslav Zaher 2016-02-13 21:25:31 +01:00
parent e0555a05a5
commit 2aebe1499f
27 changed files with 281 additions and 369 deletions

View File

@ -912,6 +912,10 @@
C8FA89201C30424000CD3A17 /* VirtualTimeConverterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FA89121C30405400CD3A17 /* VirtualTimeConverterType.swift */; };
C8FA89211C30424000CD3A17 /* VirtualTimeConverterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FA89121C30405400CD3A17 /* VirtualTimeConverterType.swift */; };
C8FA89221C30424100CD3A17 /* VirtualTimeConverterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FA89121C30405400CD3A17 /* VirtualTimeConverterType.swift */; };
C8FD21AE1C67E14C00863EC3 /* UIBindingObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FD21AD1C67E14C00863EC3 /* UIBindingObserver.swift */; };
C8FD21AF1C67E14C00863EC3 /* UIBindingObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FD21AD1C67E14C00863EC3 /* UIBindingObserver.swift */; };
C8FD21B01C67E14C00863EC3 /* UIBindingObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FD21AD1C67E14C00863EC3 /* UIBindingObserver.swift */; };
C8FD21B11C67E14C00863EC3 /* UIBindingObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8FD21AD1C67E14C00863EC3 /* UIBindingObserver.swift */; };
CB255BD71BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; };
CB255BD81BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; };
CB255BD91BC46A9C00798A4C /* RetryWhen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB255BD61BC46A9C00798A4C /* RetryWhen.swift */; };
@ -1639,6 +1643,7 @@
C8FA89131C30405400CD3A17 /* VirtualTimeScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualTimeScheduler.swift; sourceTree = "<group>"; };
C8FA89161C30409900CD3A17 /* HistoricalScheduler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoricalScheduler.swift; sourceTree = "<group>"; };
C8FA891B1C30412A00CD3A17 /* HistoricalSchedulerTimeConverter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoricalSchedulerTimeConverter.swift; sourceTree = "<group>"; };
C8FD21AD1C67E14C00863EC3 /* UIBindingObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBindingObserver.swift; sourceTree = "<group>"; };
CB255BD61BC46A9C00798A4C /* RetryWhen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RetryWhen.swift; sourceTree = "<group>"; };
CB30D9E81BF0E3500084C1C0 /* SingleAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SingleAsync.swift; sourceTree = "<group>"; };
CB883B3A1BE24355000AC2EE /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
@ -2119,6 +2124,7 @@
C80DDE8C1BCE69BA006A1832 /* Driver */,
C80D33931B922FB00014629D /* ControlEvent.swift */,
C80D33941B922FB00014629D /* ControlProperty.swift */,
C8FD21AD1C67E14C00863EC3 /* UIBindingObserver.swift */,
);
path = CocoaUnits;
sourceTree = "<group>";
@ -3273,6 +3279,7 @@
C88254301B8A752B00B02D69 /* UISearchBar+Rx.swift in Sources */,
C88254181B8A752B00B02D69 /* ItemEvents.swift in Sources */,
C8DB96881BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */,
C8FD21AE1C67E14C00863EC3 /* UIBindingObserver.swift in Sources */,
C882541B1B8A752B00B02D69 /* RxTableViewDataSourceType.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -3291,6 +3298,7 @@
C8DB967F1BF7496C0084BD53 /* KVORepresentable.swift in Sources */,
C8093EFE1B8A732E0088E94D /* RxTarget.swift in Sources */,
C8093ED21B8A732E0088E94D /* _RX.m in Sources */,
C8FD21AF1C67E14C00863EC3 /* UIBindingObserver.swift in Sources */,
C8093EFC1B8A732E0088E94D /* RxCocoa.swift in Sources */,
C8DB968E1BF7595D0084BD53 /* KVORepresentable+Swift.swift in Sources */,
C80D33991B922FB00014629D /* ControlEvent.swift in Sources */,
@ -4162,6 +4170,7 @@
C8F0C03B1BBBFBB9001B112F /* UISearchBar+Rx.swift in Sources */,
C8F0C03C1BBBFBB9001B112F /* ItemEvents.swift in Sources */,
C8DB968B1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */,
C8FD21B11C67E14C00863EC3 /* UIBindingObserver.swift in Sources */,
C8F0C03D1BBBFBB9001B112F /* RxTableViewDataSourceType.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -4253,6 +4262,7 @@
D203C4FD1BB9C53700D02D00 /* RxSearchBarDelegateProxy.swift in Sources */,
D2138C8A1BB9BEBE00339B5C /* Logging.swift in Sources */,
C8DB968A1BF756F40084BD53 /* KVORepresentable+CoreGraphics.swift in Sources */,
C8FD21B01C67E14C00863EC3 /* UIBindingObserver.swift in Sources */,
D203C50F1BB9C53E00D02D00 /* UIStepper+Rx.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -0,0 +1,63 @@
//
// UIBindingObserver.swift
// Rx
//
// Created by Krunoslav Zaher on 2/7/16.
// Copyright © 2016 Krunoslav Zaher. All rights reserved.
//
import Foundation
#if !RX_NO_MODULE
import RxSwift
#endif
/**
Observer that enforces interface binding rules:
* can't bind errors (in debug builds binding of errors causes `fatalError` in release builds errors are being logged)
* ensures binding is performed on main thread
`InterfaceBindingObserver` doesn't retain target interface and in case owned interface element is released, element isn't bound.
*/
public class UIBindingObserver<UIElementType, Value where UIElementType: AnyObject> : ObserverType {
public typealias E = Value
weak var UIElement: UIElementType?
let binding: (UIElementType, Value) -> Void
/**
Initializes `ViewBindingObserver` using
*/
public init(UIElement: UIElementType, binding: (UIElementType, Value) -> Void) {
self.UIElement = UIElement
self.binding = binding
}
/**
Binds next element to owner view as described in `binding`.
*/
public func on(event: Event<Value>) {
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let element):
if let view = self.UIElement {
binding(view, element)
}
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
/**
Erases type of observer.
- returns: type erased observer.
*/
public func asObserver() -> AnyObserver<Value> {
return AnyObserver(eventHandler: on)
}
}

View File

@ -223,7 +223,7 @@ extension ObservableType {
-> Disposable {
let proxy = proxyForObject(P.self, object)
let disposable = installDelegate(proxy, delegate: dataSource, retainDelegate: retainDataSource, onProxyForObject: object)
let subscription = self.asObservable()
// source can't ever end, otherwise it will release the subscriber
.concat(Observable.never())

View File

@ -25,10 +25,13 @@ extension NSButton {
Reactive wrapper for `state` property`.
*/
public var rx_state: ControlProperty<Int> {
return rx_value(getter: { [weak self] in
return self?.state ?? 0
}, setter: { [weak self] state in
self?.state = state
})
return NSButton.rx_value(
self,
getter: { control in
return control.state
}, setter: { control, state in
control.state = state
}
)
}
}

View File

@ -43,57 +43,41 @@ extension NSControl {
return ControlEvent(events: source)
}
func rx_value<T: Equatable>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
static func rx_value<C: NSControl, T: Equatable>(control: C, getter: (C) -> T, setter: (C, T) -> Void) -> ControlProperty<T> {
MainScheduler.ensureExecutingOnScheduler()
let source = rx_lazyInstanceObservable(&rx_value_key) { () -> Observable<T> in
return Observable.create { [weak self] observer in
guard let control = self else {
let source = control.rx_lazyInstanceObservable(&rx_value_key) { () -> Observable<T> in
return Observable.create { [weak weakControl = control] (observer: AnyObserver<T>) in
guard let control = weakControl else {
observer.on(.Completed)
return NopDisposable.instance
}
observer.on(.Next(getter()))
observer.on(.Next(getter(control)))
let observer = ControlTarget(control: control) { control in
observer.on(.Next(getter()))
let observer = ControlTarget(control: control) { _ in
if let control = weakControl {
observer.on(.Next(getter(control)))
}
}
return observer
}
.distinctUntilChanged()
.takeUntil(self.rx_deallocated)
.distinctUntilChanged()
.takeUntil(control.rx_deallocated)
}
let bindingObserver = UIBindingObserver(UIElement: control, binding: setter)
return ControlProperty(values: source, valueSink: AnyObserver { event in
switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
return ControlProperty(values: source, valueSink: bindingObserver)
}
/**
Bindable sink for `enabled` property.
*/
public var rx_enabled: AnyObserver<Bool> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.enabled = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { (owner, value) in
owner.enabled = value
}.asObserver()
}
}

View File

@ -27,31 +27,21 @@ extension NSImageView {
- parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...)
*/
public func rx_imageAnimated(transitionType: String?) -> AnyObserver<NSImage?> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
if let transitionType = transitionType {
if value != nil {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = transitionType
self?.layer?.addAnimation(transition, forKey: kCATransition)
}
return UIBindingObserver(UIElement: self) { control, value in
if let transitionType = transitionType {
if value != nil {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = transitionType
control.layer?.addAnimation(transition, forKey: kCATransition)
}
else {
self?.layer?.removeAllAnimations()
}
self?.image = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
else {
control.layer?.removeAllAnimations()
}
control.image = value
}.asObserver()
}
}

View File

@ -18,11 +18,15 @@ extension NSSlider {
Reactive wrapper for `value` property.
*/
public var rx_value: ControlProperty<Double> {
return rx_value(getter: { [weak self] in
return self?.doubleValue ?? 0
}, setter: { [weak self] value in
self?.doubleValue = value
})
return NSControl.rx_value(
self,
getter: { control in
return control.doubleValue
},
setter: { control, value in
control.doubleValue = value
}
)
}
}

View File

@ -105,20 +105,12 @@ extension NSTextField {
let source = Observable.deferred { [weak self] in
delegate.textSubject.startWith(self?.stringValue ?? "")
}.takeUntil(rx_deallocated)
return ControlProperty(values: source, valueSink: AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.stringValue = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
})
let observer = UIBindingObserver(UIElement: self) { control, value in
control.stringValue = value
}
return ControlProperty(values: source, valueSink: observer.asObserver())
}
}

View File

@ -17,37 +17,17 @@ extension NSView {
Bindable sink for `hidden` property.
*/
public var rx_hidden: AnyObserver<Bool> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.hidden = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { view, value in
view.hidden = value
}.asObserver()
}
/**
Bindable sink for `alphaValue` property.
*/
public var rx_alpha: AnyObserver<CGFloat> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.alphaValue = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { view, value in
view.alphaValue = value
}.asObserver()
}
}

View File

@ -19,22 +19,13 @@ extension UIActivityIndicatorView {
Bindable sink for `startAnimating()`, `stopAnimating()` methods.
*/
public var rx_animating: AnyObserver<Bool> {
return AnyObserver {event in
MainScheduler.ensureExecutingOnScheduler()
switch (event) {
case .Next(let value):
if value {
self.startAnimating()
} else {
self.stopAnimating()
}
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
return UIBindingObserver(UIElement: self) { activityIndicator, active in
if active {
self.startAnimating()
} else {
self.stopAnimating()
}
}
}.asObserver()
}
}

View File

@ -21,19 +21,9 @@ extension UIBarButtonItem {
Bindable sink for `enabled` property.
*/
public var rx_enabled: AnyObserver<Bool> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.enabled = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { UIElement, value in
UIElement.enabled = value
}.asObserver()
}
/**

View File

@ -20,19 +20,9 @@ extension UIControl {
Bindable sink for `enabled` property.
*/
public var rx_enabled: AnyObserver<Bool> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.enabled = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { control, value in
control.enabled = value
}.asObserver()
}
/**
@ -62,39 +52,31 @@ extension UIControl {
return ControlEvent(events: source)
}
func rx_value<T: Equatable>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak self] observer in
guard let control = self else {
observer.on(.Completed)
return NopDisposable.instance
}
static func rx_value<C: UIControl, T: Equatable>(control: C, getter: (C) -> T, setter: (C, T) -> Void) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak weakControl = control] observer in
guard let control = weakControl else {
observer.on(.Completed)
return NopDisposable.instance
}
observer.on(.Next(getter()))
observer.on(.Next(getter(control)))
let controlTarget = ControlTarget(control: control, controlEvents: [.AllEditingEvents, .ValueChanged]) { control in
observer.on(.Next(getter()))
let controlTarget = ControlTarget(control: control, controlEvents: [.AllEditingEvents, .ValueChanged]) { _ in
if let control = weakControl {
observer.on(.Next(getter(control)))
}
}
return AnonymousDisposable {
controlTarget.dispose()
}
}
return AnonymousDisposable {
controlTarget.dispose()
}
}
.distinctUntilChanged()
.takeUntil(rx_deallocated)
return ControlProperty<T>(values: source, valueSink: AnyObserver { event in
MainScheduler.ensureExecutingOnScheduler()
.takeUntil(control.rx_deallocated)
switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
})
let bindingObserver = UIBindingObserver(UIElement: control, binding: setter)
return ControlProperty<T>(values: source, valueSink: bindingObserver)
}
}

View File

@ -20,11 +20,14 @@ extension UIDatePicker {
Reactive wrapper for `date` property.
*/
public var rx_date: ControlProperty<NSDate> {
return rx_value(getter: { [weak self] in
self?.date ?? NSDate()
}, setter: { [weak self] value in
self?.date = value
})
return UIControl.rx_value(
self,
getter: { datePicker in
datePicker.date
}, setter: { datePicker, value in
datePicker.date = value
}
)
}
}

View File

@ -29,31 +29,21 @@ extension UIImageView {
- parameter transitionType: Optional transition type while setting the image (kCATransitionFade, kCATransitionMoveIn, ...)
*/
public func rx_imageAnimated(transitionType: String?) -> AnyObserver<UIImage?> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
if let transitionType = transitionType {
if value != nil {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = transitionType
self?.layer.addAnimation(transition, forKey: kCATransition)
}
return UIBindingObserver(UIElement: self) { imageView, image in
if let transitionType = transitionType {
if image != nil {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = transitionType
imageView.layer.addAnimation(transition, forKey: kCATransition)
}
else {
self?.layer.removeAllAnimations()
}
self?.image = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
else {
imageView.layer.removeAllAnimations()
}
imageView.image = image
}.asObserver()
}
}

View File

@ -20,38 +20,18 @@ extension UILabel {
Bindable sink for `text` property.
*/
public var rx_text: AnyObserver<String> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { label, text in
label.text = text
}.asObserver()
}
/**
Bindable sink for `attributedText` property.
*/
public var rx_attributedText: AnyObserver<NSAttributedString?> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.attributedText = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { label, text in
label.attributedText = text
}.asObserver()
}
}

View File

@ -19,23 +19,13 @@ extension UIRefreshControl {
Bindable sink for `beginRefreshing()`, `endRefreshing()` methods.
*/
public var rx_refreshing: AnyObserver<Bool> {
return AnyObserver {event in
MainScheduler.ensureExecutingOnScheduler()
switch (event) {
case .Next(let value):
if value {
self.beginRefreshing()
} else {
self.endRefreshing()
}
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
return UIBindingObserver(UIElement: self) { refreshControl, refresh in
if refresh {
self.beginRefreshing()
} else {
self.endRefreshing()
}
}
}.asObserver()
}
}

View File

@ -39,17 +39,12 @@ extension UIScrollView {
*/
public var rx_contentOffset: ControlProperty<CGPoint> {
let proxy = proxyForObject(RxScrollViewDelegateProxy.self, self)
return ControlProperty(values: proxy.contentOffsetSubject, valueSink: AnyObserver { [weak self] event in
switch event {
case .Next(let value):
self?.contentOffset = value
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
let bindingObserver = UIBindingObserver(UIElement: self) { scrollView, contentOffset in
scrollView.contentOffset = contentOffset
}
return ControlProperty(values: proxy.contentOffsetSubject, valueSink: bindingObserver)
}
/**

View File

@ -40,17 +40,12 @@ extension UISearchBar {
}
.startWith(text)
}
let bindingObserver = UIBindingObserver(UIElement: self) { (searchBar, text: String) in
searchBar.text = text
}
return ControlProperty(values: source, valueSink: AnyObserver { [weak self] event in
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
return ControlProperty(values: source, valueSink: bindingObserver)
}
}

View File

@ -20,11 +20,14 @@ extension UISegmentedControl {
Reactive wrapper for `selectedSegmentIndex` property.
*/
public var rx_value: ControlProperty<Int> {
return rx_value(getter: { [weak self] in
self?.selectedSegmentIndex ?? 0
}, setter: { [weak self] value in
self?.selectedSegmentIndex = value
})
return UIControl.rx_value(
self,
getter: { segmentedControl in
segmentedControl.selectedSegmentIndex
}, setter: { segmentedControl, value in
segmentedControl.selectedSegmentIndex = value
}
)
}
}

View File

@ -20,11 +20,14 @@ extension UISlider {
Reactive wrapper for `value` property.
*/
public var rx_value: ControlProperty<Float> {
return rx_value(getter: { [weak self] in
self?.value ?? 0.0
}, setter: { [weak self] value in
self?.value = value
})
return UIControl.rx_value(
self,
getter: { slider in
slider.value
}, setter: { slider, value in
slider.value = value
}
)
}
}

View File

@ -20,11 +20,14 @@ extension UIStepper {
Reactive wrapper for `value` property.
*/
public var rx_value: ControlProperty<Double> {
return rx_value(getter: { [weak self] in
self?.value ?? 0
}, setter: { [weak self] value in
self?.value = value
})
return UIControl.rx_value(
self,
getter: { stepper in
stepper.value
}, setter: { stepper, value in
stepper.value = value
}
)
}
}

View File

@ -20,11 +20,14 @@ extension UISwitch {
Reactive wrapper for `on` property.
*/
public var rx_value: ControlProperty<Bool> {
return rx_value(getter: { [weak self] in
self?.on ?? false
}, setter: { [weak self] value in
self?.on = value
})
return UIControl.rx_value(
self,
getter: { uiSwitch in
uiSwitch.on
}, setter: { uiSwitch, value in
uiSwitch.on = value
}
)
}
}

View File

@ -20,11 +20,14 @@ extension UITextField {
Reactive wrapper for `text` property.
*/
public var rx_text: ControlProperty<String> {
return rx_value(getter: { [weak self] in
self?.text ?? ""
}, setter: { [weak self] value in
self?.text = value
})
return UIControl.rx_value(
self,
getter: { textField in
textField.text ?? ""
}, setter: { textField, value in
textField.text = value
}
)
}
}

View File

@ -45,17 +45,12 @@ extension UITextView {
.startWith(text)
.distinctUntilChanged()
}
let bindingObserver = UIBindingObserver(UIElement: self) { (textView, text: String) in
textView.text = text
}
return ControlProperty(values: source, valueSink: AnyObserver { [weak self] event in
switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
}
})
return ControlProperty(values: source, valueSink: bindingObserver)
}
}

View File

@ -19,38 +19,18 @@ extension UIView {
Bindable sink for `hidden` property.
*/
public var rx_hidden: AnyObserver<Bool> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.hidden = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { view, hidden in
view.hidden = hidden
}.asObserver()
}
/**
Bindable sink for `alpha` property.
*/
public var rx_alpha: AnyObserver<CGFloat> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler()
switch event {
case .Next(let value):
self?.alpha = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
return UIBindingObserver(UIElement: self) { view, alpha in
view.alpha = alpha
}.asObserver()
}
}

View File

@ -20,22 +20,15 @@ struct Colors {
extension UINavigationController {
var rx_serviceState: AnyObserver<ServiceState?> {
return AnyObserver { event in
switch event {
case .Next(let maybeServiceState):
// if nil is being bound, then don't change color, it's not perfect, but :)
if let serviceState = maybeServiceState {
let isOffline = serviceState ?? .Online == .Offline
return UIBindingObserver(owner: self) { navigationController, maybeServiceState in
// if nil is being bound, then don't change color, it's not perfect, but :)
if let serviceState = maybeServiceState {
let isOffline = serviceState ?? .Online == .Offline
self.navigationBar.backgroundColor = isOffline
? Colors.OfflineColor
: Colors.OnlineColor
}
case .Error(let error):
bindingErrorToInterface(error)
case .Completed:
break
self.navigationBar.backgroundColor = isOffline
? Colors.OfflineColor
: Colors.OnlineColor
}
}
}.asObserver()
}
}

View File

@ -21,34 +21,21 @@ extension UIImageView{
}
func rxex_downloadableImageAnimated(transitionType:String?) -> AnyObserver<DownloadableImage> {
return AnyObserver { [weak self] event in
guard let strongSelf = self else { return }
MainScheduler.ensureExecutingOnScheduler()
switch event{
case .Next(let value):
for subview in strongSelf.subviews {
subview.removeFromSuperview()
}
switch value{
case .Content(let image):
strongSelf.rx_image.onNext(image)
case .OfflinePlaceholder:
let label = UILabel(frame: strongSelf.bounds)
label.textAlignment = .Center
label.font = UIFont.systemFontOfSize(35)
label.text = "⚠️"
strongSelf.addSubview(label)
}
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
return UIBindingObserver(UIElement: self) { imageView, image in
for subview in strongSelf.subviews {
subview.removeFromSuperview()
}
}
switch value{
case .Content(let image):
strongSelf.rx_image.onNext(image)
case .OfflinePlaceholder:
let label = UILabel(frame: strongSelf.bounds)
label.textAlignment = .Center
label.font = UIFont.systemFontOfSize(35)
label.text = "⚠️"
strongSelf.addSubview(label)
}
}.asObserver()
}
}
#endif