前言:
iOS的设计目标鼓励您创建数字接口(digital interface),对触摸,手势和方向的变化做出反应,就好像它们是物理对象而不仅仅是简单的像素集合。可以使用户可以通过皮肤深层的自身形态与界面更深层次的联系。
打开ViewController.swift,并将以下代码添加到下面的代码viewDidLoad:
let square = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
square.backgroundColor = UIColor.gray
view.addSubview(square)
animator = UIDynamicAnimator(referenceView: view)
gravity = UIGravityBehavior(items: [square])
animator.addBehavior(gravity)
大多数行为具有许多配置属性; 例如,重力行为允许您改变其角度和幅度。尝试修改这些属性,使您的对象以不同的加速度下降,侧面或对角线。
注意:单位上的一个简单单词:在物理世界中,重力(g)以米/秒表示,大约等于9.8 m/s2。使用牛顿第二定律,您可以用下列公式计算物体在重力影响下的距离:
distance = 0.5 × g × time2
在UIKit Dynamics中,公式是相同的,但单位是不同的。而不是米,您可以使用每秒成千上万个像素的单位。使用牛顿第二定律,您仍然可以根据您提供的重力组件随时确定您的view在何处。
即使在屏幕底部消失后,它也会继续下降。为了将其保留在屏幕的边界内,您需要定义边界
var collision: UICollisionBehavior!
collision = UICollisionBehavior(items: [square])
collision.translatesReferenceBoundsIntoBoundary = true
animator.addBehavior(collision)
上述代码创建了一个碰撞行为,该行为定义了一个或多个相关联项目与之相关联的边界。 而不是明确添加边界坐标,上述代码将translatesReferenceBoundsIntoBoundary 属性设置为true。这导致边界提供给UIDynamicAnimator参考视图的边界。
添加一个不可移动的障碍,下降的正方形将与之相冲突。
let barrier = UIView(frame: CGRect(x: 0, y: 300, width: 130, height: 20))
barrier.backgroundColor = UIColor.red
view.addSubview(barrier)
确实提供了一个重要的提醒:dynamics只影响与行为相关联的视图 大多数行为可以与多个项目相关联,并且每个项目可以与多个行为相关联
为了使square
与障碍物相撞,请找到初始化碰撞行为的行,并将其替换为以下内容:
collision = UICollisionBehavior(items: [square, barrier])
碰撞对象需要知道它应该与之相互作用的每个视图; 因此,将项目列表中的障碍添加到允许碰撞对象也可以作用在障碍物上。 效果如下:
与障碍物碰撞
可以看出,square
跟障碍物交互不是很正确,障碍物应该不可移动,更奇怪的是障碍物从屏幕的底部反弹,并不像square
那样沉稳,因为重力行为与障碍物无关
将碰撞行为初始化更改回最初
collision = UICollisionBehavior(items: [square])
在这一行之后,添加一下内容:(添加跟barrier
相同frame的boundary
)
collision.addBoundary(withIdentifier: "barrier" as NSCopying, for: UIBezierPath(rect: barrier.frame))
红色障碍物对用户仍旧可见,而对动力引擎(dynamics engine)不可见;相反边界(boundary)对动力引擎可见,对用户不可见
随着square
的下降,它似乎与barrier
相互作用,但它实际上是与不可动的boundary
相撞。
与障碍物碰撞2
下面将展示动态引擎如何与应用程序中的对象进行交互的一些细节。
每个动态行为(dynamic behavior)都有个一个action属性,你可以在action属性中提供要在动画每一步执行的block,讲下列代码添加到viewDidLoad:
collision.action = {
print("\(NSStringFromCGAffineTransform(square.transform)) \(NSStringFromCGPoint(square.center))")
}
打印日志如下:(动力引擎对square
的frame的影响)
[1, 0, 0, 1, 0, 0] {150, 212}
[1, 0, 0, 1, 0, 0] {150, 218}
[1, 0, 0, 1, 0, 0] {150, 224}
[1, 0, 0, 1, 0, 0] {150, 231}
[1, 0, 0, 1, 0, 0] {150, 237}
[1, 0, 0, 1, 0, 0] {150, 244}
一旦方块击中障碍物,它就开始旋转,这样会产生如下的日志信息:
[0.9999181, 0.01279965, -0.01279965, 0.9999181, 0, 0] {150, 249}
[0.99820054, 0.059964005, -0.059964005, 0.99820054, 0, 0] {152, 249}
[0.9942596, 0.10699479, -0.10699479, 0.9942596, 0, 0] {154, 249}
[0.98810399, 0.15378727, -0.15378727, 0.98810399, 0, 0] {155, 249}
[0.97978747, 0.20004122, -0.20004122, 0.97978747, 0, 0] {157, 250}
[0.96930701, 0.24585338, -0.24585338, 0.96930701, 0, 0] {158, 251}
从打印日志,可以看到动态引擎正在使用变换(transform)和frame偏移(frame offset)的来改变view的position
如果在动画过程中,我们通过代码改变方块的frame和transform属性,物体属性会被我们重写,也就是说,动力学控制过程中,我们不应该通过transform来缩放物体等。
动力行为赋予的对象是term items而不是view。因此,拥有动力行为的对象需要遵守UIDynamicItem协议:
protocol UIDynamicItem : NSObjectProtocol {
var center: CGPoint { get set }
var bounds: CGRect { get }
var transform: CGAffineTransform { get set }
}
该UIDynamicItem协议给dynamics读写访问中心和转换属性(the center and transform properties),允许它基于其内部计算移动items。它还具有对边界的读取访问权限,它用于确定items的size,这样可以在items周边创建碰撞边界,并在施加力时计算物品的质量。
这个协议意味着动态不紧密耦合UIView; 确实有另一个UIKit类不是视图,但仍然采用这个协议:UICollectionViewLayoutAttributes
。这允许dynamics动画在集合视图中对items进行动画。
添加UICollisionBehaviorDelegate
class ViewController: UIViewController, UICollisionBehaviorDelegate {
添加一个协议方法
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) {
print("Boundary contact occurred - \(identifier)")
}
打印日志如下:
Boundary contact occurred - Optional(barrier)
Boundary contact occurred - Optional(barrier)
Boundary contact occurred - Optional(barrier)
Boundary contact occurred - Optional(barrier)
Boundary contact occurred - nil
Boundary contact occurred - nil
Boundary contact occurred - nil
Boundary contact occurred - nil
square
与带标识barrier
的边界相撞四次
为了方便看,我们改一下square
的背景颜色,每次撞击边界时,方形将闪烁黄色。在collisionBehavior
方法里面加以下代码
let collidingView = item as! UIView
collidingView.backgroundColor = UIColor.yellow
UIView.animate(withDuration: 0.3) {
collidingView.backgroundColor = UIColor.gray
}
到目前为止,UIKit Dynamics通过根据您的项目的界限进行计算,自动设置物品的物理属性(如质量和弹性)。接下来,您将看到如何通过使用UIDynamicItemBehavior
该类自己来控制这些物理属性。
上述代码创建一个item行为,将其与square
相关联,然后将该行为对象添加到动画制作器。弹性属性控制物品的柔软度; 值为1.0表示完全弹性的碰撞; 也就是说,碰撞中没有能量或速度损失。您将您的square
的弹性设置为0.6,这意味着每次弹跳时,平方将失去速度。
我们加以下代码,有线框,方便看
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.clear
outline.layer.borderColor = square.layer.presentation()?.backgroundColor
outline.layer.borderWidth = 1.0
self.view.addSubview(outline)
}
updateCount += 1
}
在上面的代码中,只改变了项目的弹性; 但是,该项目的行为类具有可以在代码中操作的其他许多属性。它们如下:
下面,介绍如何动态添加和删除行为。
首先添加一下属性
var firstContact = false
将以下代码添加到碰撞委托方法(collisionBehavior
)的末尾
if (!firstContact) {
firstContact = true
let square = UIView(frame: CGRect(x: 30, y: 0, width: 100, height: 100))
square.backgroundColor = UIColor.gray
view.addSubview(square)
collision.addItem(square)
gravity.addItem(square)
let attach = UIAttachmentBehavior(item: collidingView, attachedTo:square)
animator.addBehavior(attach)
}
上述代码检测到barrier
和square
之间的初始接触,创建第二个square
并将其添加到碰撞和重力行为。
效果如下:
此外,您还可以设置 attachment 行为,以创建使用虚拟弹簧连接一对对象的效果。
添加另一种类型的动态行为——UISnapBehavior,当用户点击时,UISnapBehavior 让对象以弹簧般动画效果跳到一个特定的位置
现在移除firstContact
属性以及在collisionBehavior()
添加它的的代码
在touchesEnded
方法中加入下面代码
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
if (snap != nil) {
animator.removeBehavior(snap)
}
let touch = touches.anyObject() as UITouch
snap = UISnapBehavior(item: square, snapToPoint: touch.locationInView(view))
animator.addBehavior(snap)
}
这段代码很简单。首先,它检查是否存在现有的捕捉行为(snap behavior)并将其删除。然后创建一个新的捕捉行为,将square
对齐到用户触摸的位置,并将其添加到动画制作工具(animator)。
现在你可以随便点击屏幕,square
会跳到你点击的位置。
效果如下:
下一篇UIKit Dynamics 的介绍
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有