iOS Programming · · 30 min read

Adding Animated Effects to iOS App Using UIKit Dynamics

Adding Animated Effects to iOS App Using UIKit Dynamics

In the recent tutorials, it has been underlined that iOS 7 has brought along great new features. Many new frameworks, libraries and APIs have been exposed to developers, letting them create modern and engaging applications and attract more users. One of them is the UIKit Dynamics library, integrated right into the UIKit framework, allowing the implementation of real physics in the most simple manner ever possible.

UIKit Dynamics is a brand new library shipped in iOS 7, and to those who haven’t used it or ever read about it, it might sounds hard to work with. However, the truth is totally different, and through this tutorial I’ll make an effort to present how easy it has been done to add realism to an app without having deep knowledge of physics, math and of course libraries or frameworks like Core Animation. People who have already worked with such technologies, they’ll find UIKit Dynamics a really handy tool, as it takes away all the hassle and effort that was just described.

uikit dynamics featured

Before we proceed in the details of the tutorial, let’s get to know some necessary stuff of the UIKit Dynamics, and let’s meet some of its most important classes that always play significant role when implementing it. So, let me start by saying that by being the UIKit Dynamics part of the UIKit framework, there’s no need to add any extra framework in order to use it. It’s always there, ready to be used at any time when the UIKit is imported into a project. It can be applied to any UIView object, or a subclass of it (such as UIButton or UILabel). The heart of this library is the UIDynamicAnimator class, and behind it there is hidden a physics engine, implemented and built right into the UIKit framework. It is responsible for producing the animation that will attach all the desired realistic effects to an app. However, even though the UIDynamicAnimator class is the core of the UIKit Dynamics, it doesn’t do anything on its own. Objects of some other classes must be added to it, named behaviors, or programmatically speaking, UIDynamicBehaviors. A UIKit Dynamics behavior actually represents a physical behavior of the real world in the programming world, and it specifies the realistic effects provided by UIKit Dynamics to developers. The classes related to behaviors, along with a short description, are the following:

  • UIGravityBehavior: It’s name says it all. The UIView objects that is applied to gain gravity attributes.
  • UICollisionBehavior: It provides the ability to two or more view objects to collide, or between a view object and a boundary (such as the screen bounds).
  • UIPushBehavior: It applies a force (in other words an acceleration) towards any direction to one or more view objects, and it supports two modes: The continuous where the pushed object gets its speed gradually, and the instantaneous where the object is pushed at max speed at once.
  • UIAttachmentBehavior: It is used to attach a view object to another, or a view object to a point.
  • UISnapBehavior: Using this class, a view object can animate like it is magnetized from an invisible magnet existing to a specific point. More than that, it contains a damping property, which can be used to make the object bounce around the point that it will finally snap to.

Besides the above, there is also one more important class, the UIDynamicItemBehavior. This class doesn’t attach a specific behavior to an object, but it’s used to adjust certain properties regarding other behaviors. These properties are:

  • elasticity: It’s value ranges from 0.0 to 1.0 and it specifies how elastic a collision between two objects or an object and a boundary will be.
  • density: This property represents the mass of an object. The greater its value, the bigger the object’s mass.
  • resistance: Using this property, the damping of an object’s velocity can be modified.
  • friction: With this one, the friction between two view objects can be set.
  • angularResistance: It’s used for damping the angular velocity of an object.
  • allowsRotation: This BOOL property can be used to specify if an object should be rotated or not.

I guess that it would be pointless to note that you should not worry in case all these seem too strange to you. As you’ll see along this tutorial, using the dynamic behaviors is easy enough and just a matter of a few lines of code. Anyway, at this point our brief introduction to the UIKit Dynamics has been concluded, so we can move forward to the really interesting stuff. As always, I encourage you to visit the official documentation and do an extra reading there as well in order to get a deeper understanding on this topic.

For now, come along to explore some of the new great features that the UIKit Dynamics library offers!

Demo App Overview

As always, we will implement a sample application to demonstrate the UIKit Dynamics essentials. Through it, you’ll see the basics you need to know so as to start incorporating some nice effects in your applications. So, first of all let me tell you that our app will be separated in two parts, therefore we are going to create a Tabbed Application with two tabs.

In the first half of it (or in other words in the first tab), we are going to see how UIKit Dynamics actually work using some of the classes described in the introduction. Every object in this part is going to be kind of UIView class. We’ll start by creating a simple view with round shape just like a ball, and we’ll see how gravity affects it once it get applied to it. Next, we’ll continue by trying out collisions, so we’ll watch our ball bouncing just before it finally sits at the bottom of the screen.

UIKit Dynamics - Bouncing Ball

Next, in order to make it more interesting and with goal to test more classes, we are going to add some more views that will be used as obstacles to the ball. The first one will exist at the left and upper side of the screen, the next one to the right and lower than the first one, and finally the last one will exist even lower and centered to the screen. The first two obstacle views are going to be immovable, however the third one will be free to rotate once our ball hits it. Finally, at the bottom side of the screen we’ll add one more view in order to be used as a paddle, which we’ll move only to the x-axis simply by touching it. When the paddle makes contact with the ball, it will let it bounce, just like a ping pong game. The following graphics represent all that:

