UIDynamic动画(很好玩)

碰撞动画.gif

最近公司的项目有点紧,这段时间抽时间整理下关于UIDynamic的思路,现在将牛顿摆锤动画做一个简单的实现.

  • 根据效果可以看到, 需要实现的动画有

1.自由下落动画 UIGravityBehavior
2.用户拖拽动画 UIPushBehavior
3.碰撞动画 UICollisionBehavior
4.连带动画 UIAttachmentBehavior
5.空气的考虑等 UIDynamicItemBehavior

1.UIAttachmentBehavior 描述一个view和一个锚相连接的情况,也可以描述view和view之间的连接。attachment描述的是两个点之间的连接情况,可以通过设置来模拟无形变或者弹性形变的情况(再次希望你还记得这些概念,简单说就是木棒连接和弹簧连接两个物体)。当然,在多个物体间设定多个;UIAttachmentBehavior,就可以模拟多物体连接了..有了这些,似乎可以做个老鹰捉小鸡的游戏了- -…

2.UISnapBehavior 将UIView通过动画吸附到某个点上。初始化的时候设定一下UISnapBehavior的initWithItem:snapToPoint:就行,因为API非常简单,视觉效果也很棒,估计它是今后非游戏app里会被最常用的效果之一了;

3.UIPushBehavior 可以为一个UIView施加一个力的作用,这个力可以是持续的,也可以只是一个冲量。当然我们可以指定力的大小,方向和作用点等等信息。

4.UIDynamicItemBehavior 其实是一个辅助的行为,用来在item层级设定一些参数,比如item的摩擦,阻力,角阻力,弹性密度和可允许的旋转等等。

  • 根据视图看出需要的控件有(摆动的view,锚点view,中间的连线view)
  • 在这里我采用自定义控件将摆动的boll设置为圆形的View

需要的属性

@implementation BollAnimation
{
    // 记录球的个数
    NSInteger bollCount;
    // 放置所有的球
    NSArray *_bolls;
    // 放置所有的锚点
    NSArray *_anchors;
    // UIDynamicAnimator 可以理解为动画的执行框框,主要用来规定动画执行范围,以及动画的行为(那些动画)
    UIDynamicAnimator *_animator;
    // 记录用户的拖拽
    UIPushBehavior *_userDragBehavior;
}

关于代码的实现(主要是其中方法的设计,在注释中对其进行详细介绍)

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 设置球的总数
        bollCount = 5;
        self.backgroundColor = [UIColor whiteColor];
        // 初始化球和锚点
        [self createBollsAndAnchors];
        // 给所有的球添加动画行为
        [self applyDynamicBehaviors];
    }
    return self;
}
// 用来初始化所有的球
- (void)createBollsAndAnchors
{
    NSMutableArray *bolls = [NSMutableArray array];
    NSMutableArray *anchors = [NSMutableArray array];
    // 为了方便操作, 估算球的大小,让其占据屏幕中间
    CGFloat bollWH = CGRectGetWidth(self.bounds) / (3.0 * (bollCount - 1));
    // 遍历为每个球设置其摆放位置
    for (int i = 0 ; i < bollCount; i++) {
        BollView *boll = [[BollView alloc]initWithFrame:CGRectMake(0, 0, bollWH-1, bollWH-1)];
        CGFloat x = CGRectGetWidth(self.bounds)/3.0 + i * bollWH;
        CGFloat y = CGRectGetHeight(self.bounds)/1.5;
      // 求出每个摆动球的中心位置.(方便于后面的监听,监听摆动球的中心点的变化)
        boll.center = CGPointMake(x, y);
        // 给球添加手势
        UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePanGesture:)];
        [boll addGestureRecognizer:panGesture];
      // 添加监听,用来监听摆动球中心位置的变化(方便重绘,如果连线是view的话,其位置的变化比较频繁,所以采用画的, 锚点的中心和摆动球的中心进行点的绘画)
        [boll addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:nil];
      // 将摆动球放置在数组中
        [bolls addObject:boll];
     // 添加到view上让其能够显示
        [self addSubview:boll];
      // 设置锚点
        UIView *blueBox = [self createAnchorsForBoll : boll];
       // 添加所有的锚点
        [anchors addObject:blueBox];
         [self addSubview:blueBox];
    }
    _anchors = anchors;
    _bolls = bolls;
}
// 创建对应的锚点view
- (UIView *)createAnchorsForBoll:(BollView *)boll
{
    UIView *blueBox = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 10, 10)];
    blueBox.backgroundColor = [UIColor blueColor];
    CGPoint point = boll.center;
    point.y -= CGRectGetHeight(self.bounds) / 4.0;
    blueBox.center = point;
    return blueBox;
}
// 手势方法,用于监听用户手势的拖动(开始拖动时添加拖拽动画,让摆动球能有一个拖拽动画,这个动画是独立的,应该在用户停止手势时,移除其拖拽动画,如果不移除的话,岂不是一直执行拖拽动画.还怎么去摆动)
- (void)handlePanGesture:(UIPanGestureRecognizer *) panGesture
{
// 通过对手势状态的判定,添加push动画
    if (panGesture.state == UIGestureRecognizerStateBegan) {
        // 如果已经存在,则移除原有的push动画
        if (_userDragBehavior) {
            [_animator removeBehavior:_userDragBehavior];
        }
// 创建push动画
        _userDragBehavior = [[UIPushBehavior alloc]initWithItems:@[panGesture.view] mode:UIPushBehaviorModeContinuous];
// 力度为1;
        _userDragBehavior.magnitude = 1;
        [_animator addBehavior:_userDragBehavior];
    }
// push方向,拖拽方向,默认让其只能执行x方向的,如果是y方向的话就没有意义了,不过大家可以试试
    _userDragBehavior.pushDirection = CGVectorMake([panGesture translationInView:self].x, 0);
// 根据手势状态, 来移除拖拽动画(也就是停止它)
    if (panGesture.state == UIGestureRecognizerStateEnded) {
        [_animator removeBehavior:_userDragBehavior];
        _userDragBehavior = nil;
    }
}
// 监听球的摆动,能够实时重绘连线,
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [self setNeedsDisplay];
}
// 绘画代码
- (void)drawRect:(CGRect)rect
{
// 开启上下文
    CGContextRef ctn = UIGraphicsGetCurrentContext();
// 遍历所有的摆动球   
 for (id<UIDynamicItem> boll in _bolls) {
// 拿到所有锚点的中心位置        
CGPoint anchorCenter = [[_anchors objectAtIndex:[_bolls indexOfObject:boll]] center];
// 拿到所有球的中心位置
        CGPoint bollCenter = [boll center];
//设置路径
        UIBezierPath *path = [UIBezierPath bezierPath];
// 从锚点中心    
    [path moveToPoint:anchorCenter];
// 到摆动球中心        
[path addLineToPoint:bollCenter];
// 设置颜色      
  [[UIColor blackColor] setStroke];
// 添加路经      
  CGContextAddPath(ctn, path.CGPath);     
// 将路径画在上下文中        
CGContextStrokePath(ctn); 
    }
}
// dealloc 方法中移除监听
- (void)dealloc
{
    for (BollView *boll in _bolls) {
        [boll removeObserver:self forKeyPath:@"center"];
    }
}

