UIKit Dynamics 在 iOS 7 中首次被引入,可以让开发者通过简单的方式,给应用上的界面添加模拟物理世界的交互动画,用于改善用户体验。
在iOS 9中又加入了一些大的改进,在iOS 9之前,UIKitDynamics 的 collision bounds
只能是长方形。这让一些并非是完美的长方形的碰撞效果看起来有些古怪。iOS 9 中支持三种 collision bounds
分别是Rectangle(长方形)
,Ellipse(椭圆形)
和 Path(路径)
。Path 可以是任意路径,只要是逆时针的,并且不是交叉在一起的。一个警告是,path 必须是凸面的不能使凹面的,当然也可以自定义视图。讲完了常识,有了一个概念后开始干货讲解一下属性以及用法吧。
UIDynamicAnimator
UIDynamicAnimator
用于创建力学动画生成器,封装了底下的物理引擎,使得我们可以方便地添加物理行为。
// 行为视图的父视图必须是动画生成器的参考视图
UIDynamicAnimator * animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UIDynamicAnimator
包含了下面几种物理动画:
1、UIGravityBehavior(重力)
2、UICollisionBehavior(碰撞)
3、UIAttachmentBehavior(吸附)
4、UISnapBehavior (捕获)
5、UIPushBehavior(作用力)
另外UIDynamicBehavior
定义了物理行为的类型(例如:UIGravityBehavior等行为),UIDynamicItem
参与物理动画的对象。具体的下面开始讲解:
UIGravityBehavior——重力
创建UIGravityBehavior,并添加到UIDynamicBehavior
,代码如下:
UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[label]];
[animator addBehavior:gravityBehavior];
上面是简单的动力的创建,Items
指需要添加重力动画的对象。当然,还可以设置重力的方向以及作用力(也就是重力加速度,1.0为地球的重力加速度)
// 设置方向(dx,dy)
gravityBehavior.gravityDirection = CGVectorMake(0, 1.0);
// 设置angle:角度(弧度) magnitude:量级(重力系数)
// 当然弧度以及重力系数都可以单独设置
[gravityBehavior setAngle:3.14/2 magnitude:0.1f];
magnitude重力系数:取值在0.0 - 1.0之间,1.0为地球引力,UIGravityBehavior就简单的介绍到这边。
UICollisionBehavior——碰撞
创建UICollisionBehavior,并添加到UIDynamicBehavior
,代码如下:
UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[label]];
[animator addBehavior:collisionBehavior];
iOS提供了对UICollisionBehavior的封装包括:碰撞模式、设置边界以及UICollisionBehaviorDelegate
代理
模式
typedef NS_OPTIONS(NSUInteger, UICollisionBehaviorMode) {
UICollisionBehaviorModeItems = 1 << 0,//物体相互碰撞
UICollisionBehaviorModeBoundaries = 1 << 1,//物体不相互碰撞,只与边界碰撞
UICollisionBehaviorModeEverything = NSUIntegerMax//既与物体碰撞又与边界碰撞
} NS_ENUM_AVAILABLE_IOS(7_0);
边界
collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
代理
UICollisionBehaviorDelegate
提供了几种方法:
// 开始碰撞
- (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier atPoint:(CGPoint)p;
// 碰撞结束
- (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier;
UICollisionBehavior
碰撞的介绍就到这了哈,具体使用还要看情景。
UIAttachmentBehavior——吸附
创建,并添加到引擎上:
UIAttachmentBehavior * attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:connectLabel attachedToAnchor:centerLabel.center];
[animator addBehavior:attachmentBehavior];
系统提供的构造初始化方法有以下几种:
// 元素和锚点之间的吸附
- (instancetype)initWithItem:(id <UIDynamicItem>)item attachedToAnchor:(CGPoint)point;
// 元素和锚点之间的吸附,offset参数设置元素连接力作用点的偏移量
- (instancetype)initWithItem:(id <UIDynamicItem>)item offsetFromCenter:(UIOffset)offset attachedToAnchor:(CGPoint)point NS_DESIGNATED_INITIALIZER;
// 元素和元素之间的吸附
- (instancetype)initWithItem:(id <UIDynamicItem>)item1 attachedToItem:(id <UIDynamicItem>)item2;
// 元素和元素之间的吸附,offset1、offset2分别是两个元素连接力作用点的偏移量
- (instancetype)initWithItem:(id <UIDynamicItem>)item1 offsetFromCenter:(UIOffset)offset1 attachedToItem:(id <UIDynamicItem>)item2 offsetFromCenter:(UIOffset)offset2 NS_DESIGNATED_INITIALIZER;
属性
类型
typedef NS_ENUM(NSInteger, UIAttachmentBehaviorType) {
UIAttachmentBehaviorTypeItems, //吸附元素
UIAttachmentBehaviorTypeAnchor //吸附锚点
} NS_ENUM_AVAILABLE_IOS(7_0);
根据不同需求,针对是需要吸附元素Items
还是锚点Anchor
进行处理动画。
锚点
@property (readwrite, nonatomic) CGPoint anchorPoint;
当吸附发生在元素和锚点之间的时候,我们可以通过anchorPoint属性获得锚点位置,如果吸附发生在元素和元素之间的时候,该属性的值为(0, 0)
距离
元素吸附力作用点和锚点的距离 或 两个元素吸附力作用点间的距离
@property (readwrite, nonatomic) CGFloat length;
振动频率
@property (readwrite, nonatomic) CGFloat frequency; // in Hertz
frequency
是指元素在发生弹性吸附时震动的频率,值越大,弹性运动的频率越快。
弹性阻力
@property (readwrite, nonatomic) CGFloat damping; // 1: critical damping
damping
是设置元素在弹性吸附时震动所受到的阻力,值越大,阻力越大,弹性运动震动的幅度越小。
旋转阻力
@property (readwrite, nonatomic) CGFloat frictionTorque NS_AVAILABLE_IOS(9_0); // default is 0.0
frictionTorque
为旋转力矩,指围绕一点旋转所受的阻力,默认值0.0,值越大,阻力越大。
运动范围
@property (readwrite, nonatomic) UIFloatRange attachmentRange NS_AVAILABLE_IOS(9_0); // default is UIFloatRangeInfinite
iOS 9新出的几个方法就不去说了,没用过哈,等后面去玩玩再来更新,当然有玩过的欢迎指教科普一下哈。
UISnapBehavior——捕获
创建UISnapBehavior
UISnapBehavior * snapBehavior = [[UISnapBehavior alloc] initWithItem:_dynamicItem1View snapToPoint:CGPointMake(250, 400)];
[animator addBehavior: snapBehavior];
Items
参数为添加的动态元素,Point
参数为要捕获的点。
属性
捕获点
@property (nonatomic, assign) CGPoint snapPoint NS_AVAILABLE_IOS(9_0);
在初始化方法中可以直接设置捕获点,当然也可以通过手势点击设置捕获点。
震动阻尼
@property (nonatomic, assign) CGFloat damping;
阻尼的有效范围为0.0~1.0,0.0最大震荡、1.0最小震荡,默认值为0.5
UIPushBehavior——作用力
给某物体加上一个作用力,让物体运动起来。
UIPushBehavior * pushBehavior = [[UIPushBehavior alloc] initWithItems:@[label] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.pushDirection = CGVectorMake(1.0f, 0);
[animator addBehavior:pushBehavior];
属性
类型
typedef NS_ENUM(NSInteger, UIPushBehaviorMode) {
UIPushBehaviorModeContinuous, //连续作用力
UIPushBehaviorModeInstantaneous //瞬间作用力
} NS_ENUM_AVAILABLE_IOS(7_0);
活跃状态
@property (nonatomic, readwrite) BOOL active;
在添加一个push behavior
到animator
时,使用这个属性来激活或禁用推力作用,而不是通过重新添加behavior
来实现。
推力角度
@property (readwrite, nonatomic) CGFloat angle;
推力矢量的大小
@property (readwrite, nonatomic) CGFloat magnitude;
默认值为nil,没有任何力量。当设置一个负值,力的方向改变。
推力矢量的方向
@property (readwrite, nonatomic) CGVector pushDirection;
方法
// 作用力中心偏移量设置
- (void)setTargetOffsetFromCenter:(UIOffset)o forItem:(id <UIDynamicItem>)item;
// 获取作用力中心偏移量
- (UIOffset)targetOffsetFromCenterForItem:(id <UIDynamicItem>)item;
// 设置推力的角度和大小
- (void)setAngle:(CGFloat)angle magnitude:(CGFloat)magnitude;
总结与问题
总体就先介绍这些吧,文章也比较长还没配图就凑合着看吧。本文还不够完善,给几个写的不错的链接吧@MajorLMJ,这位大神写的都很赞,他是把每一种类型单独区分开也配上了动画图,没办法公司的电脑配置跑不起模拟器手机录屏又麻烦就先不弄了哈,后续会更新。顺带附上一个对引擎应用的很赞的帖子仿饿了么店铺滚动,公司项目也有这个需求找了好久,后面又实践了下还是达不到饿了么或者美团外卖的效果,惭愧啊只做出一半的效果上滑的时候都没有问题但是在下拉回弹的时候达不到,当然还是会后续努力...废话就扯到这儿了。当然有实现出来的大牛欢迎给个demo给小弟研究研究或者指导一下也行,非常感谢哈。