Created
May 18, 2019 18:50
-
-
Save myurieff/f137ed609b2c822ac61a1bc079183344 to your computer and use it in GitHub Desktop.
Simple animatable bindings using RxSwift + RxCocoa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Our binder will work universally for every UIView subclass. | |
extension Reactive where Base: UIView { | |
/// Provides an observer that will animate incoming values. | |
/// | |
/// - Parameters: | |
/// - keyPath: keyPath to the property that will be mutated and animated. | |
/// - animator: the animator instance that will be used to animate changes. | |
/// - Returns: a Binder instance. | |
public func animated<Value>( | |
// using keyPaths will allow us to have a single function that will work | |
// on every property of the Base class. | |
// Otherwise we would have to write one for every property that we might | |
// want to animate later on (alpha, frame and so on). | |
// Of course, it is your responsibility to use it only with animatable properties. | |
_ keyPath: ReferenceWritableKeyPath<Base, Value>, | |
// I am a big fan of UIViewPropertyAnimator and I think it works wonderfully | |
// for the current use case. Alternatively, we can have an UIView.AnimationOptions | |
// parameter here or some other sort of a configuration that will later on be used | |
// for performing the animation. | |
with animator: UIViewPropertyAnimator | |
) -> Binder<Value> { | |
return Binder(self.base) { view, newValue in | |
// This is what essentially your ordinary Binder looks like. | |
// Here we're just setting the newValue inside an animation closure. | |
animator.addAnimations { | |
view[keyPath: keyPath] = newValue | |
} | |
// And then triggering the animator. | |
animator.startAnimation() | |
} | |
} | |
} | |
extension UIViewPropertyAnimator { | |
// If you use the same animation style all over the app, you can save yourself some code | |
// by making a static property that represents that animation. | |
// As an example, I like fading out / in views with a short, ease both curve animation, | |
// as it looks smoother than doing it instantly, and also is short enough to not be | |
// distracting or annoying when it happens all the time. | |
static let short = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut, animations: nil) | |
} | |
// Using all of this is super clean and simple. | |
// Instead of the usual | |
itemsCount.map { ($0 == 0) ? 0.4 : 1.0 }.drive(itemBadge.rx.alpha).disposed(by: bag) | |
// we now use our new binder function | |
itemsCount.map { ($0 == 0) ? 0.4 : 1.0 }.drive(itemBadge.rx.animated(\.alpha, with: .short)).disposed(by: bag) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment