UIKit Dynamics, Core Animation Layers and Autolayout Constraints

Recently, we have been busy with interesting consulting work, and have not had a lot of time to write new blog posts. We appreciate the many emails, asking for more, but we have been fighting time and finalizing projects. Now, however, one of our projects is nearly finished. So, I found a couple of hours to write this post about combining UIKit Dynamics and other UIKit APIs.

We covered UIKit Dynamics in a previous post. This new API allows developers to add real-world inspired interactions to an app’s UI. As we described in that post, the core object of this API is the dynamic animator (an object of type UIDynamicAnimator), which animates a dynamic item according to the behavior you add to it. A dynamic item is an object conforming to the UIDynamicItem protocol, and with iOS 7, UIView and UICollectionView conform to this protocol by default.

When I saw UIKit Dynamics for the first time at the WWDC 2013, I was really excited because the physics engine was one wish on my WWDC wish-list (last year, Apple almost made all my wishes real!!!). However, when I went through the documentation I found that the CALayer class does not conform to the UIDynamicItem protocol. At first, I thought it would be easy to make the CALayer conform to this protocol. But I found a problem immediately when I subclassed CALayer and added UIDynamicItem as a protocol:

This didn’t work because the UIDynamicItem protocol provides 3 properties:

The bounds and the transform properties collide against the bounds and the transform properties of CALayer defined in this way:

To solve it, I decided to take a different approach. This same approach can be used to add dynamics to other UIKit objects. Let’s build an example.

Dynamic layers

Create a single-view Xcode project and name it DynamicLayer. Let’s create a snap behavior and try to animate a layer with this behavior. In the ViewController.m, create a property for the dynamic animator:

and in the viewDidLoad create the animator and assign it to this property:

Now, let’s add a new class (subclass of NSObject) to the project. Name it DynamicHub and make it conform to UIDynamicItem protocol.

In the implementation, add the following init method.

The initialization of the _bounds instance variable (line a) is necessary, because the dynamic behavior expects the item with a size different than zero.

Let’s go back to the ViewController.m and create an instance of the DynamicHub class.

After doing this, let’s add a Core Animation layer with some bounds, position, and background color, and add it to the viewcontroller view layer to render it:

Finally, let’s create a snap behavior. To learn more about other available behaviors, and how to create your own custom behavior, take a look at our previous post on UIKit Dynamics. Also consider attending one of our training classes, where we cover this and other topics in-depth. So, after the previous chunk of code, add the following lines:

Now, notice that I am using the dynamicHub object as dynamic item for the snap behavior. During the animation, the bounds, the center and transform properties of this object gets modified by the animator once we add the behavior to the animator. But, how can we transfer this behavior onto the rendered layer? Well, remember that a dynamic behavior is a subclass of a UIDynamicBehavior class. One of its properties is the action block. During every animation frame, the animator executes this action block for each frame. You can add any functionality here, but because it is called so frequently, performance is critical. For our case, the block should look like this:

The last line of code adds the behavior to the animator and starts the animation. Lines 1, 2 and 4 are used to switch off the implicit animations of the layer. If you do not do this, every time the animator executes this block an implicit animation is generated and the final result looks strange. Line 3 is where the magic happens. For each animation frame, we are reading the center value of the dynamicHub object and updating the layer position.

Now, this example looks really easy. So, you might also ask me: can we do the same animation just using the backing layer of the view? To which, I would answer: “yes, of course!”. However, with this approach, you can animate any layer property, and more importantly, you can animate any layer type: transform layers (CATransformLayer), replicator layers (CAReplicatorLayer), text layers (`CATextLayer), and so on.

Sometime ago, we published a couple of posts about Replicator Layers and Transform Layers. Take a look at these posts. Maybe you can get some interesting ideas for new animations.

Dynamic layout constraints

Autolayout was introduced in iOS 6, but, initially, its adoption by the developer community was very poor. Lack of compatibility with iOS 5 caused many developers to simply ignored it. This changed when iOS 7 came out last year. Suddenly, developers were forced to use auto layout because of the different metrics between iOS 6 and iOS 7. Xcode 5 also simplified its usage.

We showed how to use Auto Layout in combination with Core Animation in this post.

Here, we will reveal how UIKit Dynamics can be applied to the layout constraints to generate nice animation effects. Again we will use the DynamicHub class from the previous example.

Let’s create a new single-view project and name it DynamicLayout. In the Main.storyboard file, add a 100x100 view to the viewcontroller view and change is color to orange. Center the view in the viewcontroller view and add 4 constraints to fix the view to the edges of its container (see next figure). Also add a button at the bottom of the viewcontroller view and constrain it to the view center 20 pixels from the view bottom.

In the ViewController.h, create 4 outlets for the 4 orange view constraints:

Then, connect them to the 4 constraints in Interface Builder as shown here:

Also add an outlet and an action for the button:

and connect them in Interface Builder with the button object. Let’s also add a property for the animator as we did it in the previous example:

Add the UIDynamicAnimatorDelegate as protocol of the ViewController class in the ViewController.m file:

In the viewDidLoad add these two lines of code:

In the button action, we add the code to generate the behavior animation. Before that, let’s add a dynamic animator delegate method to enable the button after the animation is completed and remove the behavior from the animator:

Now, let’s build the important functionality. The button action contains this code:

In line 1, we disable the button to avoid having the user continue to fire this method until the animation is completed. In line 2, we create our dynamicHub object as we did in the previous example with the layer. Then, we again create a snap behavior with the dynamicHub as a dynamic item and a given snap point (we will discuss this a little bit later). Finally, we build again the action block. This time, we use the center components (x and y) to change the constant property of each constraint. I decided to use the x for the horizontal constraints and the y for the vertical constraints. If you use just one of them (x or y) for the 4 constraints, you will get a slightly different animation (try that). Line 3 adds the snapBehavior to the animator to fire the animation.

The following video shows the final result.

Conclusions

As explained in Apple documentation and videos, and in various blogs around in the Internet, you can use UIKit Dynamics to animate views and collection views.

Here, however, I have shown how to use UIKit Dynamics differently. Try this approach for other objects. For example, try to animate the contentOffset of a scrollview using the gravity behavior. Or try to animate the color components of a layer using the transform property of the UIDynamicItem protocol.
The possibilities are endless.

Keep coding,

Geppy

*Geppy Parziale has 15+ years of professional experience in software design and development. He has an extensive knowledge in biometrics applications, computer vision and pattern recognition.

He is proficient in Apple-related technologies such as Cocoa and Cocoa Touch. He worked at Apple as a senior iOS and OS X engineer for computer vision and image processing applications. Some of his work is included in the latest version of iOS, OS X and Xcode.

Geppy teaches iOS development since 2008. He trained many of the Fortune 100 companies in the world. You can learn more about his training classes here: http://training.invasivecode.com.

iOS Consulting | INVASIVECODE

iOS Training | INVASIVECODE

(Visited 82 times, 1 visits today)