天天品尝iOS7甜点 Day0:UIKit Dynamics

UIKit DyanmicsUIKit 下的一个二维的物理引擎。

参考:

关键的几个类:

UIDynamicBehavior 模拟对象的行为

UIDynamicAnimator 物理引擎

UIDynamicAnimator 自己可以根据不同的行为来计算不同对象间的相互影响。

使用方法总结:

  1. UIDynamicBehavior 的子类对象设置不同物体各自的行为或者它们通用的行为,然后将它们添加到 UIDynamicBehavior 实例对象上,
  2. UIDynamicBehavior 实例对象添加到 UIDynamicAnimator 物理引擎上。

Demo源码:牛顿摆钟

该 Demo 构建一个牛顿摆钟模拟重力实验。


SCBallBearingView

// **********************************************
#import <UIKit/UIKit.h>

/**
 圆形钟摆球模型
 */
@interface SCBallBearingView : UIView
@end

// **********************************************
#import "SCBallBearingView.h"
@import QuartzCore;

@implementation SCBallBearingView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // 初始化代码
        self.backgroundColor = [UIColor lightGrayColor];
        self.layer.cornerRadius = MIN(CGRectGetHeight(frame), CGRectGetWidth(frame)) / 2.0;
        self.layer.borderColor = [UIColor grayColor].CGColor;
        self.layer.borderWidth = 2;
    }
    return self;
}

@end

❇️ SCNewtonsCradleView

// **********************************************
#import <UIKit/UIKit.h>

@interface SCNewtonsCradleView : UIView
@end

// **********************************************
#import "SCNewtonsCradleView.h"
#import "SCBallBearingView.h"

@implementation SCNewtonsCradleView {
    NSArray *_ballBearings;             // 钟摆球容器
    UIDynamicAnimator *_animator;       // 用于控制行为的物理引擎
    UIPushBehavior *_userDragBehavior;  // 手势的线性力量
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // 初始化代码
        [self createBallBearings];
        [self applyDynamicBehaviors];
        
        // 初始化时施加推力让钟摆球摇摆
        _userDragBehavior = [[UIPushBehavior alloc] initWithItems:@[_ballBearings[0]] mode:UIPushBehaviorModeInstantaneous];
        _userDragBehavior.pushDirection = CGVectorMake(-0.5, 0);
        [_animator addBehavior:_userDragBehavior];
    }
    return self;
}

// 1.创建5个钟摆球
- (void)createBallBearings
{
    NSMutableArray *bbArray = [NSMutableArray array];
    NSUInteger numberBalls = 5;
    CGFloat ballSize = CGRectGetWidth(self.bounds) / (3.0 * (numberBalls - 1));
    
    for (NSUInteger i=0; i<numberBalls; i++) {
        // 钟摆球需要设置得足够小,使它们之间静态摆放时仍留有间隙。
        SCBallBearingView *bb = [[SCBallBearingView alloc] initWithFrame:CGRectMake(0, 0, ballSize - 1, ballSize - 1)];
        
        // 把每个钟摆球放到合适的位置
        CGFloat x = CGRectGetWidth(self.bounds) / 3.0 + i * ballSize;
        CGFloat y = CGRectGetHeight(self.bounds) / 2.0;
        bb.center = CGPointMake(x, y);
        
        // 为每个钟摆球添加一个手势操作,这样可以通过手势来摆弄钟摆球模型:
        UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleBallBearingPan:)];
        [bb addGestureRecognizer:gesture];
        
        // 把钟摆球添加进数组中,并添加为当前页面子视图
        [bbArray addObject:bb];
        [self addSubview:bb];
    }
    _ballBearings = [NSArray arrayWithArray:bbArray];
}

#pragma mark - UIDynamics utility methods
// 2.添加动力行为
- (void)applyDynamicBehaviors
{
    // 创建一个复合的行为集合 UIDynamicBehavior (可以包含很多基本行为)
    UIDynamicBehavior *behavior = [[UIDynamicBehavior alloc] init];
    
    // 将每个球轴承安装到其枢轴点
    for(id<UIDynamicItem> ballBearing in _ballBearings)
    {
        // 2.1 为每个钟摆球添加连结物行为
        UIDynamicBehavior *attachmentBehavior = [self createAttachmentBehaviorForBallBearing:ballBearing];
        [behavior addChildBehavior:attachmentBehavior];
    }
    
    // 2.2 添加重力行为
    [behavior addChildBehavior:[self createGravityBehaviorForObjects:_ballBearings]];
    
    // 2.3 添加碰撞行为
    [behavior addChildBehavior:[self createCollisionBehaviorForObjects:_ballBearings]];
    
    // 2.4 指定碰撞的弹性
    // 使用 UIDynamicItemBehavior 来指定碰撞的弹性。
    // dynamic item 行为也可以设置线性和角速度,把它们添加到手势操作上面是十分有用的。
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:_ballBearings];
    // 设置了其他的属性
    itemBehavior.elasticity = 1.0;    // 弹性
    itemBehavior.allowsRotation = NO; // rotation(旋转),运行旋转时,可以指定角阻力
    itemBehavior.resistance = 2.f;    // resistance(阻尼,空气产生的阻尼)
    [behavior addChildBehavior:itemBehavior];
    
    // 2.5 创建物理引擎动画
    // 创建物理引擎来控制这些行为,所以我们定了一个类的全局变量(iVar):UIDynamicAnimator *_animator;
    // UIDynamicAnimator代表了模拟动态系统的物理引擎,在这里我们创建了它,并且指定了它参照的view(既指定宇宙空间),也添加了我们创建的行为.
    _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self];
    // Add the composite behavior
    [_animator addBehavior:behavior];
}


