UIKit Dynamics Tutorial

Learn how to make your user interfaces in iOS 7 feel realistic with this UIKit Dynamics tutorial! By Colin Eberhardt.

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Collision notifications

So far you have added a few views and behaviors then let dynamics take over. In this next step you will look at how to receive notifications when items collide.

Open ViewController.m and adopt the UICollisionBehaviorDelegate protocol:

@interface ViewController () <UICollisionBehaviorDelegate>

@end

Still in viewDidLoad, set the view controller as the delegate just after the collision behavior has been instantiated, as follows:

_collision.collisionDelegate = self;

Next, add an implementation for one of the collision behavior delegate methods:

- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item 
            withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p {
    NSLog(@"Boundary contact occurred - %@", identifier);
}

This delegate method is fired off when a collision occurs and prints out a log message to the console. In order to avoid cluttering up your console log with lots of messages, feel free to remove the _collision.action logging you added in the previous section.

Build and run; your objects will interact, and you’ll see the following entries in your console:

2013-07-26 08:44:37.473 DynamicsPlayground[18104:a0b] Boundary contact occurred - barrier
2013-07-26 08:44:37.689 DynamicsPlayground[18104:a0b] Boundary contact occurred - barrier
2013-07-26 08:44:38.256 DynamicsPlayground[18104:a0b] Boundary contact occurred - (null)
2013-07-26 08:44:38.372 DynamicsPlayground[18104:a0b] Boundary contact occurred - (null)
2013-07-26 08:44:38.455 DynamicsPlayground[18104:a0b] Boundary contact occurred - (null)
2013-07-26 08:44:38.489 DynamicsPlayground[18104:a0b] Boundary contact occurred - (null)
2013-07-26 08:44:38.540 DynamicsPlayground[18104:a0b] Boundary contact occurred - (null)

From the log messages you can see that the square collides twice with the boundary identifier barrier; this is the invisible boundary you added earlier. The (null) identifier refers to the reference view boundary.

These log messages can be fascinating reading (seriously!), but it would be much more fun to provide a visual indication when the item bounces.

Below the line that sends message to the log, add the following:

UIView* view = (UIView*)item;
view.backgroundColor = [UIColor yellowColor];
[UIView animateWithDuration:0.3 animations:^{
    view.backgroundColor = [UIColor grayColor];
}];

The above code changes the background color of the colliding item to yellow, and then fades it back to gray again.

Build and run to see this effect in action:

YellowCollision

The square will flash yellow each time it hits a boundary.

So far UIKit Dynamics has automatically set the physical properties of your items (such as mass or elasticity) by calculating them based on your item’s bounds. Next up you’ll see how you can control these physical properties yourself by using the UIDynamicItemBehavior class.

Configuring item properties

Within viewDidLoad, add the following to the end of the method:

UIDynamicItemBehavior* itemBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:@[square]];
itemBehaviour.elasticity = 0.6;
[_animator addBehavior:itemBehaviour];

The above code creates an item behavior, associates it with the square, and then adds the behavior object to the animator. The elasticity property controls the bounciness of the item; a value of 1.0 represents a completely elastic collision; that is, where no energy or velocity is lost in a collision. You’ve set the elasticity of your square to 0.6, which means that the square will lose velocity with each bounce.

Build and run your app, and you’ll notice that the square now behaves in a bouncier manner, as below:

PrettyBounce

Note: If you are wondering how I produced the above image with trails that show the previous positions of the square, it was actually very easy! I simply added a block to the action property of one of the behaviors, and every fifth time the block code was executed, added a new square to the view using the current center and transform from the square.

In the above code you only changed the item’s elasticity; however, the item’s behavior class has a number of other properties that can be manipulated in code. They are as follows:

  • elasticity – determines how ‘elastic’ collisions will be, i.e. how bouncy or ‘rubbery’ the item behaves in collisions.
  • friction – determines the amount of resistance to movement when sliding along a surface.
  • density – when combined with size, this will give the overall mass of an item. The greater the mass, the harder it is to accelerate or decelerate an object.
  • resistance – determines the amount of resistance to any linear movement. This is in contrast to friction, which only applies to sliding movements.
  • angularResistance – determines the amount of resistance to any rotational movement.
  • allowsRotation – this is an interesting one that doesn’t model any real-world physics property. With this property set to NO the object will not rotate at all, regardless of any rotational forces that occur.

Adding behaviors dynamically

In its current state, your app sets up all of the behaviors of the system, then lets dynamics handle the physics of the system until all items come to rest. In this next step, you’ll see how behaviors can be added and removed dynamically.

Open ViewController.m and add the following instance variable:

BOOL _firstContact;

Add the following code to the end of the collision delegate method collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint:

if (!_firstContact)
{
    _firstContact = YES;
    
    UIView* square = [[UIView alloc] initWithFrame:CGRectMake(30, 0, 100, 100)];
    square.backgroundColor = [UIColor grayColor];
    [self.view addSubview:square];
    
    [_collision addItem:square];
    [_gravity addItem:square];
    
    UIAttachmentBehavior* attach = [[UIAttachmentBehavior alloc] initWithItem:view
                                                               attachedToItem:square];
    [_animator addBehavior:attach];
}

The above code detects the initial contact between the barrier and the square, creates a second square and adds it to the collision and gravity behaviors. In addition, you set up an attachment behavior to create the effect of attaching a pair of objects with a virtual spring.

Build and run your app; you should see a new square appear when the original square hits the barrier, as shown below:

Attachment

While there appears to be a connection between the two squares, you can’t actually see the connection as a line or spring since nothing has been drawn on the screen to represent it.

Where To Go From Here?

At this point you should have a solid understanding of the core concepts of UIKit Dynamics.

If you’re interested in learning more about UIKit Dynamics, check out our book iOS 7 By Tutorials. The book takes what you’ve learned so far and goes a step further, showing you how to apply UIKit Dynamics in an real world scenario:

SandwichFlowDynamics

The user can pull up on a recipe to take a peek at it, and when they release the recipe, it will either drop back into the stack, or dock to the top of the screen. The end result is an application with a real-world physical feel.

I hope you enjoyed this UIKit Dynamics tutorial – we think it’s pretty cool and look forward to seeing the creative ways you use it in your apps. If you have any questions or comments, please join the forum discussion below!

The full sourcecode for the Dynamics Playground you have built in this tutorial is available on github, with a commit for each ‘build and run’ step.

Colin Eberhardt

Contributors

Colin Eberhardt

Author

Over 300 content creators. Join our team.