UIKit Dynamics - Playball

In the second part of the app, we are going to see something completely different, much more useful, something that you’ll be able to take it and use it in your projects if you’d like. We are going to create a sidebar menu, which will be shown simply by making a swipe gesture with your finger from left to right, occupying just a portion of the screen at the left side of it, and it will be closed by dragging from the menu’s right side towards left. By using the UIKit Dynamics library, we’ll let it appear and disappear in a more realistic fashion, as you see in the following image. Nothing is going to happen when tapping on menu options, as we only care about the way the menu gets showed up and hidden.

UIKit Dynamics - Menu

Note that we won’t implement the menu as a new view controller, but we’ll add it as a subview to the second tab view controller’s view. The use of an extra view controller would make us fall into a completely different case, the so-called custom transitions, which is not my goal, but it could be the topic of another tutorial. In case you want to implement something similar to your apps, it’s up to you to either use a simple view like we’re going to do here, or a new view controller. At the end, the way the menu view will appear, remains the same.

So, after having taken a rough idea about our demo app, let’s start working on it. If you haven’t ever worked with UIKit Dynamics, I’m confident that you’ll be amazed by how easy and fast it is to use physicality in your apps.

Creating the Demo App

Let’s get started by launching Xcode and creating a new project. The familiar guide will appear that will help up set it up. At the first step, make sure to select the Tabbed Application option, in the Application category, under the iOS section as shown to the next figure:

UIKit Dynamics - Create Project

Click Next to proceed. In the second step, set the DynamicsDemo in the Product Name field, or pick any other name you would like for the demo app. Also, make sure that in the Devices drop down menu, the iPhone value is selected. Leave the rest as it is, and click Next again to be navigated to the last step of the guide.

UIKit Dynamics - Create Project

In this window, just select a place on your disk to save the project, and click on the Create button. The project is ready.

The Gravity Effect

Now that our project is ready, we can see very quickly and easily the first effect of the UIKit Dynamics, the gravity. As I already said in the app overview section, we are going to use a UIView object with a round shape, making it look like a ball, in which we’ll attach the gravity behavior.

However, before we manage to do so, some preparational steps are required to be done first. So, being on Xcode, begin by opening the Main.storyboard file and let your interface appear on the Interface Builder. Once it shows up, go to the First View Controller scene and then select and delete the pre-made contents. We want it to be totally clear.

UIKit Dynamics - Delete View Controller

Next, double click on the bar button item title at the tab bar area, and rename it from First to Ball.

UIKit Dynamics - Rename Tab

Our job in the Interface Builder is over for now. Let’s focus on the implementation, so open the FirstViewController.m file and go to the top of it. Import the next framework that will allow us to make the ball view rounded:

#import ;

Now, go right below in the private interface section and add the following object declarations:

@interface FirstViewController ()

@property (nonatomic, strong) UIDynamicAnimator *animator;

@property (nonatomic, strong) UIView *orangeBall;
@end

You see that the first object we declare is an UIDynamicAnimator object, which will be used to perform all the UIKit Dynamics animations. As I said on the beginning, it’s not going to work on its own, but don’t worry yet. We are going to add the gravity behavior to it in a while. Next, we declare a UIView object, named orangeBall that will represent the rounded view that we’ll create right after, and as you conclude from its name, it’s going to be… orange.

Now, head to the viewDidLoad method to initialize these two objects.

- (void)viewDidLoad
{
    [super viewDidLoad];
	
    // Setup the ball view.
    self.orangeBall = [[UIView alloc] initWithFrame:CGRectMake(100.0, 100.0, 50.0, 50.0)];
    self.orangeBall.backgroundColor = [UIColor orangeColor];
    self.orangeBall.layer.cornerRadius = 25.0;
    self.orangeBall.layer.borderColor = [UIColor blackColor].CGColor;
    self.orangeBall.layer.borderWidth = 0.0;
    [self.view addSubview:self.orangeBall];
    
    // Initialize the animator.
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
}

The given code above is easy enough, so I’ll just make a couple of comments. Firstly, the rounded shape of the view is achieved by this line:

self.orangeBall.layer.cornerRadius = 25.0;

Regarding the animator initialization, you see that it needs a reference view in which is going to work. In our case, this view is our default one.

Let’s keep going, and as fans of the tidy code, let’s declare a private method in which we’ll do all of our job:

@interface FirstViewController ()
...
...

-(void)demoGravity;

@end

The first important part of the tutorial is finally here. Implement this method by adding the next two lines in it:

-(void)demoGravity{
    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.orangeBall]];
    
	[self.animator addBehavior:gravityBehavior];
}

In the first line we initialize an UIGravityBehavior object, which it accepts upon the initializations a NSArray object containing all views that the gravity effect will be applied to. In our case, the only such view is the orangeBall, but if we had more views that we’d like to gain gravity, we would add them here. Anyway, you see that initializing a gravity behavior is quite simple.