添加动画代码

#pragma mark applyDynamicBehaviors
// 添加所有动画代码
- (void)applyDynamicBehaviors
{
    // 添加UIDynamic的动力行为,同时把多个动力行为组合为一个复杂的动力行为
// UIDynamicBehavior抽象类,主要是组合各种动画
    UIDynamicBehavior *behavior = [[UIDynamicBehavior alloc]init];
    // 为球与点之间添加连带动画(附属)(添加后就会在重力行为绕着锚点运动)
    [self applyAttachBehaviorForBall:behavior];
    // 每个球加入重力动画
    [behavior addChildBehavior:[self creatGravityBehaviorForObjects:_bolls]];
// 每个球加入 碰撞动画
    [behavior addChildBehavior:[self createCollisionBehavior:_bolls]];
// 每个球加入一系列的
    [behavior addChildBehavior:[self createItemBehavior]];
    _animator = [[UIDynamicAnimator alloc]initWithReferenceView:self];
    [_animator addBehavior:behavior];
}
// 为每一组(球和点)附带动画 (球绕着点的摆动)
- (void)applyAttachBehaviorForBall:(UIDynamicBehavior *)behavior
{
    for (int i = 0; i < bollCount; i ++) {
        UIDynamicBehavior *attachmentBehavior = [self creatAttachmentBehaviorForBall:[_bolls objectAtIndex:i] toAnchor:[_anchors objectAtIndex:i]];
        [behavior addChildBehavior:attachmentBehavior];
    }
}
- (UIDynamicBehavior *)creatAttachmentBehaviorForBall:(id<UIDynamicItem>)boll toAnchor:(id<UIDynamicItem>) anchor
{

    // 附属动画
    // UIAttachmentBehavior 描述一个view和一个锚相连接的情况,也可以描述view和view之间的连接。attachment描述的是两个点之间的连接情况,可以通过设置来模拟无形变或者弹性形变的情况(再次希望你还记得这些概念,简单说就是木棒连接和弹簧连接两个物体)。当然,在多个物体间设定多个;UIAttachmentBehavior,就可以模拟多物体连接了..有了这些,似乎可以做个老鹰捉小鸡的游戏了- -… (它和重力动画联系到一起,就有点摆动的意思)
    UIAttachmentBehavior *attachmentBehavior = [[UIAttachmentBehavior alloc]initWithItem:boll attachedToAnchor:[anchor center]]; 
    return attachmentBehavior;
}
// 添加重力动画
- (UIDynamicBehavior *)creatGravityBehaviorForObjects:(NSArray *)objects
{
    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc]initWithItems:objects];
    
    return gravityBehavior;  
}
// 添加碰撞动画
- (UIDynamicBehavior *)createCollisionBehavior:(NSArray *)objects
{
    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc]initWithItems:objects];
    return collisionBehavior;
}
// 添加额外动画
- (UIDynamicBehavior *)createItemBehavior
{
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc]initWithItems:_bolls];
    itemBehavior.elasticity = 1.0;
    itemBehavior.allowsRotation = NO;
    itemBehavior.resistance = 0;
    return itemBehavior;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 4,992评论 5 13
  • UIKit动力学最大的特点是将现实世界动力驱动的动画引入了UIKit,比如重力,铰链连接,碰撞,悬挂等效果,即将2...
    BarleyZ阅读 1,223评论 0 49
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,318评论 6 30
  • 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你...
    被吹落的风阅读 1,498评论 1 2
  • 如果李安开吃播账号,你会关注吗? 如果李安不是导演,那他也一定是粉丝众多的吃播博主了。 在李安的“家庭三部曲”(《...
    约克镇的疯子阅读 553评论 2 6