iOS 9 Swift Programming Cookbook (2015)
Chapter 13. UI Dynamics
13.6 Designing a Velocity Field on Your UI
Problem
You want to apply force, following a vector, onto your UI components.
Solution
Follow these steps:
1. Create an animator of type UIDynamicAnimator (see Recipe 13.1).
2. Create your collision detector of type UICollisionBehavior.
3. It’s best to also have gravity or other forces applied to your field (see Recipe 13.1 and Recipe 13.2).
4. Create your velocity of type UIFieldBehavior using this class’s velocityFieldWithVector(_:) method and supplying a vector of type CGVector.
5. Set the position property of your velocity field to an appropriate point on your reference view.
6. Set the region property of your velocity to an appropriate region (of type UIRegion) of your reference view.
7. Once done, add your behaviors to your animator.
NOTE
I recommend that you have a look at Recipe 13.1 where I described most of the basics of setting up a scene with gravity and an animator. I won’t go into those in detail again.
In this recipe, I am also going to use a few extensions that we coded in Recipe 13.5.
Discussion
A velocity field applies a force toward a given direction to dynamic items, such as UIView instances. In this recipe, I am going to design a field that looks like our field in Recipe 13.5. On top of that, I am going to apply a slight upward and leftbound force that is positioned smack dab in the center of the screen. I am also going to position an orange view on my main storyboard and have all the forces applied to this little poor guy. I will then place the orange view on top of the reference view so that when I run the app, a few things will happen:
1. The southeast-bound gravity will pull the orange view to the bottom right of the screen.
2. The orange view will keep falling down until it hits the northwest bound velocity field, at which point the orange view will get uncomfortable and move up and left a bit a few times, and keep falling until it gets out of the velocity field.
3. The orange view will then eventually settle at the bottom right of the view.
I now need you to set up your gravity, animator, and collision detector just as you did in Recipe 13.2 so that I don’t have to repeat that code. Then set up the velocity field:
lazy var velocity: UIFieldBehavior = {
let vector = CGVector(dx: -0.4, dy: -0.5)
let velocity = UIFieldBehavior.velocityFieldWithVector(vector)
velocity.position = self.view.center
velocity.region = UIRegion(radius: 100.0)
velocity.addItem(self.orangeView)
return velocity
}()
Then batch up all your forces into one variable that you can give to our animator, using the extension we wrote in Recipe 13.5:
var behaviors: [UIDynamicBehavior]{
return [self.collision, self.gravity, self.velocity]
}
override func viewDidLoad() {
super.viewDidLoad()
animator.addBehaviors(behaviors)
}
And when the user starts panning your orange view around, stop all the forces, then restart them when she is done dragging:
@IBAction func panning(sender: UIPanGestureRecognizer) {
switch sender.state{
case .Began:
collision.removeItem(orangeView)
gravity.removeItem(orangeView)
velocity.removeItem(orangeView)
case .Changed:
orangeView.center = sender.locationInView(view)
case .Ended, .Cancelled:
collision.addItem(orangeView)
gravity.addItem(orangeView)
velocity.addItem(orangeView)
default: ()
}
}
See Also