The next step is necessary if we want to see it working. Further than simply creating the behavior, we must also add it to the animator, therefore we use the addBehavior method of the animator object to do so.

Ready to try this out? Go first to the viewDidLoad method and invoke this method:

- (void)viewDidLoad
{
    ...
    ...
        
    [self demoGravity];
}

Run it now to the iPhone Simulator. You should see something similar to the next one:

UIKit Dynamics - Ball Falling

Congratulations! Your first UIKit Dynamics effect is perfectly working! If we didn’t add the gravity behavior to the ball view, it would remain still at the 100, 100 point of the screen. Now, the gravity is taking it down, and even you don’t see it, the ball view continues to “fall” outside the visible area of the screen. Let me give you a tip on how you can check this.

Each behavior has an action property, which is actually a block, and in there it can be written any code we want to be executed during the dynamic animation. In other words, we can add code that will be executed as long as the ball view will fall. In practice, while still being in this method, add the following couple of lines:

gravityBehavior.action = ^{
        NSLog(@"%f", self.orangeBall.center.y);
    };

As you see, we assign a block directly to the action property of the gravity behavior. Then, with a simple NSLog command we display the center-y point of ball view to the debugger. Once you run it, you should see an increasing float number that represents the ball position. You’ll find out that it won’t stop on its own, so stop the execution through Xcode.

Adding a Collision

So, we’ve managed to create and add a gravity behavior to the orange ball view, however simply making it fall endlessly due to gravity is totally pointless. It would be much more interesting if we could make our ball bounce and finally rest when it hits the top side of the tab bar , just like a real ball on the floor.

The behavior we want to use for this purpose, is a collision behavior. What we are going to do is fairly simple. We will initialize a UICollisionBehavior object, we’ll set the boundaries we want (in our case the top side of the tab bar) and we’ll finally add it to the animator. Let’s have a look at the code first, and then we’ll make any further comments:

-(void)demoGravity{
    …
	…
    
    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.orangeBall]];
    
	[collisionBehavior addBoundaryWithIdentifier:@"tabbar"
                                       fromPoint:self.tabBarController.tabBar.frame.origin
                                         toPoint:CGPointMake(self.tabBarController.tabBar.frame.origin.x + self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.origin.y)];

    [self.animator addBehavior:collisionBehavior];
}

Once again, upon initialization we specify the orangeBall as the item that this behavior will be applied to. The addBoundaryWithIdentifier:fromPoint:toPoint: method is used to define a custom boundary, which you can imagine as an invisible line from point to point specified in the way shown above.

Here is what you get if you run it now:

UIKit Dynamics - Ball Collision

Great, we’ve created a collision behavior and our ball view doesn’t get lost in chaos, however the bouncing doesn’t say much yet. We can fix this by changing the elasticity of the collision, but how can this be done?

If you remember from the introduction, there is a class named UIDynamicItemBehavior, which can be used to alter various properties of all the other behaviors. We are going to create an object of this class, and then through it we’ll modify the elasticity value. In the demoGravity method, add the following code:

UIDynamicItemBehavior *ballBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.orangeBall]];
    ballBehavior.elasticity = 0.75;
    [self.animator addBehavior:ballBehavior];

The elasticity property gets a value ranging from 0.0 to 1.0, where 0.0 means no elasticity at all, and 1.0 means a fully elastic collision. By setting it to 0.75 we can manage a good enough elastic collision that will lead to an also good enough bouncing of the ball view. Running the app now will show something like the next one, which is definitely much better:

UIKit Dynamics - Bouncing Ball

Now that we’ve met the UIDynamicItemBehavior class and the way it works, you may try out more properties that can affect the ball movement. So, why don’t you add the to the code above the resistance or the friction properties as well and play around with their values (0.0 to 1.0)?

Sometimes it might be necessary to perform some extra actions when a collision happens, therefore the UIKit class provides us with the UICollisionBehaviourDelegate protocol, which if adopted, gives us delegate methods to handle the begin and end states of a collision between two dynamic items or an item and a boundary. I guess it would be useful to have a bit more detailed look at this, so open the FirstViewController.h file and modify the interface header line, so to adopt that protocol:

@interface FirstViewController : UIViewController 

Next, go back to the FirstViewController.m file, to the demoGravity method, and locate the following line:

[self.animator addBehavior:collisionBehavior];

Right under it, make our class the delegate of the collisionBehavior object, just like it’s shown here:

collisionBehavior.collisionDelegate = self;

To try some delegate methods out, we could make our ball view change color when the collision begins, and get its original color when it ends. Add the next really easy code fragment:

-(void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id)item withBoundaryIdentifier:(id)identifier atPoint:(CGPoint)p{
    
    self.orangeBall.backgroundColor = [UIColor blueColor];
}


-(void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id)item withBoundaryIdentifier:(id)identifier{
    
    self.orangeBall.backgroundColor = [UIColor orangeColor];
}