// 连结物的行为(attachment behavior)需要添加到钟摆球上面,它代表了绳子对它的牵引作用:
- (UIDynamicBehavior *)createAttachmentBehaviorForBallBearing:(id<UIDynamicItem>)ballBearing
{
    // UIAttachmentBehavior 实例伴随一个动态的对象(一个锚点或者另外的对象),它们有具体的属性来控制绳子对它的行为 - 具体就是它的频繁性、阻尼和长度。默认的设置就是一个完全刚性的连接物(没有任何长度伸缩弹性的物体),刚性的连接物正是我们的钟摆球所需要的。
    
    CGPoint anchor = ballBearing.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;
    [self addSubview:blueBox];
    
    // 2.1 创建连结物行为
    UIAttachmentBehavior *behavior = [[UIAttachmentBehavior alloc] initWithItem:ballBearing
                                                               attachedToAnchor:anchor];
    return behavior;
}

- (UIDynamicBehavior *)createGravityBehaviorForObjects:(NSArray *)objects
{
    // 2.2 创建重力行为
    // UIGravityBehavior 代表了对象本身和地球之间的重力吸引力,它有一些属性允许你设置矢量的万有引力(就是重力系数magnitude和方向direction),我们这里增加了重力系数,但是保持重力的方向在y轴上面。
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:objects];
    gravity.magnitude = 10; // 设置重力系数
    return gravity;
}

- (UIDynamicBehavior *)createCollisionBehaviorForObjects:(NSArray *)objects
{
    // 2.3 创建碰撞行为
    // 将碰撞行为作用在模拟系统中的所有对象上面。
    // 碰撞行为还可以用于模型对象达到边界如视图边界,或任意的贝塞尔曲线路径的界限。
    return [[UICollisionBehavior alloc] initWithItems:objects];
}

#pragma mark - UIGestureRecognizer target method
// 实现手势的具体行为
// UIPushBehavior 代表一个简单的线性力量作用于对象上面。当力消失的时候,我们就回收作用在钟摆球上面的力,我们定义了一个类全局变量(iVar) UIPushBehavior *_userDragBehvior,这个变量可是在手势触发开始的时候赋值,需要记得把这个行为添加到dynamics animator中, 我们力的大小与水平位移成正比, 为了让钟摆球摇摆,我们需要在手势结束的时候删除掉push行为。

- (void)handleBallBearingPan:(UIPanGestureRecognizer *)recognizer
{
    // 拖动手势触发时,创建一个拖动力
    if (recognizer.state == UIGestureRecognizerStateBegan) {
        if(_userDragBehavior) {
            [_animator removeBehavior:_userDragBehavior];
        }
        // 将 UIPushBehavior 对象添加到 UIDynamicAnimator 物理引擎上
        _userDragBehavior = [[UIPushBehavior alloc] initWithItems:@[recognizer.view] mode:UIPushBehaviorModeContinuous];
        [_animator addBehavior:_userDragBehavior];
    }
    
    // 设置力的大小,与水平位移成正比
    _userDragBehavior.pushDirection = CGVectorMake([recognizer translationInView:self].x / 10.f, 0);
    
    // 拖动手势结束时,回收作用在钟摆球上面的力
    if (recognizer.state == UIGestureRecognizerStateEnded) {
        [_animator removeBehavior:_userDragBehavior];
        _userDragBehavior = nil;
    }
}

将牛顿钟摆球视图添加到主页:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _newtonsCradle = [[SCNewtonsCradleView alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:_newtonsCradle];
}

动画效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,104评论 4 62
  • iOS 7增加了UIKit Dynamics库,其集成于UIKit框架中,将2D物理引擎引入了UIKit,提供了以...
    pro648阅读 2,807评论 2 14
  • 一出门,就与微风撞个满怀。风中含着满是枇把树叶的香味。早晨,好清爽! 我抬头一看,竞不敢相信自...
    flyboy168阅读 623评论 3 2
  • 作为一个绘画小白,一直想画点什么,但总觉得自己缺乏这方面的天赋。其实小学时对绘画很有兴趣,无奈作品被打击,从此脆弱...
    Yumi玉米粒阅读 816评论 2 2
  • 一,词向量的概念将word映射到一个新的空间中,并以多维的连续实数向量进行表示叫做“Word Representi...
    MiracleJQ阅读 2,721评论 0 2