重力学这个这个名词不论在哪个行业领域听起来似乎都很高大上。那么在Swift中的重力学是什么呢?那就是将我们移动端屏幕上毫无生命力的东西也置于万有引力中,使它们能够展现出好像真的由于引力而向下坠落以及碰到物体后自然的弹开的效果。
要想做到这一点,我们得需要两个利器: UIKit Dynamics 和 Motion Effects。
1. UIKit Dynamics是UIKit中一套完整的物理引擎。它可以让我们在程序中对界面元素添加一些行为从而达到诸如重力、弹簧等现实中的动作行为。你只需在引擎中注册界面元素,并指定好物理行为,其他的事就交给物理引擎去完成了。
UIDynamicAnimation属于UIKit物理引擎中的类。它的作用是跟踪你添加到物理引擎中的各种行为动作,并且提供整个上下文。实例化UIDynamicAnimator时,它的构造函数需要传入一个referenceView参数,用于告知它要跟踪并制定坐标的View。
在UIKit的动态引擎中,每个行为都有一个action属性,它的类型是一个函数(()->Void)。
collision.action = {
println("\(NSStringFromCGAffineTransform(square.transform))\(NSStringFromCGPoint(square.center))")
}
2. Motion Effects可以创建很酷的视觉效果,就像你iPhone上横竖屏切换时那样。它基于Apple提供的重力加速度提供的数据计算分析,使我们的界面元素根据移动设备的倾斜方向做出相应地反应。
物理引擎: UIDynamicAnimator
// 实例化UIKit物理引擎类,作用于ViewController的View
var animator: UIDynamicAnimator = UIDynamicAnimator(referenceView:self.view)
重力行为: UIGravityBehavior
UIGravityBehavior是一个模拟重力的模型,可作用于一个或多个元素。它的构造函数需要传入一个数组,该数组内容就是我们希望有重力表现的一个元素或多个元素。
// 实例化重力行为类,目前只作用于square控件
var gravity:UIGravityBehavior =UIGravityBehavior(items: [square])
// 角度 angle为0时,方块会水平向右移动,随着值得增大,方块会顺时针改变角度,不设置时默认是垂直向下移动。
gravity.angle =1.6
//速率 值越大下降的速度越快,当magnitude属性的值为0时,方块就不会下降了,所以最小的速率是0.1.
gravity.magnitude =0.1
// 将重力行为添加到UIKit物理引擎类中
animator.addBehavior(gravity)
// 碰撞行为: UICollisionBehavior
// 实例化碰撞行为类,目前只作用于控件square
var collision: UICollisionBehavior = UICollisionBehavior(items: [square])
// 将参考视图的边界作为碰撞边界,它德尔意思是将UIDynamicAnimator引用的View的边界作为碰撞行为的触发边界,这样就不用我们再去设置边界的坐标了。
collision.translatesReferenceBoundsIntoBoundary =true
// 为碰撞效果中添加一个隐形的边界。
collision.addBoundaryWithIdentifier("barrier", forPath:UIBezierPath(rect: barrier.frame)
// 将碰撞行为添加到UIKit物理引擎类中
animator.addBehavior(collision)
// 碰撞代理: UICollisionBehaviorDelegate
collision.collisionDelegate =self
// 关联行为: UIAttachmentBehavior
let attach:UIAttachmentBehavior = UIAttachmentBehavior(item: square1, attachedToItem: square2)
animator.addBehavior(attach)
// 用户交互行为: UISnapBehavior
当你点击屏幕时,UISnapBehavior行为会让对象像弹簧一样跳到你点击的那个位置。
var snap: UISnapBehavior!
overridefunc touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
ifsnap != nil {
animator.removeBehavior(snap)
}
let touch = (touches as NSSet).anyObject()as! UITouch
snap = UISnapBehavior(item:square, snapToPoint: touch.locationInView(view))
animator.addBehavior(snap)
}
//自定义行为: UIDynamcItemBehavior
let itemBehaviour =UIDynamicItemBehavior(items: [square])
itemBehaviour.elasticity =0.6
animator.addBehavior(itemBehaviour)
弹力(elaticity): 设置物体发生碰撞时的弹力,比如当物体碰撞时弹开的高度、角度的大小,物体的韧性等。
摩擦力(friction): 设置物体滑动时的摩擦力。
密度(density): 设置物体密度,密度越大加速度越大。
阻力(resistance): 设置物体滑动时的阻力,与friction不同的是,它只作用于线性滑动时。
角度阻力(angularResistance): 物体进行旋转运行时的阻力设置。
允许旋转(allowsRotation): 该属性并不是模拟现实中的一些行为属性,它是物体是否可以旋转的开关属性。
import UIKit
class ViewController: UIViewController, UICollisionBehaviorDelegate {
// UIKit物理引擎
var animator: UIDynamicAnimator!
// 重力行为
var gravity: UIGravityBehavior!
// 碰撞行为
var collision: UICollisionBehavior!
var firstContact = false
var square: UIView!
var snap: UISnapBehavior!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
square = UIView(frame: CGRectMake(100, 100, 100, 100))
square.backgroundColor = UIColor.cyanColor()
self.view.addSubview(square)
let barrier = UIView(frame: CGRectMake(0, 300, 140, 20))
barrier.backgroundColor = UIColor.redColor()
self.view.addSubview(barrier)
// 实现化UIKit物理引擎类,作用域ViewController的View
animator = UIDynamicAnimator(referenceView: self.view)
// 实例化重力行为类,目前只作用于刚才创建的正方形View
gravity = UIGravityBehavior(items: [square])
// 角度
gravity.angle = 1.6
// 速率
// gravity.magnitude = 0.1
// 将重力行为添加到UIKit物理引擎类中
animator.addBehavior(gravity)
// 实例化碰撞行为类,目前只作用于刚才创建的正方形View
collision = UICollisionBehavior(items: [square])
collision.collisionDelegate = self
collision.addBoundaryWithIdentifier("barrier", forPath: UIBezierPath(rect: barrier.frame))
// 将参考视图的边界作为碰撞边界
collision.translatesReferenceBoundsIntoBoundary = true
// 将碰撞行为添加到UIKit物理引擎类中
animator.addBehavior(collision)
// collision.action = {
// println("\(NSStringFromCGAffineTransform(square.transform)) \(NSStringFromCGPoint(square.center))")
// }
let itemBehaviour = UIDynamicItemBehavior(items: [square])
itemBehaviour.elasticity = 0.6
itemBehaviour.density = 1
itemBehaviour.resistance = 0
itemBehaviour.angularResistance = 0
itemBehaviour.allowsRotation = true
animator.addBehavior(itemBehaviour)
// var updateCount = 0
// collision.action = {
// if updateCount % 3 == 0{
// let outline = UIView(frame: square.bounds)
// outline.transform = square.transform
// outline.center = square.center
//
// outline.alpha = 0.5
// outline.backgroundColor = UIColor.clearColor()
// outline.layer.borderColor = square.layer.presentationLayer().backgroundColor
// outline.layer.borderWidth = 1
// self.view.addSubview(outline)
// }
// ++updateCount
// }
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
if snap != nil {
animator.removeBehavior(snap)
}
let touch = (touches as NSSet).anyObject() as! UITouch
snap = UISnapBehavior(item: square, snapToPoint: touch.locationInView(view))
animator.addBehavior(snap)
}
// MARK :-
// MARK :- UICollisionBehaviorDelegate
func collisionBehavior(behavior: UICollisionBehavior, beganContactForItem item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying, atPoint p: CGPoint) {
let collidingView = item as! UIView
collidingView.backgroundColor = UIColor.yellowColor()
UIView.animateWithDuration(0.3, animations: {
collidingView.backgroundColor = UIColor.grayColor()
})
if !firstContact {
firstContact = true
let square = UIView(frame: CGRectMake(30, 0, 100, 100))
square.backgroundColor = UIColor.grayColor()
self.view.addSubview(square)
collision.addItem(square)
gravity.addItem(square)
let attach = UIAttachmentBehavior(item: collidingView, attachedToItem: square)
animator.addBehavior(attach)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}