In the first delegate method, when the collision begins we make the ball view’s background color blue, and when it’s over we turn it to orange. If you run it now, here is what you’ll see in the Simulator (note that in the following demo the color changing might not appear correctly due to quality loss, you’d better try it to your Simulator or device):

UIKit Dynamics - Ball Falling

You see that when the ball view rests, it permanently turns to blue, meaning that the collision has not ended. That’s normal, as the ball just sits on the boundary we have set. Anyway, now that you’ve tested the UICollisionBehaviorDelegate protocol, you may comment out the color changing lines if you wish, so the orangeBall remains orange. Note that in the two delegate methods demonstrated above the example that was given was quite simple. In real applications, you might want to take advantage of the parameter values given from the methods and perform more advanced tasks. For our purpose though, what we’ve done here it’s just fine.

A More Complex Case

So far so good! We have done a great introduction to the UIKit Dynamics aspects by using the gravity behavior, the collision behavior and by modifying properties with the UIDynamicItemBehavior class. We also saw how we can use the collision delegate methods, and generally we took a nice taste of the library. However, just “dropping” a ball view from a point and watching it bounce is quite simple, so let’s go to make our example more complex by adding more views to it.

More specifically, let’s begin by adding three new views, that will work as obstacles to the ball view. The first two of them will be immovable, positioned at the left and the right side of our main view, with the second being lower than the first one. The third will be centered and even lower, but it will be able to rotate once the ball view hits on it. The next figure will make clear what we want to do:

UIKit Dynamics - Ball Obstacle

We are going to create a new method to do our new job, so go to the private interface section and declare the next one:

@interface FirstViewController ()
...
...

-(void)playWithBall;

@end

Interesting method name, don’t you thing? Let’s go to implement it, and for starters let’s create the three obstacle views and let’s add them to our default view. There is nothing difficult on that, so there you go:

-(void)playWithBall{
    UIView *obstacle1 = [[UIView alloc] initWithFrame:CGRectMake(0.0, 80.0, 120.0, 20.0)];
    obstacle1.backgroundColor = [UIColor blueColor];
    
    UIView *obstacle2 = [[UIView alloc] initWithFrame:CGRectMake(170.0, 200.0, 150.0, 20.0)];
    obstacle2.backgroundColor = [UIColor blueColor];
    
    UIView *obstacle3 = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2 - 75,
                                                                 320.0,
                                                                 150.0,
                                                                 20.0)];
    obstacle3.backgroundColor = [UIColor blackColor];
    
    
    [self.view addSubview:obstacle1];
    [self.view addSubview:obstacle2];
    [self.view addSubview:obstacle3];    
}

Next, go to the viewDidLoad method and firstly comment the demoGravity method out, and then make a call to the new one:

- (void)viewDidLoad
{
    ...
    ...    
    
    //[self demoGravity];
    [self playWithBall];
}

Run the application now if you want to see how the obstacles have been positioned. Don’t mind the ball view, you’ll just see it staying still at its original point. We’ll fix that right next.

Now, while you are in the viewDidLoad method, locate the next line that initializes the ball view:

self.orangeBall = [[UIView alloc] initWithFrame:CGRectMake(100.0, 100.0, 50.0, 50.0)];

Comment it out and add this one instead:

self.orangeBall = [[UIView alloc] initWithFrame:CGRectMake(0.0, 10.0, 50.0, 50.0)];

With that small modification, the starting point of the ball will be a little bit higher than the first obstacle at the left. Run the app if you want to see what the new initial position is.

Well, here’s the deal. Just as we previously did, we will attach a gravity behavior to the ball, so it can move towards bottom when it starts its course. However, the new clue here is that we are going to give it a little push by adding a UIDynamicPushBehavior object to the animator. The push will not take place upon the app running, but when a touch will be made to the view, so we can have greater control over the whole process. So, having said all that, let’s return to the implementation of the playWithBall method.

Initially, let’s add the gravity behavior:

UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.orangeBall]];
    [self.animator addBehavior:gravityBehavior];

These two lines are same to those we added at the first example. Now, something more interesting. We are going to create a collision behavior object and add it to the animator, but with a couple of new things this time. We’d better see the code first, then discuss:

UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.orangeBall, obstacle1, obstacle2, obstacle3]];
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [collisionBehavior addBoundaryWithIdentifier:@"tabbar"
                                       fromPoint:self.tabBarController.tabBar.frame.origin
                                         toPoint:CGPointMake(self.tabBarController.tabBar.frame.origin.x + self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.origin.y)];
    collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;
    collisionBehavior.collisionDelegate = self;
    [self.animator addBehavior:collisionBehavior];

My first comment regards the init method, where as you see besides the orangeBall object, we also specify the obstacle views as items for collision in the array we provide. Further than that, there are two new lines here. The first one…

collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;

