最近在看Sam Davies写的iOS7系列文章( http://www.shinobicontrols.com/blog/posts/2013/09/19/introducing-ios7-day-by-day),非常有意思,以此为契机准备系统的学习一下iOS7的新特性, 今天是第一篇总结:UIDynamics。
UIDynamics的作用
在iOS7之前, 想要实现动画功能只有通过CoreAnimation或者UIView的Animation, 只能自己画, 对于一些真实世界中的动画行为,碰撞啊之类,实现起来特别复杂,而UIDynamic的引入就是为了简化这一类动画行为的。其将现实世界中各种物理动力驱动的动画引入了UIKit。(注:其只只引入了2D世界的物理引擎)。有了它之后,你会惊讶于可以通过如此少的代码实现这么复杂的动画效果。
UIDynamics的知识点
实例解析
下面我们就来实例解析下,如何通过UIDynamics实现一个牛顿摆(Newton's Cradle), 实现后的效果如下:
在实现一个UIDynamic动画时, 首先需要把整个的动画过程分解到各个物理动力上, 实现一个牛顿摆的物理动力有:
- 1. 重力(UIGravityBehavior)
- 2. 球和锚点之间的牵引力(UIAttachmentBehavior)
- 3. 球与球之间的碰撞(UICollisionBehavior)
- 4. 空气阻力,摩擦力等(UIDynamicItemBehavior)
- 5. 手移动球时的推动力(UIPushBehavior)
分解完动力之后,来看代码实现:
首先,创建一个球,就是一个简单的UIView,设置下形状,颜色什么的:
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor yellowColor]; self.layer.cornerRadius = 10; self.layer.borderColor = [UIColor redColor].CGColor; self.layer.borderWidth = 3; } return self; }
然后,创建牛顿摆的View,一个牛顿摆的界面设计的UI元素有3个, 球,锚点,球和描点的连接线,另外,为了实现UIDynamic的动画效果,需要一个UIDynamicAnimator来存储所有的DynamicBehavior,同时还有一个记录用户拉球的UIPushBehavior。
#import "NewtonsCradleView.h" @implementation NewtonsCradleView{ //球的个数 NSUInteger ballCount; //球和锚点 NSArray *_balls; NSArray *_anchors; UIDynamicAnimator *_animator; UIPushBehavior *_userDragBehavior; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { ballCount = 5; //初始化球和锚点 [self createBallsAndAnchors]; //添加UIDynamics动力行为 [self applyDynamicBehaviors]; } return self; } -(void)createBallsAndAnchors { NSMutableArray *ballsArray = [NSMutableArray array]; NSMutableArray *anchorsArray = [NSMutableArray array]; //估算球的大小,占屏1/3,处于屏幕中间方便用户玩 CGFloat ballSize = CGRectGetWidth(self.bounds)/(3.0*(ballCount-1)); for(int i=0; i)ballBearing toAnchor:(id )anchor { //把球attach到锚点上 UIAttachmentBehavior *behavior = [[UIAttachmentBehavior alloc] initWithItem:ballBearing attachedToAnchor:[anchor center]]; return behavior; } - (UIDynamicBehavior *)createGravityBehaviorForObjects:(NSArray *)objects { // 为所有的球添加一个重力行为 UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:objects]; gravity.magnitude = 10; return gravity; } - (UIDynamicBehavior *)createCollisionBehaviorForObjects:(NSArray *)objects { // 为所有的球添加一个碰撞行为 return [[UICollisionBehavior alloc] initWithItems:objects]; } - (UIDynamicItemBehavior *)createItemBehavior { // 为所有的球的动力行为做一个公有配置,像空气阻力,摩擦力,弹性密度等 UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:_balls]; itemBehavior.elasticity = 1.0; itemBehavior.allowsRotation = NO; itemBehavior.resistance = 1.f; return itemBehavior; } #pragma mark - Observer -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // Observer方法,当ball的center属性发生变化时,刷新整个view [self setNeedsDisplay]; } //覆盖父类的方法,主要是为了在锚点和球之间画一条线 -(void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); for(id ballBearing in _balls){ CGPoint anchor =[[_anchors objectAtIndex:[_balls indexOfObject:ballBearing]] center]; CGPoint ballCenter = [ballBearing center]; CGContextMoveToPoint(context, anchor.x, anchor.y); CGContextAddLineToPoint(context, ballCenter.x, ballCenter.y); CGContextSetLineWidth(context, 1.0f); [[UIColor blackColor] setStroke]; CGContextDrawPath(context, kCGPathFillStroke); } [self setBackgroundColor:[UIColor whiteColor]]; } //添加了Observer必须释放,不然会造成内存泄露。 -(void)dealloc { for (BallView *ball in _balls) { [ball removeObserver:self forKeyPath:@"center"]; } } @end
把上面的2个View集合在一起,弄进一个App里就可以玩了。
所有的代码在这儿: https://github.com/xianlinbox/iOS7_New
参考文章:
http://onevcat.com/2013/06/uikit-dynamics-started/
http://beyondvincent.com/blog/2013/06/16/88/