首页 > 代码库 > Detecting and reacting to collisions Between UI Components

Detecting and reacting to collisions Between UI Components

Problem

You want to specify collision boundaries between your UI components on the screen so that they will not overlap one another.

Solution

Instantiate an object of type UICollisionBehavior and attach it to your animator ob‐ ject. Set the translatesReferenceBoundsIntoBoundary property of your collision be‐havior to YES and ensure that your animator is initialized with your superview as its reference value. This will ensure that the subviews that are the targets of your collision behavior (as will be discussed soon) will not go outside the boundaries of your super‐ view.

Discussion

A collision behavior of type UICollisionBehavior takes in objects that conform to the UIDynamicItem protocol. All views of type UIView already conform to this protocol, so all you have to do is instantiate your views and add them to the collision behavior. A collision behavior requires you to define the boundaries that the items in the animator will not be able to go past. For instance, if you define a line that runs from the bottom- left edge to the bottom-right edge of your reference view (the bottommost horizontal line of your reference view), and add a gravity behavior to your view as well, those views will be pulled down by gravity to the bottom of the view but will not be able to go further because they will collide with the bottom edge of the view, defined by the collision behavior.

If you want your reference view’s boundaries to be considered as the boundaries of your collision detection behavior, just set the translatesReferenceBoundsIntoBoundary property of the collision behavior’s instance to YES. If you want to add custom lines as boundaries to your collision behavior, simply use the addBoundaryWithIdentifi er:fromPoint:toPoint: instance method of the UICollisionBehavior class.

In this recipe, we are going to create two colored views, one on top of the other, and then add gravity to our animator so that the views will fall down from the center of the view controller’s view. Then we are going to add a collision behavior to the mix so that the views will not overlap each other. In addition, they won’t go outside the boundaries of the reference view (the view controller’s view).

So let’s begin by defining an array of our views and our animator:

#import "ViewController.h"

 

@interface ViewController ()?

@property (nonatomic, strong) NSMutableArray *squareViews;

@property (nonatomic, strong) UIDynamicAnimator *animator;

@end

 

Then when our view appears on the screen, we will set up the collision and the gravity behaviors and add them to an animator:

-       (void)viewDidAppear:(BOOL)animated{

 [super viewDidAppear:animated];

        /* Create the views */

NSUInteger const NumberOfViews = 2;?

self.squareViews = [[NSMutableArray alloc] initWithCapacity:NumberOfViews];

   NSArray *colors = @[[UIColor redColor], [UIColor greenColor]];

 

CGPoint currentCenterPoint = self.view.center;?

CGSize eachViewSize = CGSizeMake(50.0f, 50.0f);

?for (NSUInteger counter = 0; counter < NumberOfViews; counter++){

            UIView *newView =

            [[UIView alloc] initWithFrame:

             CGRectMake(0.0f, 0.0f, eachViewSize.width, eachViewSize.height)];

            newView.backgroundColor = colors[counter];

            newView.center = currentCenterPoint;

            currentCenterPoint.y += eachViewSize.height + 10.0f;

            [self.view addSubview:newView];

            [self.squareViews addObject:newView];

        }

        self.animator = [[UIDynamicAnimator alloc]

                         initWithReferenceView:self.view];

        /* Create gravity */

 UIGravityBehavior *gravity = [[UIGravityBehavior alloc]

initWithItems:self.squareViews];

 

        [self.animator addBehavior:gravity];

 

        /* Create collision detection */

        UICollisionBehavior *collision = [[UICollisionBehavior alloc]

                                          initWithItems:self.squareViews];

 

        collision.translatesReferenceBoundsIntoBoundary = YES;

 

        [self.animator addBehavior:collision];

}