Introducing RxAnimated

RxCocoa is super powerful for UI bindings - you can use bind(to:) to bind pretty much any kind of observable to a UI Control of your choice. You can bind a String to a UILabel, UIImage to a UIImageView, or an array of objects to UITableView or UICollectionView.

Now, for table and collection view bindings there is a special library to allow you to bind a list of objects called RxDataSources and besides all the other goodness the library can add animations to your bindings.

And by god it makes all the difference in the world… So, I had the idea to bring the possibility to animate bindings also to other controls besides table and collection views for a while, but just recently found the time to look in more detail in implementing something re-usable.

Introducing RxAnimated

RxAnimated is a newly released library for RxSwift/RxCocoa, which allows you to:

  • add simple pre-built transitions to your bindings, like fade and flip
  • add arbitrary block-based animations to your transitions
  • extend RxAnimated to support new bindings sinks in your own classes
  • extend RxAnimated to support new your own custom animations

In this post I’m going to show how to get started with RxAnimated. In short the goal of the library is to slightly change existing binding code so that instead of instant changes it adds animations to the UI.

Non-animated binding code

To bind an observable to a label in your UI you would usually do something like this:

import RxCocoa
...
counterObservable
  .bind(to: label.rx.text)

The binding results in instant changes in the UI like so:

Animated binding with RxAnimated

To add a simple flip transition to the same binding you need to import RxAnimated and change a bit the code like so:

import RxCocoa
import RxAnimated
...
counterObservable
  .bind(animated: label.rx.animated.flip(.top, duration: 0.33).text)

I struggled to create a very idiomatic API (and had some very fruitful discussions in the RxSwift slack) so you can with as little change add animations. You basically take the existing binding sink and insert the type of transition you want:

With this change the binding triggers the transition each time it needs to produce side effects (e.g. in that case update the text of the label):

API design

The API is designed to be very flexible and I’m going to highlight just few points here:

Animations are constrained to the UI component type

The build-in animations like fade and flip are constrained to UIView so you can use them on all views. If we look at the previous example:

counterObservable
  .bind(animated: label.rx.animated.flip(.top, duration: 0.33).text)

You would add the same animation in the same way to an image view:

imagesObservable
  .bind(animated: imageView.rx.animated.flip(.top, duration: 0.33).image)

This will produce the same animation any time a new image is emitted by the observable:

The binding type or the observable type does not restrict the animation you apply.

If you do want however to create your own animation and restrict it only to UILabel and its sub-classes you can do that and the API will not make it available for bindings to any other types.

You can easily add new binding sinks via protocols

RxAnimated includes a list of bindings you can use out of the box but what if you have a custom view with its custom properties, which you bind to and want to animate?

Let’s look at the implementation of UIView.rx.animated ... isHidden:

extension AnimatedSink where Base: UIView {
    public var isHidden: Binder<Bool> {
        return Binder(self.base) { view, hidden in
            self.type.animate(view: view, binding: {
                view.isHidden = hidden
            })
        }
    }
}

AnimatedSink is the type, which adds the rx.animated namespace to UI components. You extend it and make sure to set the Base to your target type. Then you add a property for your binding sink (above that’s isHidden), which creates a new Binder instance. Inside the Binders closure parameter you call self.type.animate(...) to create an animation.

Finally inside the closure you do the UI updates you want to have animated. In the example above that’s simply setting view.isHidden to a new value but it can be anything you want - you can transform the view, update properties, etc.

You can add new custom animations in a similar fashion, just have a look at the code.

What comes out of the box?

Here’s a list of the built-in animated sinks:

UIView
  .rx.animated...isHidden
  .rx.animated...alpha

UILabel
  .rx.animated...text
  .rx.animated...attributedText

UIControl
  .rx.animated...isEnabled
  .rx.animated...isSelected
  
UIButton
  .rx.animated...title
  .rx.animated...image
  .rx.animated...backgroundImage
  
UIImageView.rx.animated...image

NSLayoutConstraint
  .rx.animated...constant
  .rx.animated...isActive

List of the built-in animations:

UIView
  .rx.animated.fade(duration: TimeInterval)
  .rx.animated.flip(FlipDirection, duration: TimeInterval)
  .rx.animated.tick(FlipDirection, duration: TimeInterval)
  .rx.animated.animation(duration: TimeInterval, animations: ()->Void)

NSLayoutConstraint
  .rx.animated.layout(duration: TimeInterval)

Why only animated bindings?

Maybe some of you will ask “What if I want to create an arbitrary animation when my observable emits a value?” Well the good news is - you can! In observe(onNext: {...}) is the place to perform any side effects your heart desires, including animations. For the moment I don’t see big benefit (or a clear way to) create some animation API, which will add value to creating animations via observe(onNext:..).

Bindings on the other hand are a useful feature, which benefits of simple transition animations and I think RxAnimated adds a lot of value there.

Where to go from here?

RxAnimated is available for free on GitHub: https://github.com/RxSwiftCommunity/RxAnimated and can be installed via CocoaPods like so pod "RxAnimated". It supports RxSwift4+ and I’d appreciate if you have any feedback regarding the library. As always you can reach me at @icanzilb.

The best way to get started is to check the code in the repo example app which demoes various types of bindings and animations:

To learn more about creating RxCocoa bindings and RxSwift in general check out the RxBook! The book is available at http://raywenderlich.com/store - this is where you can see any updates, discuss in the website forums, etc.

Hope that post was helpful, and if you want to get in touch you can find me here

Share this post:


If you'd like to learn how to create professional production apps with RxSwift, the best resource out there is the RxSwift book written by Florent Pillet, Junior Bontognali, Marin Todorov, & Scott Gardner.

It features 20+ chapters covering the basics, the Rx operators, and advanced topics like testing, error handling, and app architecture.

Available from Ray Wenderlich: » Learn more.