Today a funny 😎 way to use device motion to move using the gravity the objects on your view controller, like this:
Setup
For this demo the interface is very simple: add lots of default view objects, like buttons, labels, switches, etc… 🪄
And connect everything via IBOutlets:
@IBOutlet weak var label1: UILabel!
@IBOutlet weak var label2: UILabel!
@IBOutlet weak var button1: UIButton!
@IBOutlet weak var button2: UIButton!
@IBOutlet weak var switch1: UISwitch!
@IBOutlet weak var switch2: UISwitch!
Now we need to add our code. First of all import the CoreMotion sdk:
import CoreMotion
next create the classes you need to create this effects: UIDynamicAnimator, UIGravityBehaviour and CMMotionManager.
var animator: UIDynamicAnimator!
var gravity: UIGravityBehavior!
var motion: CMMotionManager!
var queue: OperationQueue! // used for updating UI objects with motion
and initialize in viewDidLoad:
queue = OperationQueue.current
animator = UIDynamicAnimator(referenceView: self.view)
gravity = UIGravityBehavior(items: [label1, label2, button1, button2, switch1, switch2])
motion = CMMotionManager()
What happen here is that we have created a background queue, we have attached the animator the the view container, attached to the gravity object the elements you want to move and instantiate a motion manager.
The last step is to add to the animator the gravity behavior:
animator.addBehaviour(gravity)
Your code should be like this, you can build and run ▶️ on a physical device to see the effect:
override func viewDidLoad() {
super.viewDidLoad()
queue = OperationQueue.current
animator = UIDynamicAnimator(referenceView: self.view)
gravity = UIGravityBehavior(items: [label1, label2, button1, button2, switch1, switch2])
motion = CMMotionManager()
animator.addBehavior(gravity)
}
Cool, but without borders, the UI components fall outside your device… 😱😱😱
Add boundaries 🤟:
In order to avoid the falling out of objects you need to define a border and the objects that collides.
// the objects that responds to collision
let collision = UICollisionBehavior(items: [label1, label2, button1, button2, switch1, switch2])
// the boundary AKA the borders. In this case if the full ViewController view
collision.addBoundary(withIdentifier: "borders" as NSCopying, for: UIBezierPath(rect: self.view.frame))
Now you can add the CollisionBehaviour to your animation:
animator.addBehavior(collision)
Move objects using device motion 🏎:
You can use CoreMotion SDK to detect device movements and move objects around respecting always the gravity.
To do this, you can implement the startDeviceMotionUpdates of the motion manager:
motion.startDeviceMotionUpdates(to: queue) { motion, error in
guard let motion = motion else { return }
let grav: CMAcceleration = motion.gravity
let x = CGFloat(grav.x)
let y = CGFloat(grav.y)
var p = CGPoint(x: x, y: y)
if let orientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation {
if orientation == .landscapeLeft {
let t = p.x
p.x = 0 - p.y
p.y = t
} else if orientation == .landscapeRight {
let t = p.x
p.x = p.y
p.y = 0 - t
} else if orientation == .portraitUpsideDown {
p.x *= -1
p.y *= -1
}
}
let v = CGVector(dx: p.x, dy: 0 - p.y)
self.gravity.gravityDirection = v
}
🎉 Your code is now complete!
Build and run ▶️ to see the effects like the first attached video!
Too lazy for following all the steps in this tutorial? You can import directly the Swift Package into Xcode: https://github.com/elpsk/Gravity
Have fun and enjoy gravity! ❤️