UIKit动力学的引入,并不是为了替代CA或者UIView动画,在绝大多数情况下CA或者UIView动画仍然是最优方案,只有在需要引入逼真的交互设计的时候,才需要使用UIKit动力学它是作为现有交互设计和实现的一种补充
UIDynamic中的三个重要概念
Dynamic Animator:动画者,为动力学元素提供物理学相关的能力及动画,同时为这些元素提供相关的上下文,是动力学元素与底层iOS物理引擎之间的中介,将Behavior对象添加到Animator即可实现动力仿真
Dynamic Animator Item:动力学元素,是任何遵守了UIDynamicItem协议的对象,从iOS 7.0开始,UIView和UICollectionViewLayoutAttributes默认实现该协议。如果自定义的对象实现了该协议,即可通过Dynamic Animator实现物理仿真
UIDynamicBehavior:仿真行为,是动力学行为的父类,基本的动力学行为类UIGravityBehavior、UICollisionBehavior、UIAttachmentBehavior、UISnapBehavior、UIPushBehavior以及UIDynamicItemBehavior均继承自该父类
iOS7.0中提供的动力学行为包括:
- UIGravityBehavior:重力行为
- UICollisionBehavior:碰撞行为
- UIAttachmentBehavior:附着行为
- UISnapBehavior:吸附行为
- UIPushBehavior:推行为
- UIDynamicItemBehavior:动力学元素行为
所有的UIDynamicBehavior都是可以独立作用,同时也遵守力的合成。也就是说,组合使用行为可以实现一些较复杂的效果
重力行为(Gravity)
重力行为用于给动力学元素指定一个重力向量,代码实现如下:
@interface GravityBehaviorViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@property (nonatomic,strong)UIView *blueView;
@end
@implementation GravityBehaviorViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//初始化动画者
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
// 创建重力行为
UIGravityBehavior *behavior = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
//重力等级
behavior.magnitude = 2;
//重力方向
// behavior.angle = M_PI_4;
// 创建碰撞行为
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.greendView]];
// 开启边缘检测
collision.translatesReferenceBoundsIntoBoundary = YES;
//添加行为
[self.animator addBehavior:behavior];
[self.animator addBehavior:collision];
}
-(UIView *)greendView{
if (!_greendView) {
_greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_greendView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_greendView];
}
return _greendView;
}
@end
碰撞行为(Collision)
碰撞行为用于指定一组动力学元素,在指定的边界范围内,可以彼此发生碰撞
碰撞行为提供了代理方法,可用于在物体碰撞前、后对动力学元素做碰撞后续的处理
@interface CollisionViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@property (nonatomic,strong)UIView *blueView;
@end
@implementation CollisionViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
TestView *testView = [[TestView alloc] initWithFrame:self.view.bounds];
testView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:testView];
_blueView = [[UIView alloc] initWithFrame:CGRectMake(120, 250, 80, 80)];
_blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:_blueView];
_greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_greendView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_greendView];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UIGravityBehavior *graBehavoir = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
UICollisionBehavior *collBehavoir = [[UICollisionBehavior alloc] initWithItems:@[self.greendView,self.blueView]];
//碰撞模式
// UICollisionBehaviorModeItems 元素与元素之间发生碰撞 (跟边界不碰撞)
// UICollisionBehaviorModeBoundaries 元素与边界发生碰撞 (与元素不碰撞)
// UICollisionBehaviorModeEverything 元素、边界都碰撞 (默认)
collBehavoir.collisionMode = UICollisionBehaviorModeEverything;
collBehavoir.translatesReferenceBoundsIntoBoundary = YES;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(230, 370) radius:100 startAngle:0 endAngle:M_PI clockwise:YES];
[collBehavoir addBoundaryWithIdentifier:@"ID" forPath:path];
//动力学元素自身的行为
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.greendView]];
itemBehavior.elasticity = 0.8;
itemBehavior.friction = 0.1;
[self.animator addBehavior:graBehavoir];
[self.animator addBehavior:collBehavoir];
[self.animator addBehavior:itemBehavior];
}
@end
下面是自定义的TestView
中绘制的Bezier曲线,用来模拟碰撞效果
- (void)drawRect:(CGRect)rect{
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(230, 370) radius:100 startAngle:0 endAngle:M_PI clockwise:YES];
[path stroke];
}
吸附行为(Snap)
- 吸附行为可以将视图通过动画吸附到某个点上
- 初始化设定一下UISnapBehavior的initWithItem:snapToPoint:即可
@interface SanpViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@end
@implementation SanpViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
_greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_greendView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_greendView];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UISnapBehavior *behavior = [[UISnapBehavior alloc] initWithItem:self.greendView snapToPoint:[touches.anyObject locationInView:self.view] ];
behavior.damping = 0.9;
[self.animator addBehavior:behavior];
}
@end
附着行为(Attachment)
- 附着行为描述一个视图与一个锚点或者另一个视图相连接的情况
- 附着行为描述的是两点之间的连接情况,可以模拟刚性或者弹性连接
- 在多个物体间设定多个UIAttachmentBehavior,可以模拟多物体连接
- 常用属性:
- attachedBehaviorType:连接类型(连接到锚点或视图)
- items:连接视图数组
- anchorPoint:连接锚点
- length:距离连接锚点的距离
- 只要设置了以下两个属性,即为弹性连接
- damping:振幅大小
- frequency:振动频率
#import "AttachViewController.h"
#import "TestView.h"
@interface AttachViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@property (nonatomic,strong)TestView *testView;
@end
@implementation AttachViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.testView = [[TestView alloc] initWithFrame:self.view.bounds];
self.testView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.testView];
self.view.backgroundColor = [UIColor whiteColor];
_greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_greendView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_greendView];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
__weak typeof(self)weakSelf = self;
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UIAttachmentBehavior *behavior = [[UIAttachmentBehavior alloc] initWithItem:self.greendView attachedToAnchor:[touches.anyObject locationInView:self.view]];
behavior.length = 180;
// action为行为过程中的回调
behavior.action = ^{
weakSelf.testView.end = [self.view convertPoint:CGPointMake(50, 50) fromView:self.greendView];
weakSelf.testView.start = [touches.anyObject locationInView:self.view];
};
UIGravityBehavior *graBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
[self.animator addBehavior:behavior];
[self.animator addBehavior:graBehavior];
}
@end
此时TestView中的代码:
-(void)setEnd:(CGPoint)end{
_end = end;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.start];
[path addLineToPoint:self.end];
[path stroke];
}
推行为(Push)
- 推行为可以为一个视图施加一个作用力,该力可以是持续的,也可以是一次性的
- 可以设置力的大小,方向和作用点等信息
@interface PushBViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@end
@implementation PushBViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
_greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
_greendView.backgroundColor = [UIColor greenColor];
[self.view addSubview:_greendView];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UIPushBehavior *pushB = [[UIPushBehavior alloc] initWithItems:@[self.greendView] mode:UIPushBehaviorModeContinuous];
[pushB setAngle:0 magnitude:10];
UICollisionBehavior *collisionB = [[UICollisionBehavior alloc] initWithItems:@[self.greendView]];
collisionB.translatesReferenceBoundsIntoBoundary = YES;
UIGravityBehavior *graB = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
[self.animator addBehavior:graB];
[self.animator addBehavior:pushB];
[self.animator addBehavior:collisionB];
}
@end
献上DEMO地址