`

实战iOS7之UIDynamics

阅读更多

最近在看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<ballCount; i++) {
        BallView *ball = [[BallView alloc] initWithFrame:CGRectMake(0, 0, ballSize-1, ballSize-1)];
        CGFloat x = CGRectGetWidth(self.bounds)/3.0+i*ballSize;
        CGFloat y = CGRectGetHeight(self.bounds)/1.5;
        ball.center = CGPointMake(x, y);
        //为球添加UIPushBehavior,支持用户拖动
        UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleBallPan:)];
        [ball addGestureRecognizer:panGesture];
        //为球添加Oberser,当球的center属性发生改变时,会通知UIView的方法,刷新view,这儿主要是为了在球移动的时候保持锚点和球之间的连接线。
        [ball addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:Nil];
        [ballsArray addObject:ball];
        [self addSubview:ball];
        
        UIView *blueBox = [self createAnchorForBall:ball];
        [anchorsArray addObject:blueBox];
        [self addSubview:blueBox];
    }
    
    _balls = ballsArray;
    _anchors = anchorsArray;
}

- (UIView *)createAnchorForBall:(BallView *)ball
{
    CGPoint anchor = ball.center;
    //根据球的位置确定描点的位置
    anchor.y -= CGRectGetHeight(self.bounds) / 4.0;
    UIView *blueBox = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
    blueBox.backgroundColor = [UIColor blueColor];
    blueBox.center = anchor;
    return blueBox;
}

//处理拖动手势
-(void)handleBallPan:(UIPanGestureRecognizer *)recoginizer
{
    //用户开始拖动时创建一个新的UIPushBehavior,并添加到animator中
    if(recoginizer.state == UIGestureRecognizerStateBegan){
        if (_userDragBehavior) {
            [_animator removeBehavior:_userDragBehavior];
        }
        _userDragBehavior = [[UIPushBehavior alloc] initWithItems:@[recoginizer.view] mode:UIPushBehaviorModeContinuous];
        [_animator addBehavior:_userDragBehavior];
    }
    
    //用户完成拖动时,从animator移除PushBehavior
    _userDragBehavior.pushDirection = CGVectorMake([recoginizer translationInView:self].x/10.f, 0);
    if (recoginizer.state == UIGestureRecognizerStateEnded) {
        
        [_animator removeBehavior:_userDragBehavior];
        _userDragBehavior = nil;
    }
}

#pragma mark - UIDynamics utility methods
- (void)applyDynamicBehaviors
{
    //添加UIDynamic的动力行为,同时把多个动力行为组合为一个复杂的动力行为。
    UIDynamicBehavior *behavior = [[UIDynamicBehavior alloc] init];
    
    [self applyAttachBehaviorForBalls:behavior];
    [behavior addChildBehavior:[self createGravityBehaviorForObjects:_balls]];
    [behavior addChildBehavior:[self createCollisionBehaviorForObjects:_balls]];
    [behavior addChildBehavior:[self createItemBehavior]];
    
    _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self];
    [_animator addBehavior:behavior];
}

- (void)applyAttachBehaviorForBalls:(UIDynamicBehavior *)behavior
{
    //为每个球到对应的锚点添加一个AttachmentBehavior,并作为一个子Behavior添加到一个Behavior中。
    for(int i=0; i<ballCount; i++)
    {
        UIDynamicBehavior *attachmentBehavior = [self createAttachmentBehaviorForBallBearing:[_balls objectAtIndex:i] toAnchor:[_anchors objectAtIndex:i]];
        [behavior addChildBehavior:attachmentBehavior];
    }
}

- (UIDynamicBehavior *)createAttachmentBehaviorForBallBearing:(id<UIDynamicItem>)ballBearing toAnchor:(id<UIDynamicItem>)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<UIDynamicItem> 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/
  • 大小: 82.9 KB
  • 大小: 187.8 KB
1
0
分享到:
评论

相关推荐

    iOS7_Programming_Cookbook源代码

    《iOS 7 Programming Cookbook》是一本专为iOS开发者编写的实战指南,旨在帮助读者深入理解并熟练掌握iOS 7及更高版本的开发技术。源代码是书中各章节示例的实现,提供了丰富的实践案例,涵盖了iOS应用开发的多个...

    Swift iOS应用开发实战 完整版 (刘铭) pdf扫描版

    7. **动画效果**:介绍UIKit Dynamics和Core Animation,为应用添加动态效果和过渡动画。 8. **测试与调试**:讲解单元测试和集成测试,以及使用Xcode内置的调试工具进行问题定位。 9. **App发布**:最后,书会...

    iOS Apprentice 第六版 pdf电子书籍

    《iOS Apprentice 第六版》是一本专为初学者设计的iOS开发指南,旨在帮助读者从零开始学习如何使用Swift编程语言和Xcode开发环境构建iOS应用程序。这本书的PDF电子版本包含完整的源代码,使得读者可以边学边实践,...

    iOS Swift Programming Cookbook

    - 自定义键盘和扩展功能是iOS 8引入的重要特性之一,允许开发者创建自己的键盘或扩展程序来增强应用程序的功能。 - 书中提供了创建自定义键盘的具体步骤,包括如何设计键盘布局、处理按键输入等,并展示了如何创建...

    SwiftSampleAnimation:为 Apple Lab 制作的示例应用程序,快速显示了一些基本的动画、手势和 UIDynamics 实现

    SwiftSampleAnimation 是一个专为Apple Lab设计的示例应用程序,旨在通过实际的代码演示来教授开发者如何在iOS应用中创建和实现基本的动画效果、手势识别以及UIDynamics。这个项目对于初学者和有经验的iOS开发者来说...

    iPhone开发实战

    《iPhone开发实战》一书,主要涵盖了iOS应用开发的核心技术和实战经验,旨在帮助开发者深入理解并掌握iPhone应用程序的开发流程。以下将详细阐述其中的知识点: 1. **Swift编程语言**:作为iOS开发的主要语言,...

    UIDynamic的使用

    在iOS开发中,UIDynamics是苹果提供的一种强大的框架,它允许开发者模拟真实世界中的物理行为,如重力、碰撞、弹力等,为用户界面添加动态效果和交互性。这个框架是UIKit的一部分,使得我们可以创建出更加生动、有趣...

    IOS应用源码之高仿新百度贴吧列表效果.zip

    通过分析这个项目,开发者不仅可以学习到如何构建动态、交互丰富的列表,还能深入理解Swift编程、UI设计以及网络请求等多个iOS开发的关键领域。同时,这个源码也可以作为实战练习,提升开发者在实际项目中的问题解决...

    iOS Animations by Tutorials v4.0 && Source Code

    2. **UIKit Dynamics**: UIKit Dynamics是iOS 7引入的一个强大工具,它将物理模拟的概念引入到用户界面设计中。通过模拟重力、碰撞、摩擦等物理现象,可以实现更自然、真实的交互体验。例如,你可以创建摇晃效果、...

    swift个人项目实战学习

    在这个个人项目实战学习中,你将有机会亲手实践上述知识点,从创建项目、设计UI、编写业务逻辑到发布应用,逐步掌握iOS开发的全过程。通过这样的实践,不仅能够巩固Swift语言技能,还能提升解决实际问题的能力。在...

    iphone开发实战 书本源码

    8. **动画与过渡**:iOS提供的UIKit Dynamics和Core Animation可以创建丰富的视觉效果。源码可能包含自定义动画的实现。 9. **推送通知与本地通知**:学习如何集成Apple Push Notification Service (APNs) 和设置...

    《iPad应用开发实战》源码

    7. **动画与过渡**:UIKit Dynamics和CAAnimation框架可以实现复杂的动态效果和视图动画。源码中可能包含页面转场、滑动效果等实现。 8. **国际化与本地化**:为了让应用适应全球用户,源码可能包含字符串资源的...

    30_Swift_Projects-master5.zip

    - **基础项目**:包括简单的计算器应用、天气查询、日历应用等,让你巩固Swift的基本语法和UI设计。 - **进阶项目**:涉及网络请求、JSON解析、数据库操作,如新闻聚合应用、社交网络客户端,锻炼你的数据处理能力...

Global site tag (gtag.js) - Google Analytics