… makes our main view’s bounds work as boundaries for the items that will collide. In our case, just the ball view and the third obstacle can “hit” the view’s bounds. Anyway, that’s necessary to be done, if we don’t want our views to get lost to the invisible area of the view.

The second line…

collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;

… dictates that all edges of items that participate to a collision will have the desired behavior.

Now that the collision behavior has been setup, let’s set some more properties regarding the ball view and the obstacles. Beginning with the ball, here are its properties using a UIDynamicItemBehavior object:

UIDynamicItemBehavior *ballBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.orangeBall]];
    ballBehavior.elasticity = 0.9;
    ballBehavior.resistance = 0.0;
    ballBehavior.friction = 0.0;
    ballBehavior.allowsRotation = NO;
    [self.animator addBehavior:ballBehavior];

Regarding the first two obstacles, we want to set the same properties to both of them. As I already have said, we want them to stay still and do not rotate or move towards any direction when they collide with the ball view. For the rotation issue, we have the allowsRotation property, which we’ll simply set to NO. To prevent them however from making any other movement, we must increase their mass, using the density property. This might seem strange to you, as it did to me, but simply setting the value 1.0 to density is not going to keep our obstacles still. Actually, we need a really great value for the density property so we can make them “extra-heavy”, otherwise we might notice some slight movements towards any direction. When you’ll try the app, feel free to play around with these values and make your own deductions. For now, just take the respective code fragment:

UIDynamicItemBehavior *obstacles1And2Behavior = [[UIDynamicItemBehavior alloc] initWithItems:@[obstacle1, obstacle2]];
    obstacles1And2Behavior.allowsRotation = NO;
    obstacles1And2Behavior.density = 100000.0;
    [self.animator addBehavior:obstacles1And2Behavior];

For the third obstacle, we want the exact opposite behavior, therefore we’ll let it rotate and of course, we won’t increase its density:

UIDynamicItemBehavior *obstacle3Behavior = [[UIDynamicItemBehavior alloc] initWithItems:@[obstacle3]];
    obstacle3Behavior.allowsRotation = YES;
    [self.animator addBehavior:obstacle3Behavior];

Guess what? We’re just a step away from running the app and watch everything in action. One last step has been left, to implement the push behavior when a touch occurs on the screen. Before we implement it, go to the private interface section and declare a private BOOL property that will indicate whether the ball is rolling or not.

@interface FirstViewController ()
…
…

@property (nonatomic) BOOL isBallRolling;

@end

Now we can implement the touchesBegan:withEvent: method:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    if (!self.isBallRolling) {

        UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.orangeBall] mode:UIPushBehaviorModeInstantaneous];
        pushBehavior.magnitude = 1.5;
        [self.animator addBehavior:pushBehavior];
        
        self.isBallRolling = YES;
    }
}

If the ball view is not yet moving, then we create a push behavior object specifying the ball as the item that will be pushed. In the mode parameter we set the UIPushBehaviorModeInstantaneous value, telling it that way that we want our ball to get its max speed from the beginning and not gradually. The magnitude property actually defines the speed of the ball view. There is also the angle property, which can be used to define the direction of the push. This behavior, as any other, is finally added to the animator.

You may now run the app. When you perform a touch on the screen, the ball is pushed to the right and its course begins. In combination with the gravity and the collision behaviors, its movement will be totally natural.

UIKit Dynamics - Ball Obstacle

Want to Play?

Our previous work can be evolved even more and turn into a kind of a pong game, if we add a paddle at the bottom side of the screen (right above the tab bar), which will be able to be moved around with our finger (or the mouse in Simulator). The code additions and modifications we must do are few, so let’s get started.

At first, go to the private interface section and declare a new UIView object that will represent the paddle. We do that because we want it to be privately visible to the class, as it’s going to be used in more than one methods. Besides that, declare a CGPoint property that will be used to store the initial paddle center point. You’ll see how this can become handy in a while.

@interface FirstViewController ()

...
...

@property (nonatomic, strong) UIView *paddle;

@property (nonatomic) CGPoint paddleCenterPoint;

@end

Now, go back to the playWithBall method to setup the paddle view and and add a set a couple of properties regarding its dynamic behavior. Let’s begin with the initialization. Right after the following commands…

[self.view addSubview:obstacle1];
[self.view addSubview:obstacle2];
[self.view addSubview:obstacle3];

… add the next code snippet:

self.paddle = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2 - 75,
                                                           self.tabBarController.tabBar.frame.origin.y - 35.0,
                                                           150.0,
                                                           30.0)];
    self.paddle.backgroundColor = [UIColor greenColor];
    self.paddle.layer.cornerRadius = 15.0;
    self.paddleCenterPoint = self.paddle.center;
    [self.view addSubview:self.paddle];

The only noteworthy here is that we store to the paddleCenterPoint property the initial paddle center point. The rest of the commands are quite easy.

Next, make a slight modification to the init method of the collision behavior, and in the array with the items that will collide add the paddle object as well:

    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.orangeBall, self.paddle, obstacle1, obstacle2, obstacle3]];

