UIKit动力学三大要素
- 动力项,要做动画的元素。最常见的是视图,但任何遵从UIDynamicItem协议的动力项都可以,该协议包括bounds、center、transform属性。
- 行为,伴随时间影响一个或多个动力项。包括动力项附着到一点,重力、摩擦力。
- 动画类,伴随时间对动力项实施行为的引擎。
动力项附加到行为,行为附加到动画类。动力项不知道它的行为,如果一个视图添加了重力行为,接着将视图移除出父视图,则动力项会持续持有该视图,并对其进行动画,即使视图不可见。
内置行为
iOS 内置了可以处理大多数简单需求的有用的行为,包括迅速移动、附着、重力、碰撞、推力和动力项。
-
迅速移动 UISnapBehavior
锚点设置为视图的中心点,视图就拥有可旋转的效果
@property (nonatomic) UIDynamicAnimator *dynamicAnimator; // 根据参考视图初始化动力学动画类 self.dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; // 通过initWithCollectionViewLayout 的方式来对集合视图进行动画,这种方式创建出来的动画类对UICollectionViewLayoutAttributes 对象进行动画 // 通过init 方式创建出来的动画类,没有参考视图,可以对任意遵从<UIDynamicItem>对象进行动画 // 移除所有行为 - (IBAction)reset { [self.dynamicAnimator removeAllBehaviors]; } // 迅速移动 = 位移 + 弹力效果 - (IBAction)snap { CGPoint point = [self randomPoint]; UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.box snapToPoint:point]; // 位移修改属性,默认值0.5,越高越慢 snap.damping = 0.25; [self.dynamicAnimator addBehavior:snap]; [self.dynamicAnimator performSelector:@selector(removeBehavior:) withObject:snap afterDelay:1]; }
-
附着 UIAttachmentBehavior
如果在一个动力项上添加多个行为,比如附着 + 迅速移动,那么会导致项目更加抖动且围绕附着点旋转,解决办法是使用多个附着
- (IBAction)attach { // 可以附着到一个点上,并偏移中心 UIAttachmentBehavior *attach1 = [[UIAttachmentBehavior alloc] initWithItem:self.box1 offsetFromCenter:UIOffsetMake(25, 25) attachedToAnchor:self.box1.center]; [self.dynamicAnimator addBehavior:attach1]; // 也可以附着到一个项目上 UIAttachmentBehavior *attach2 = [[UIAttachmentBehavior alloc] initWithItem:self.box2 attachedToItem:self.box1]; [self.dynamicAnimator addBehavior:attach2]; // UIPushBehaviorModeInstantaneous 一个瞬间的推动 UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:@[self.box2] mode:UIPushBehaviorModeInstantaneous]; push.pushDirection = CGVectorMake(0, 2); [self.dynamicAnimator addBehavior:push]; }
-
推力 UIPushBehavior
-
重力 UIGravityBehavior
一个动力学动画类只能有一个重力行为
标准的UIKit重力是(0,1),即一个大小的向下向量,它对所有动力项以1000p/s2进行加速
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.box1, self.box2]]; gravity.action = ^{ NSLog(@"%@", NSStringFromCGPoint(self.box1.center)); }; [self.dynamicAnimator addBehavior:gravity];
-
碰撞 UICollisionBehavior
- 可以通过setTranslatesReferenceBoundsIntoBoundary、setTranslatesReferenceBoundsIntoBoundaryWithInsets给碰撞设置边界
- 创建碰撞行为后,可以添加、删除对象,也可以添加或删除边界,边界通过标识符跟踪,方便查找修改
- 碰撞行为还需要一个委托,对象碰撞时可以跟踪,通常用于播放声音,也可以用来添加、删除行为,例如添加一个附着行为,碰撞后就会粘在一起
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.box1, self.box2]]; [self.dynamicAnimator addBehavior:collision]; UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:@[self.box1] mode:UIPushBehaviorModeInstantaneous]; push.pushDirection = CGVectorMake(3, 0); [self addTemporaryBehavior:push];
动力项
-
UIDynamicItemBehavior 与其他行为不同,用于对动力项赋值物理属性。例如:密度、弹性、摩擦力、阻力。
-
对一个对象施加推力,移除推力行为,动力项立即停止移动
-
如果动力项拥有一个动力项行为,即使移除推力行为,动力项也会保持当前速度继续移动。
实战代码
行为合集:行为内包含多个内置行为
// 1. 继承关系
@interface DefaultBehavior : UIDynamicBehavior
// 2. 将需要集合的行为附加到当前行为
- (instancetype)init {
self = [super init];
if (self) {
// 碰撞行为
UICollisionBehavior *collisionBehavior = [UICollisionBehavior new];
collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
[self addChildBehavior:collisionBehavior];
// 重力行为
UIGravityBehavior *gravityBehavior = [UIGravityBehavior new];
[self addChildBehavior:gravityBehavior];
}
return self;
}
// 3.封装两个对外函数,使动力项可以附加到行为上
- (void)addItem:(id<UIDynamicItem>)item {
for (id behavior in self.childBehaviors) {
[behavior addItem:item];
}
}
- (void)removeItem:(id<UIDynamicItem>)item {
for (id behavior in self.childBehaviors) {
[behavior removeItem:item];
}
}
自定义行为
// 移动视图 DraggableView
// 1. 保存控制器动画类 UIDynamicAnimator,添加拖拽手势
// 2. 实现 <NSCopying> ,传递动画类,同步bounds、center、transform、alpha
// 3. 响应手势函数:
// 3.1 拖拽过程中:动画类移除移动行为 UISnapBehavior,通过创建新的移动行为来实现位移
// 3.2 拖拽结束:移除移动行为
// 自定义“分裂”行为 TearOffBehavior
// 1. 初始化
// 1.1 通过添加 快速移动行为 UISnapBehavior 实现视图可旋转
[self addChildBehavior:[[UISnapBehavior alloc] initWithItem:view
snapToPoint:anchor]];
// 1.2 实现 self.action 属性
// 1.2.1 当视图发生偏移足够距离,通过复制当前视图,创建一个“分裂”出来的新视图,并将 新视图 附加到 新的分裂 行为上。
// 1.2.2 注意: handler实际上只有一个,都是原始视图初始化的时候传递的
// 1.2.3 行为和动画类的交替,移除动画类上前一个分裂行为,只保留新的
// 1.2.4 给分裂出来的复制品添加碰撞和重力行为,并且添加个移除的特效(通过UIGraphicsBeginImageContext打开上下文将视图切片,通过拼接切片的方式维持原视图的视觉效果,通过施加推力行为+UIView动画使透明度渐变为0,并在动画结束后移除视图)
网友评论