-
Notifications
You must be signed in to change notification settings - Fork 472
Using UIKitDynamics
UIKit Dynamics was introduced in iOS 7 to introduce a new level of realism to the UI. It's the same physics engine that powers SpriteKit except that it's set up to work with UIViews.
The UIDynamicAnimator is the brains behind the dynamics in the view. There is one instance of UIDynamicAnimator for all the dynamic views, it is the bridge between UIKit and the physics engine, and contains all the "behaviors" such as gravity and collision detection.
The UIDynamicAnimator is instantiated with a reference view, which is usually the root view of the UIViewController, i.e., self.view
.
var animator: UIDynamicAnimator!
override func viewDidLoad() {
super.viewDidLoad()
animator = UIDynamicAnimator(referenceView: self.view)
}
You can create nearly any effect by using one or more of the available behaviors: gravity, push, snap, attachment, and collision. After creating the behavior, add it to the animator to enable it.
Since you may want to add or remove items from gravity dynamically, you should create the gravity behavior as a property.
var gravity: UIGravityBehavior!
In viewDidLoad
, create the gravity behavior and add it to the animator.
override func viewDidLoad() {
super.viewDidLoad()
gravity = UIGravityBehavior()
animator.addBehavior(gravity)
}
At any time, you can add or remove views to the gravity behavior based on user input or some other condition.
gravity.addItem(self.greenView)
gravity.removeItem(self.blueView)
You can change the direction and force of gravity by modifying the gravityDirection
property.
// Gravity still goes down, but at a reduced force
self.gravity.gravityDirection = CGVector(dx: 0, dy: 0.1)
// Gravity goes up
self.gravity.gravityDirection = CGVector(dx: 0, dy: -1)
// Gravity goes diagonal
self.gravity.gravityDirection = CGVector(dx: 1, dy: 1)
Since you may want to add or remove items from collision dynamically, you should create the collision behavior as a property.
var collision: UICollisionBehavior!
In viewDidLoad
, create the collision behavior and add it to the animator.
override func viewDidLoad() {
super.viewDidLoad()
collision = UICollisionBehavior()
animator.addBehavior(collision)
}
At any time, you can add or remove views to the collision behavior based on user input or some other condition.
collision.addItem(self.greenView)
collision.removeItem(self.blueView)
You can add arbitrary boundaries for objects to collide with. Choose an identifier, so that when a collision event happens, you can check which boundary an object collided with.
collision.addBoundary(withIdentifier: "shelf" as NSCopying, from: CGPoint(x: 0, y: 200), to: CGPoint(x: 150, y: 240))
You can set the bounds of the view to automatically be boundaries by setting the translatesReferenceBoundsIntoBoundaries
property.
collision.translatesReferenceBoundsIntoBoundary = true
In order to detect collisions, you have to first set the collisionDelegate.
collision.collisionDelegate = self
Then, in your view controller, declare that you implement the UICollisionBehaviorDelegate protocol.
class ViewController: UIViewController, UICollisionBehaviorDelegate {
...
}
If you added the method below previously, be sure to comment it out.
collision.translatesReferenceBoundsIntoBoundary = true
var boundary
won't know what the identifier is for that collision.
Implement one of the appropriate collision events, e.g., collision between two objects or collision between an object and a boundary.
For example, to detect the collision of an object with a boundary, implement the following method:
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) {
// You have to convert the identifier to a string
let boundary = identifier as! String
// The view that collided with the boundary has to be converted to a view
let view = item as! UIView
if boundary == "shelf" {
// Detected collision with a boundary called "shelf"
} else if (boundary == "bottom") {
// Detected collision with bounds of reference view
}
}
To detect the collision of an object with another object, implement the following method:
func collisionBehavior(behavior: UICollisionBehavior, beganContactForItem item1: UIDynamicItem, withItem item2: UIDynamicItem, atPoint p: CGPoint) {
var view1 = item1 as UIView
var view2 = item2 as UIView
}
Since you may want to add or remove items from push dynamically, you should create the push behavior as a property.
var push: UIPushBehavior!
In viewDidLoad
, create the push behavior and add it to the animator.
override func viewDidLoad() {
super.viewDidLoad()
push = UIPushBehavior()
animator.addBehavior(push)
}
At any time, you can add or remove views to the push behavior based on user input or some other condition.
push.addItem(self.greenView)
push.removeItem(self.blueView)
You can change the direction and force of the push force by modifying the pushDirection
property.
// Push goes down, but 1/10 the force of gravity
push.pushDirection = CGVectorMake(0,0.1)
// Push goes up
push.pushDirection = CGVectorMake(0,-1)
// Push goes diagonal
push.pushDirection = CGVectorMake(1,1)