Fine. Lastly, let’s set a couple of properties that define the dynamic behavior of the paddle. Just like the first two obstacles, we wish our paddle neither to rotate nor to be moved due to collisions. Having that in mind, just right before the closing of the playWithBall method, add the next lines:

UIDynamicItemBehavior *paddleBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.paddle]];
    paddleBehavior.allowsRotation = NO;
    paddleBehavior.density = 100000.0;
    [self.animator addBehavior:paddleBehavior];

Launch the app now and see that the paddle view has been added. It’s there, however it cannot be moved yet. So, let’s see how we’ll do that.

I mentioned previously that we want our paddle to be moved using our finger, or the mouse pointer in Simulator. For that reason, we will implement the touchesMoved:withEvent: method and in there we will write all the necessary code that will make the paddle obey to our will. Furthermore, it’s more desirable the paddle to be moved only horizontally, and remain vertically steady no matter what our finger move is, so we will predict that in our implementation. Anyway, here is the code, more discussion in a while:

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];
    
    CGFloat yPoint = self.paddleCenterPoint.y;
    CGPoint paddleCenter = CGPointMake(touchLocation.x, yPoint);
    
    self.paddle.center = paddleCenter;
    [self.animator updateItemUsingCurrentState:self.paddle];

Here you can clearly see the purpose of the paddleCenterPoint property. As I said, we want the paddle to have a constant value on the y-axis, and that property is where we take if from. Moreover, the new center point of the paddle is composed by the x-point of the touch location, and the y-point of the initial paddle center. The new center is set to the paddle, so it is moved according to our commands. Finally, and that’s really important, nothing is going to happen visually without the invocation of the updateItemUsingCurrentState method of the animator. This one updates the visual state of the item been given as an argument, and make sure to always call it in your own projects when making visual updates.

Okay, the paddle is ready and it moves according to our finger’s (or pointer’s) direction, and you can test it now if you want. However, how about to give some extra push to the ball when it collides to the paddle? That would give it some extra acceleration that would eventually lead to better effects.

Previously, when we first saw the collision behavior, we also talked about the UICollisionBehaviorDelegate protocol and we implemented a couple of its delegate methods. Here, we need to implement one more such method, and in there to create a push behavior that will be added to the animator.

The following code fragment shows the delegate method that is called when two dynamic items collide (the ball view and the paddle view). Every time that a new collision begins between these two views, a new push behavior is created that accelerates the ball view even more.

-(void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id)item1 withItem:(id)item2 atPoint:(CGPoint)p{
    
    if (item1 == self.orangeBall && item2 == self.paddle) {
        UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.orangeBall] mode:UIPushBehaviorModeInstantaneous];
        pushBehavior.angle = 0.0;
        pushBehavior.magnitude = 0.75;
        [self.animator addBehavior:pushBehavior];
    }
}

The condition above is necessary so we are sure that the collision regards the ball and the paddle views.

Running the app now, you’ll see that the ball gets an extra push after each contact with the paddle.

UIKit Dynamics - Playball

An Animated Menu

All the work we previously did over the last sections gave us an opportunity to get familiarized with the UIKit Dynamic concepts, but it’s obvious that besides the teaching purpose, the bouncing ball view is not going to be useful to anyone’s project. Therefore, I think is quite important to create something that you can take with you when you finish this tutorial, something that will be both useful and reusable.

A nice case where UIKit Dynamics can provide great effects to a useful component, is when showing or hiding a menu, and especially a sidebar menu, which is exactly what we are going to do here. My goal is this: Using a UISwipeGestureRecognizer gesture from left to right, a menu that will occupy a portion of the screen will appear. Using the same gesture in the menu, swiping from right to left, the menu will close. Behind the menu view, another semi-transparent view will appear to cover any contents of the main view from accidental taps, and it will go away when the menu will hide. The next one tells everything:

UIKit Dynamics - Menu

Of course, none of the menu options is going to work, as we only care about the show/hide functionality. It might look somehow difficult to be done, but don’t worry, it’s an easily doable task as you’ll finally find out.

Before we begin working, let me make a clarification. The solution we’re going to implement here won’t use any extra view controller, but only UIView objects in the class we’re already working on, and it best suits in cases where you want to present a menu with options to a single view controller. For app-wide menus, you should better create a new view controller (both the class and in storyboard) to implement the menu, so you can access it from other various view controllers. I’m not going to do it that way, because this is actually the implementation of custom view controller transitions, and it’s totally out of my purpose. So, keep that in mind if you’re planning to do something similar to what we’ll do next to any of your projects.

So, let’s get started with all these, as we have a small journey ahead of us. The couple of steps described here regarding the Interface Builder are optional, so follow if only you want, otherwise keep reading after this part. Begin by opening the Main.storyboard file, and by going directly to the Second View Controller scene. Instead of deleting the existing contents, you may edit them and write your own messages, or just leave them as they are. The next figure shows a sample of what I wrote the the existing contents.

UIKit Dynamics - Second VC

Next, double click on the Second title, and rename it to Menu:

