RxAnimated provides animation interface to RxCocoa's bindings.
It comes with few predefined animation bindings, and provides a flexible mechanism for you to add your own predefined animations and use them when binding with RxCocoa.
When binding values with RxCocoa you write something like:
textObservable
.bind(to: labelFlip.rx.text)
This updates the label's text each time the observable emits a new string value. But this happens abruptly and without any transition. With RxAnimated you can use the animated
extension to bind values with animations, like so:
textObservable
.bind(animated: labelFlip.rx.animated.flip(.top, duration: 0.33).text)
The "difference" is that you use bind(animated:)
instead of bind(to:)
and you insert animated.flip(.top, duration: 0.33)
(or one of the other provided or custom animation methods) between rx
and the property sink you want to use, e.g. text
in the example above.
The same built-in fade and flip animations work on any UIView
element. And also on specific properties like UILabel.rx.text
or UIImageView.rx.image
.
List of built-in animated sinks:
UIView.rx.animated...isHidden
UIView.rx.animated...alpha
UILabel.rx.animated...text
UILabel.rx.animated...attributedText
UIControl.rx.animated...isEnabled
UIControl.rx.animated...isSelected
UIButton.rx.animated...title
UIButton.rx.animated...image
UIButton.rx.animated...backgroundImage
UIImageView.rx.animated...image
NSLayoutConstraint.rx.animated...constant
NSLayoutConstraint.rx.animated...isActive
List of the built-in animations:
UIView.rx.animated.fade(duration: TimeInterval)
UIView.rx.animated.flip(FlipDirection, duration: TimeInterval)
UIView.rx.animated.tick(FlipDirection, duration: TimeInterval)
UIView.rx.animated.animation(duration: TimeInterval, animations: ()->Void)
NSLayoutConstraint.rx.animated.layout(duration: TimeInterval)
Check the demo app for a number of examples.
You can easily add your custom bind animations to match the visual style of your app.
I. (Optional) If you are animating a new binding sink that has no animated binding (e.g. UIImageView.rx.image
, UILabel.rx.text
and more are already included but you need another property)
// This is your class `UILabel`
extension AnimatedSink where Base: UILabel {
// This is your property name `text` and value type `String`
public var text: Binder<String> {
let animation = self.type!
return Binder(self.base) { label, text in
animation.animate(view: label, block: {
guard let label = label as? UILabel else { return }
// Here you update the property
label.text = text
})
}
}
}
II. Add your new animation method:
// This is your class `UIView`
extension AnimatedSink where Base: UIView {
// This is your animation name `tick`
public func tick(_ direction: FlipDirection = .right, duration: TimeInterval) -> AnimatedSink<Base> {
// use one of the animation types and provide `setup` and `animation` blocks
let type = AnimationType<Base>(type: RxAnimationType.spring(damping: 0.33, velocity: 0), duration: duration, setup: { view in
view.alpha = 0
view.transform = CGAffineTransform(rotationAngle: direction == .right ? -0.3 : 0.3)
}, animations: { view in
view.alpha = 1
view.transform = CGAffineTransform.identity
})
//return AnimatedSink
return AnimatedSink<Base>(base: self.base, type: type)
}
}
III. Now you can use your new animation to bind subscriptions. Here's how usually binding UIImageView.rx.image
looks like:
imageObservable
.bind(to: imageView.rx.image)
And the result is non-animated binding:
If you use your new custom animation binding like so:
imageObservable
.bind(to: imageView.rx.animated.tick(.right, duration: 0.33).image)
The effect will be:
And if you use the same animation on a UILabel
:
textObservable
.bind(to: labelCustom.rx.animated.tick(.left, duration: 0.75).text)
The sky is the limit! (And good taste.)
The demo app shows few animations in action, download the repo and give it a try.
RxAnimated depends on RxSwift 5+.
RxAnimated is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "RxAnimated"
RxAnimated is available under the MIT license. See the LICENSE file for more info.