Introduction
Apple has always strived to introduce classes that can add some practicality to the user interfaces so that working on them isn't a pain-inducing exercise for the user. UIKit Dynamics is one of those tools that fuels them to achieve this objective, and in a manner most perfect.
Before the advent of the tools of the likes of UIKit Dynamics, it was a full of fuss job to accomplish this smooth interface operationability. With the UIKit Dynamics, you can add far more realism to the views in respect to their animated representations, which manages to be extremely close to how a tangible object will behave when subjected to stress and momentum.
Now, there are different classes that affect the behavior of design elements. We will explain each with elaborate examples.
UIGravityBehavior
UIGravityBehavior
assigns the gravitational behavior to the views. It acts much like the real world objects which fall to the ground when there is an absence of force pulling them in any other direction.
We first have to initialize the view that the class will act upon, and this will be done in the GravityAndCollisionViewController
class:
var squareView: UIView!
We then add various methods like viewDidLoad()
, the instance of UICollisionBehavior
class and so on to bring the gravity into the effect.
The complete code for GravityAndCollisionViewController looks like this:
import UIKit
class GravityBehaviourViewController: UIViewController {
var boxView: UIView!
var gravityBehaviour : UIGravityBehavior!
var dynAnimationBehaviour: UIDynamicAnimator!
var collision: UICollisionBehavior!
var dynBehaviour: UIDynamicItemBehavior!
override func viewDidLoad() {
super.viewDidLoad()
boxView = UIView(frame: CGRect
(x: UIScreen.mainScreen().bounds.size.width/2-75, y: 50, width: 150, height: 150))
boxView.backgroundColor = UIColor.redColor()
view.addSubview(boxView)
dynAnimationBehaviour = UIDynamicAnimator(referenceView: view)
gravityBehaviour = UIGravityBehavior(items: [boxView])
dynAnimationBehaviour.addBehavior(gravityBehaviour )
collision = UICollisionBehavior(items: [boxView])
collision.translatesReferenceBoundsIntoBoundary = true
dynAnimationBehaviour.addBehavior(collision)
dynBehaviour = UIDynamicItemBehavior(items: [boxView])
dynBehaviour.elasticity = 0.7
dynAnimationBehaviour.addBehavior(dynBehaviour)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Snap
As the name suggests, this feature facilitates snapping the view to a desirable spot on the screen. What makes it good is that it is characterized by the elasticity that is much like in a real world. The UISnapBehavior
class is used here.
You can find the Snap view controller at the Main.storyboard file. The Tap Gesture Recognizer is then needed to be dragged to view controller. Then, we move on to defining actions and naming them, before proceeding any further.
Here is how we will write the SnapViewController.swift file:
import UIKit
class SnapBehaviourViewController: UIViewController {
var boxView: UIView!
var snapBehaviour : UISnapBehavior!
var dynAnimator: UIDynamicAnimator!
@IBAction func handleTap(sender: UITapGestureRecognizer) {
let tapPoint: CGPoint = sender.locationInView(view)
if (snapBehaviour != nil) {
dynAnimator.removeBehavior(snapBehaviour)
}
snapBehaviour = UISnapBehavior(item: boxView, snapToPoint: tapPoint)
dynAnimator.addBehavior(snapBehaviour)
}
override func viewDidLoad() {
super.viewDidLoad()
boxView = UIView(frame: CGRect(x: 100, y: 100, width: 150, height: 150))
boxView.backgroundColor = UIColor.orangeColor()
view.addSubview(boxView)
dynAnimator = UIDynamicAnimator(referenceView: view)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The UISnapBehavior
is called instantly as and when the user touches the screen.
Attachment
Attach feature is leveraged to establish a connection between any two separate items, or for that matter, an item and a spot on screen. Let us elaborate it further by using the UIAttachmentBehavior
class to attach the view to a point.
The Attach view controller can be found in the Main.storyboard file. We would then need a pan gesture recognizer in the view controller. And then, we can proceed with the rest of the steps like defining and assigning actions.
The code for AttachviewController.swift will look like:
import UIKit
class AttachmentBehaviourViewController: UIViewController {
var boxView: UIView!
var referenceView: UIView!
var attachment: UIAttachmentBehavior!
var dynAnimationBehaviour: UIDynamicAnimator!
var gravityBehaviour: UIGravityBehavior!
@IBAction func handlePan(sender: UIPanGestureRecognizer) {
attachment.anchorPoint = sender.locationInView(view)
referenceView.center = sender.locationInView(view)
}
override func viewDidLoad() {
super.viewDidLoad()
boxView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
boxView.backgroundColor = UIColor.greenColor()
view.addSubview(boxView)
referenceView = UIView
(frame: CGRect(x: view.center.x, y: view.center.y, width: 30, height: 30))
referenceView.backgroundColor = UIColor.blackColor()
view.addSubview(referenceView)
attachment = UIAttachmentBehavior(item: boxView,
attachedToAnchor: CGPointMake(referenceView.center.x, referenceView.center.y))
dynAnimationBehaviour = UIDynamicAnimator(referenceView: view)
dynAnimationBehaviour.addBehavior(attachment)
gravityBehaviour = UIGravityBehavior(items: [boxView])
dynAnimationBehaviour.addBehavior(gravityBehaviour)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Paragraph 4: Push Behaviour
import UIKit
class PushBehaviourViewController: UIViewController {
var greyBoxView: UIView!
var yelloBoxView: UIView!
var dynAnimator: UIDynamicAnimator!
override func viewDidLoad() {
super.viewDidLoad()
greyBoxView = UIView(frame: CGRect(x: 100, y: 100, width: 70, height: 70))
greyBoxView.backgroundColor = UIColor.grayColor()
view.addSubview(greyBoxView)
yelloBoxView = UIView(frame: CGRect(x: 220, y: 100, width: 50, height: 50))
yelloBoxView.backgroundColor = UIColor.yellowColor()
view.addSubview(yelloBoxView)
let geryBoxPushBehaviour: UIPushBehavior = UIPushBehavior
(items: [greyBoxView], mode: UIPushBehaviorMode.Continuous)
let yellowBoxPushBehaviour: UIPushBehavior = UIPushBehavior
(items: [yelloBoxView], mode: UIPushBehaviorMode.Instantaneous)
geryBoxPushBehaviour.setAngle( CGFloat(M_PI_2) , magnitude: 0.5);
yellowBoxPushBehaviour.setAngle( CGFloat(M_PI_2) , magnitude: 0.5);
dynAnimator = UIDynamicAnimator(referenceView: view)
dynAnimator.addBehavior(geryBoxPushBehaviour)
dynAnimator.addBehavior(yellowBoxPushBehaviour)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Now, in the code written above, we have created more than one view and the UIAttachmentBehavior
is instantiated with the squareView
. Now, one of the boxes is falling down because of gravity, but the other box keeps it from falling to the ground because of the attachment property.
Push
Now, when it comes to the UIPushBehavior
, there are two modes, namely, continuous push behavior and instantaneous push behavior. The former executes a push behavior that keeps running till the specified duration is completed. In the latter case, there is just a single instance of push.
With continuous push, as there is incessant energy applied, the view will accelerate.
Let's see a code for applying different pushes on two views:
import UIKit
class PushBehaviourViewController: UIViewController {
var greyBoxView: UIView!
var yelloBoxView: UIView!
var dynAnimator: UIDynamicAnimator!
override func viewDidLoad() {
super.viewDidLoad()
greyBoxView = UIView(frame: CGRect(x: 100, y: 100, width: 70, height: 70))
greyBoxView.backgroundColor = UIColor.grayColor()
view.addSubview(greyBoxView)
yelloBoxView = UIView(frame: CGRect(x: 220, y: 100, width: 50, height: 50))
yelloBoxView.backgroundColor = UIColor.yellowColor()
view.addSubview(yelloBoxView)
let geryBoxPushBehaviour: UIPushBehavior = UIPushBehavior
(items: [greyBoxView], mode: UIPushBehaviorMode.Continuous)
let yellowBoxPushBehaviour: UIPushBehavior = UIPushBehavior
(items: [yelloBoxView], mode: UIPushBehaviorMode.Instantaneous)
geryBoxPushBehaviour.setAngle( CGFloat(M_PI_2) , magnitude: 0.5);
yellowBoxPushBehaviour.setAngle( CGFloat(M_PI_2) , magnitude: 0.5);
dynAnimator = UIDynamicAnimator(referenceView: view)
dynAnimator.addBehavior(geryBoxPushBehaviour)
dynAnimator.addBehavior(yellowBoxPushBehaviour)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
We have predefined the magnitude as well as the angles to dictate the direction in which the push is working. As a result of the code values, we can see two boxes starting at just about the same point, but one of them moving at a higher speed.
Let's See an Example that Combines Multiple Behaviors
Now that we have elaboratively discussed how each behavior works, let us get down to combining the different behaviors on views by citing an example in the shape of a code:
import UIKit
class ExampleApplicationViewController: UIViewController {
var overlayView: UIView!
var alertView: UIView!
var dynAnimationBehaviour: UIDynamicAnimator!
var attachmentBehavior : UIAttachmentBehavior!
var snapBehavior : UISnapBehavior!
override func viewDidLoad() {
super.viewDidLoad()
dynAnimationBehaviour = UIDynamicAnimator(referenceView: view)
createOverlay()
createAlert()
}
func createOverlay() {
overlayView = UIView(frame: view.bounds)
overlayView.backgroundColor = UIColor.blackColor()
overlayView.alpha = 0.0
view.addSubview(overlayView)
}
func createAlert() {
let alertWidth: CGFloat = 250
let alertHeight: CGFloat = 150
let alertViewFrame: CGRect = CGRectMake(0, 0, alertWidth, alertHeight)
alertView = UIView(frame: alertViewFrame)
alertView.backgroundColor = UIColor.yellowColor()
alertView.alpha = 0.0
alertView.layer.cornerRadius = 10;
alertView.layer.shadowColor = UIColor.blackColor().CGColor;
alertView.layer.shadowOffset = CGSizeMake(0, 5);
alertView.layer.shadowOpacity = 0.3;
alertView.layer.shadowRadius = 10.0;
let button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.setTitle("Dismiss", forState: UIControlState.Normal)
button.backgroundColor = UIColor.whiteColor()
button.frame = CGRectMake(0, 0, alertWidth, 40.0)
button.addTarget(self, action: Selector("dismissAlert"),
forControlEvents: UIControlEvents.TouchUpInside)
alertView.addSubview(button)
view.addSubview(alertView)
}
func showAlert() {
if (alertView == nil) {
createAlert()
}
createGestureRecognizer()
dynAnimationBehaviour.removeAllBehaviors()
UIView.animateWithDuration(0.4) {
self.overlayView.alpha = 1.0
}
alertView.alpha = 1.0
var snapBehaviour: UISnapBehavior =
UISnapBehavior(item: alertView, snapToPoint: view.center)
dynAnimationBehaviour.addBehavior(snapBehaviour)
}
func dismissAlert() {
dynAnimationBehaviour.removeAllBehaviors()
var gravityBehaviour: UIGravityBehavior = UIGravityBehavior(items: [alertView])
gravityBehaviour.gravityDirection = CGVectorMake(0.0, 10.0);
dynAnimationBehaviour.addBehavior(gravityBehaviour)
var itemBehaviour: UIDynamicItemBehavior = UIDynamicItemBehavior(items: [alertView])
itemBehaviour.addAngularVelocity(CGFloat(-M_PI_2), forItem: alertView)
dynAnimationBehaviour.addBehavior(itemBehaviour)
UIView.animateWithDuration(0.4, animations: {
self.overlayView.alpha = 0.0
}, completion: {
(value: Bool) in
self.alertView.removeFromSuperview()
self.alertView = nil
})
}
@IBAction func showAlertView(sender: UIButton) {
showAlert()
}
func createGestureRecognizer() {
let panGestureRecognizer: UIPanGestureRecognizer =
UIPanGestureRecognizer(target: self, action: Selector("handlePan:"))
view.addGestureRecognizer(panGestureRecognizer)
}
func handlePan(sender: UIPanGestureRecognizer) {
if (alertView != nil) {
let panLocationInView = sender.locationInView(view)
let panLocationInAlertView = sender.locationInView(alertView)
if sender.state == UIGestureRecognizerState.Began {
dynAnimationBehaviour.removeAllBehaviors()
let offset = UIOffsetMake(panLocationInAlertView.x -
CGRectGetMidX(alertView.bounds), panLocationInAlertView.y -
CGRectGetMidY(alertView.bounds));
attachmentBehavior = UIAttachmentBehavior
(item: alertView, offsetFromCenter: offset, attachedToAnchor: panLocationInView)
dynAnimationBehaviour.addBehavior(attachmentBehavior)
}
else if sender.state == UIGestureRecognizerState.Changed {
attachmentBehavior.anchorPoint = panLocationInView
}
else if sender.state == UIGestureRecognizerState.Ended {
dynAnimationBehaviour.removeAllBehaviors()
snapBehavior = UISnapBehavior(item: alertView, snapToPoint: view.center)
dynAnimationBehaviour.addBehavior(snapBehavior)
if sender.translationInView(view).y > 100 {
dismissAlert()
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
We thus explain how the UIKit Dynamics suite of functions can be leveraged to create views with properties akin to real life. There are endless ways you can implement the properties to the apps and gaining command over them would serve you well in the long run.