UIKit Dynamics - Rename tab

The optional setup on Interface Builder is over, so let’s go to code now. Open the SecondViewController.m file, and go to the private interface section to do some declarations. Before I give you the code, let me mention that the menu options are going to be listed to a table view. As you’ll see in the following code fragment, there are four declared objects: The menu view along with its background view, the table view that will present the options and the UIDynamicAnimator object of course that will handle all the animation. Also, there’s declared a private method as well, named setupMenuView, and as its name suggests, it will be used to setup our menu view (and not only). Here are the declarations:

@interface SecondViewController ()

@property (nonatomic, strong) UIView *menuView;

@property (nonatomic, strong) UIView *backgroundView;

@property (nonatomic, strong) UITableView *menuTable;

@property (nonatomic, strong) UIDynamicAnimator *animator;


-(void)setupMenuView;

@end

Also, prior to any implementation, add the next constant definition under the last #import line:

#define menuWidth 150.0

As you understand, this one defines the width of our menu. Feel free to change it if you want.

Now, let’s head directly to the implementation of the setupMenuView method, where three tasks must be performed: To setup the background view, to setup the menu view, and finally to setup the table view. Let’s see them one by one:

The background view, which initially is fully transparent:

-(void)setupMenuView{
    // Setup the background view.
    self.backgroundView = [[UIView alloc] initWithFrame:self.view.bounds];
    self.backgroundView.backgroundColor = [UIColor lightGray];
    self.backgroundView.alpha = 0.0;
    [self.view addSubview:self.backgroundView];
}

The menu view, that initially is positioned out of the visible area of the screen, at the left side:

-(void)setupMenuView{
    …
	…
    
    // Setup the menu view.
    self.menuView = [[UIView alloc] initWithFrame:CGRectMake(-menuWidth,
                                                             20.0,
                                                             menuWidth,
                                                             self.view.frame.size.height - self.tabBarController.tabBar.frame.size.height)];
    
    self.menuView.backgroundColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1.0];
    [self.view addSubview:self.menuView];

}

The table view, where you can change any of the properties I’ve set if you want:

-(void)setupMenuView{
    …
	…    
    
    // Setup the table view.
    self.menuTable = [[UITableView alloc] initWithFrame:self.menuView.bounds
                                                  style:UITableViewStylePlain];
    self.menuTable.backgroundColor = [UIColor clearColor];
    self.menuTable.separatorStyle = UITableViewCellSeparatorStyleNone;
    self.menuTable.scrollEnabled = NO;
    self.menuTable.alpha = 1.0;
    
    self.menuTable.delegate = self;
    self.menuTable.dataSource = self;
    
    [self.menuTable reloadData];
    
    [self.menuView addSubview:self.menuTable];
}

In total, this method contains enough code, but surely it’s not hard. At this point, Xcode will issue some warnings, because we’ve set our class as the delegate and datasource of the table view without having adopted the respective protocols. We’ll resolve this now, so open the SecondViewController.h file and modify the interface header line by adopting these two protocols. Here is how your interface header must look like:

@interface SecondViewController : UIViewController 

Now, let’s go back to the SecondViewController.m file, and let’s implement all the minimum required delegate and datasource table view methods. I’ll give you them all in once, as they’re pretty easy. You’ll see that the table view contains five rows, each one matching to an option, and when tapping on any of them, the selection simply gets removed from the selected row.

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 5;
}


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    
    NSString *menuOptionText = [NSString stringWithFormat:@"Option %d", indexPath.row + 1];
    cell.textLabel.text = menuOptionText;
    
    cell.textLabel.textColor = [UIColor lightGrayColor];
    cell.textLabel.font = [UIFont fontWithName:@"Futura" size:13.0];
    cell.textLabel.textAlignment = NSTextAlignmentCenter;
    
    cell.backgroundColor = [UIColor clearColor];
    
    return cell;
}


-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 50.0;
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [[tableView cellForRowAtIndexPath:indexPath] setSelected:NO];
}

Let’s move to the viewDidLoad now, and for starters, invoke the setupMenuView method:

- (void)viewDidLoad
{
    [super viewDidLoad];
   
    [self setupMenuView];
}

Next, in the same method, initialize the animator object:

self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

Let’s focus our attention now on how we can make the menu view show and hide from the main view. If we think a moment about it, you’ll see that we actually desire the same animated movement to take place when either the menu view appears on the screen or it goes away, with just one difference, the direction. That means that the dynamic behaviors that define the animation could be implemented in one method only, where depending on the desired state of the menu view, all direction-related properties could get their values on the fly.

Having that in mind, let’s declare a private method that will perform the most essential task of this example:

@interface SecondViewController ()
…
…

-(void)toggleMenu:(BOOL)shouldOpenMenu;

@end

The shouldOpenMenu parameter specifies whether the menu should open or close.

