UIKit Dynamics - Collision Detection in iOS8 with Swift

With UIKit Dynamics you can specify collision behaviours to your objects. The dynamic items can collide with each other and/or any boundary you specify. In this tutorial we will create some custom boundaries and randomly let some squares fall down on to these boundaries. This tutorial is built in iOS 8.1 and Xcode 6.1

Update: Apr 20, 2017. The rewritten version in Swift for iOS 10.3 and Xcode 8.3 is available here.

Open Xcode and create a new Single View Application. For product name, use IOS8SwiftCollisionDetectionTutorial and then fill out the Organization Name and Organization Identifier with your customary values. Enter Swift as Language and make sure only iPhone is selected in Devices.

We will draw some lines using Core Graphics so we need to create a custom UIView, so we can alter the drawRect method. Select New File -> iOS -> Source -> Cocoa Touch Class. Name the class MyView and make it a subclass of UIView. 

Go to the MyView.swift file.To draw the lines we will create a helper function drawLineFromPoint:toPoint:pointY

func drawLineFromPoint(fromX: CGFloat, toPoint toX: CGFloat, pointY y: CGFloat) {
    var currentContext = UIGraphicsGetCurrentContext()
    CGContextMoveToPoint(currentContext, fromX, y)
    CGContextAddLineToPoint(currentContext, toX, y)

The lines will be drawn with a width of 5 points. Next, we will alter the drawRect method

override func drawRect(rect: CGRect) {
      drawLineFromPoint(0, toPoint: bounds.size.width/3, pointY: bounds.size.height - 100.0)
      drawLineFromPoint(bounds.size.width/3, toPoint:bounds.size.width*0.67, pointY:bounds.size.height - 150.0)
      drawLineFromPoint(bounds.size.width*0.67, toPoint:bounds.size.width, pointY:bounds.size.height - 100.0)

We make three calls to our helper function creating three lines of the same width with different heights. We need to connect our MyView to the view controller. Go to the storyboard and select the view. Go to the Identity Inspector and change the class to MyView.

Build and Run, the Lines are drawn on the screen.

Next, drag a button to the Storyboard and give it a title of "Next". The main view should look like this.

Select the Assistant Editor and make sure the ViewController.swift is visible. Ctrl and drag from the Button to the ViewController class  and create the following Action.

In ViewController.swift we need to declare some properties to keep track of our views. Add the following properties

var squareViews:[UIView] = []
var animator:UIDynamicAnimator!
var colors:[UIColor] = []
var centerPoint:[CGPoint] = []
var sizeOfSquare:CGSize!

The squareViews propery will contain our views. We need the colors array, the centerPoint array and the sizeOfSquare property  to allocate them to our views later on. The animator property is needed to dynamically allocate our behaviours later. Next add the following properties

var leftBoundaryHeight:CGFloat!
var middleBoundaryHeight:CGFloat!
var rightBoundaryHeight:CGFloat!
var leftBoundaryWidth:CGFloat!
var middleBoundaryWidth:CGFloat!
var leftSquareCenterPointX:CGFloat!
var middleSquareCenterPointX:CGFloat!
var rightSquareCenterPointX:CGFloat!
var squareCenterPointY:CGFloat!

These properties are needed to set our custom boundaries and add a starting point for our squares. First, create a helper method setBoundaryValues to set these properties.

func setBoundaryValues() {
    leftBoundaryHeight = view.bounds.size.height - 100.0
    middleBoundaryHeight = view.bounds.size.height - 150.0
    rightBoundaryHeight = view.bounds.size.height - 100.0
    leftBoundaryWidth = view.bounds.size.width/3
    middleBoundaryWidth = view.bounds.size.width * 0.67
    leftSquareCenterPointX = view.bounds.size.width/6
    middleSquareCenterPointX = view.bounds.size.width/2
    rightSquareCenterPointX = view.bounds.size.width * 0.84
    squareCenterPointY = view.bounds.size.height - 400

In viewDidLoad, we will call our helper method and set the remainder of the square values.

override func viewDidLoad() {
    // Create the colors array
    colors = [UIColor.redColor(), UIColor.blueColor(), UIColor.greenColor(), UIColor.purpleColor(), UIColor.grayColor()]
    // Create the centerpoint of our squares
    var leftCenterPoint = CGPointMake(leftSquareCenterPointX, squareCenterPointY)
    var middleCenterPoint = CGPointMake(middleSquareCenterPointX, squareCenterPointY)
    var rightCenterPoint = CGPointMake(rightSquareCenterPointX, squareCenterPointY)
    centerPoint = [leftCenterPoint,middleCenterPoint,rightCenterPoint]
    // set the size of our squares
    sizeOfSquare = CGSizeMake(50.0, 50.0)

So, every view has a size of 50 and can be of 5 different colors. The main flow of our project happens in our releaseNextSquare IBAction method. 

var newView = UIView(frame: CGRectMake(0.0, 0.0, sizeOfSquare.width, sizeOfSquare.height))
    var randomColorIndex = Int(arc4random()%5)
    newView.backgroundColor = colors[randomColorIndex]
    var randomCenterPoint = Int(arc4random()%3)
    newView.center = centerPoint[randomCenterPoint]

The view is created and a random centerpoint and color is assigned. The new view is added to the main view and to our array. Add the remainder of the lines

animator = UIDynamicAnimator(referenceView: self.view)
// create gravity
var gravity = UIGravityBehavior(items: squareViews)
// create collision detection
var collision = UICollisionBehavior(items: self.squareViews)
// set collision boundaries
collision.addBoundaryWithIdentifier("leftBoundary", fromPoint:CGPointMake(0.0, leftBoundaryHeight), toPoint: CGPointMake(leftBoundaryWidth, leftBoundaryHeight))
collision.addBoundaryWithIdentifier("middleBoundary", fromPoint:CGPointMake(view.bounds.size.width/3, middleBoundaryHeight), toPoint: CGPointMake(middleBoundaryWidth, middleBoundaryHeight))
collision.addBoundaryWithIdentifier("rightBoundary", fromPoint:CGPointMake(middleBoundaryWidth, rightBoundaryHeight), toPoint: CGPointMake(view.bounds.size.width, rightBoundaryHeight))
collision.collisionMode = UICollisionBehaviorMode.Everything

irst, we add a gravity behavior to our animator to let our squares fall. Next, a collision behaviour is added with our custom boundaries. This behaviour is also added to our animator. The default collision mode of UICollisionBehaviour is UICollisionBehaviourMode.Everything, which means that all items and boundary can collide with each other. Build and Run, and keep pressing the Next button to let the squares fall down.

You can download the source code of the IOS8SwiftCollisionDetectionTutorial at the ioscreator repository on Github.