Before we implement it, let’s see for a while what kind of dynamic behaviors we are going to need for showing the menu view. First of all, we need a collision behavior in order to make it stop to an invisible boundary and not to travel from edge to edge in the screen. Next, we want a gravity behavior with direction to the right, so the menu view looks like it’s pulled by the boundary we set. These two, along with a UIDynamicItemBehavior object for setting the collision elasticity are fine, but we could also add a push behavior so our menu view moves a little bit faster. We’ll use the exact same behaviors to close the menu towards the opposite direction.

In the implementation that follows right next, at the beginning of the method you’ll see all three values that depend on the shouldOpenMenu parameter flag.

-(void)toggleMenu:(BOOL)shouldOpenMenu{
    [self.animator removeAllBehaviors];
    
    CGFloat gravityDirectionX = (shouldOpenMenu) ? 1.0 : -1.0;
    CGFloat pushMagnitude = (shouldOpenMenu) ? 20.0 : -20.0;
    CGFloat boundaryPointX = (shouldOpenMenu) ? menuWidth : -menuWidth;
    
    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.menuView]];
    gravityBehavior.gravityDirection = CGVectorMake(gravityDirectionX, 0.0);
    [self.animator addBehavior:gravityBehavior];
    
    
    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.menuView]];
    [collisionBehavior addBoundaryWithIdentifier:@"menuBoundary"
                                       fromPoint:CGPointMake(boundaryPointX, 20.0)
                                         toPoint:CGPointMake(boundaryPointX, self.tabBarController.tabBar.frame.origin.y)];
    [self.animator addBehavior:collisionBehavior];
    
    
    UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.menuView]
                                                                    mode:UIPushBehaviorModeInstantaneous];
    pushBehavior.magnitude = pushMagnitude;
    [self.animator addBehavior:pushBehavior];
    
    
    UIDynamicItemBehavior *menuViewBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.menuView]];
    menuViewBehavior.elasticity = 0.4;
    [self.animator addBehavior:menuViewBehavior];
    
    self.backgroundView.alpha = (shouldOpenMenu) ? 0.5 : 0.0;
}

Each behavior setup is simple enough, and according to what we’ve seen up to now, it’s easy to be understood by anyone. Just a couple of notes: Firstly, notice that at the beginning of the method we remove any previous behaviors from the animator. We need to do that because when we’ll perform the opposite gesture to hide the menu, new behaviors will be added to the animator and some of them (such as the gravity behavior) must exist only once in the behaviors collection of the animator. At second, the transparency of the background view is also dynamically specified depending on the menu state.

Well, now that the most important method of this part of the tutorial is ready, let’s see how it can be used. I have already said that by making swipe gesture to the right in the main view, the menu must appear. With a swipe gesture to the left in the menu view, it must disappear. The easiest and most wise thing we can do, is to invoke the same method when each one of these gestures is made, and then depending on the gesture direction to call the toggleMenu: method with the appropriate argument value. Let’s see that in action.

Start by going to the viewDidLoad method and create the two gestures we need. For the menu opening, here it is:

UISwipeGestureRecognizer *showMenuGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                                                          action:@selector(handleGesture:)];
    showMenuGesture.direction = UISwipeGestureRecognizerDirectionRight;
    [self.view addGestureRecognizer:showMenuGesture];

And for the closing:

UISwipeGestureRecognizer *hideMenuGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self
                                                                                          action:@selector(handleGesture:)];
    hideMenuGesture.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.menuView addGestureRecognizer:hideMenuGesture];

There are two differences in these two code segments: The first is the direction of the gesture. The second is the view that each gesture is added to. Notice that in the second case, the swipe gesture to the left is added to the menu view.

As you see in both selectors, a method named handleGesture: is specified, and this one we are going to declare and implement right now. Let’s begin by its declaration:

@interface SecondViewController ()
…
…

-(void)handleGesture:(UISwipeGestureRecognizer *)gesture;

@end

Then, let’s implement it. You’ll see in just a second that depending on the direction property of the gesture parameter object, we call the toggleMenu: method specifying the appropriate argument value.

-(void)handleGesture:(UISwipeGestureRecognizer *)gesture{
    if (gesture.direction == UISwipeGestureRecognizerDirectionRight) {
        [self toggleMenu:YES];
    }
    else{
        [self toggleMenu:NO];
    }
}

Easy, right?

Now you can run the app. Test the menu and feel free to play around by changing various property values. Further than that, try to create some effects on your own that you would like to see.

Summary

UIKit Dynamics is a pretty useful library that can give life to application. Taking advantage of the provided behaviors and setting the appropriate values to various properties, cool effects can be created that fit to any application ecosystem. Keep in mind that you can always make effect combinations, however be aware that sometimes you may end up with conflicts. Through this tutorial an effort was made to present the UIKit Dynamics basics and its most essential concepts. It’s now up to all of you to work on it even more, and to implement natural effects that will make your applications more attractive. I hope I gave you some good food for thought and practice, and until next time, keep playing some… ball.

For your complete reference, you can download the complete Xcode project from here.

What do you think about this tutorial? Feel free to leave us comment and share your thought.

Read next