UIDynamic物理引擎

概述


最近群里有人私信我关于iOS物理引擎的知识,虽然UIDynamic在iOS7就引入了,但项目中还真没用到过,就简单研究了下。由于本demo很简单,就没有上传GitHub,想要源码的可以进文章底部的技术群获取。

详细


一、基本知识

UIDynamic可以为继承UIView的控件添加物理行为。可以看下这些API
  • Dynamic Animator 动画者,为动力学元素提供物理学相关的能力及动画,同时为这些元素提供相关的上下文,是动力学元素与底层iOS物理引擎之间的中介,将Behavior对象添加到Animator即可实现动力仿真

  • Dynamic Animator Item:动力学元素,是任何遵守了UIDynamic协议的对象,从iOS7开始,UIView和UICollectionViewLayoutAttributes默认实现协议,如果自定义对象实现了该协议,即可通过Dynamic Animator实现物理仿真。

  • UIDynamicBehavior:仿真行为,是动力学行为的父类,基本的动力学行为类包括:

    • UIGravityBehavior 重力行为
    • UICollisionBehavior 碰撞行为
    • UIAttachmentBehavior 吸附行为
    • UISnapBehavior 迅猛移动弹跳摆动行为
    • UIPushBehavior 推动行为
具体实现步骤:

1、创建一个仿真者[UIDynamicAnimator] 用来仿真所有的物理行为
2、创建具体的物理仿真行为[如重力UIGravityBehavior]
3、将物理仿真行为添加给仿真者实现仿真效果。

二、单行为效果

我这里简单写几个行为事例,其他创建方法基本和这一样,可以自己尝试:

1、重力效果:

// 创建一个仿真者[UIDynamicAnimator] 用来仿真物理行为
  UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

  // 创建重力的物理仿真行为,并设置具体的items(需要仿真的view)
  UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[view]];

  // 将重力仿真行为添加给仿真者实现仿真效果,开始仿真
  [animator addBehavior:gravity];

具体效果:

Untitled.gif

2、碰撞效果:

是不是很简单,咱们再来一个碰撞效果:

// 碰撞检测
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[view]];
// 设置不要出边界,碰到边界会被反弹
collision.translatesReferenceBoundsIntoBoundary = YES;
// 开始仿真
[animator addBehavior:collision];

具体效果:

碰撞效果

3、摆动效果:

// 创建震动行为,snapPoint是它的作用点
    self.snap = [[UISnapBehavior alloc] initWithItem:view snapToPoint:view.center];

// 开始仿真
[animator addBehavior:collision];

具体效果:

摆动效果

单效果创建都差不多,这里只写几个简单的,其他的可以看我参考的这篇文章 //www.greatytc.com/p/e096d2dda478

三、组合行为效果

把单效果稍微组合一下:

1、重力加碰撞:

// 创建一个仿真者[UIDynamicAnimator] 用来仿真物理行为
  UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];

  // 创建重力的物理仿真行为,并设置具体的items(需要仿真的view)
  _gravity = [[UIGravityBehavior alloc] init];
  _collision = [[UICollisionBehavior alloc] init];
  _collision.translatesReferenceBoundsIntoBoundary = YES;

  // 将重力仿真行为添加给仿真者实现仿真效果,开始仿真
  [self.animator addBehavior:_gravity];
  [self.animator addBehavior:_collision];

   // 为view添加重力效果
   [self.gravity addItem:view];
   // 为view添加碰撞效果
   [self.collision addItem:view];

具体效果:

重力加碰撞

2、重力加弹跳:


    // 动态媒介
    UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    [self.animators addObject:animator];
    // 重力
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[square]];
    [animator addBehavior:gravity];
    
    // 碰撞
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[square]];
    collision.collisionDelegate = self;
    [collision addBoundaryWithIdentifier:@"barrier" forPath:[UIBezierPath bezierPathWithRect:self.view.bounds]];
    collision.translatesReferenceBoundsIntoBoundary = YES;
    [animator addBehavior:collision];
    
    // 动力学属性
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[square]];
    itemBehavior.elasticity = 1;
    [animator addBehavior:itemBehavior];

具体效果:

重力加弹跳,酷炫吧?

四、大厂用到的实际效果

一些大厂在利用这些效果,比如苹果的iMessage消息滚动视觉差效果、百度外卖重力感应(这个用到了重力感应)、摩拜单车贴纸效果,接下来咱们就逐个实现一下这些效果:

1、防iMessage滚动效果:

这里参考了著名开发者王维@onevcat重的一篇文章


// 自定义UICollectionViewFlowLayout
@interface WZBCollectionViewLayout : UICollectionViewFlowLayout

// 重写prepareLayout方法
- (void)prepareLayout
{
    [super prepareLayout];
    
    if (!_animator) {
        // 创建一个仿真者[UIDynamicAnimator] 用来仿真物理行为
        _animator = [[UIDynamicAnimator alloc] initWithCollectionViewLayout:self];
        CGSize contentSize = [self collectionViewContentSize];
        NSArray *items = [super layoutAttributesForElementsInRect:CGRectMake(0, 0, contentSize.width, contentSize.height)];
        for (UICollectionViewLayoutAttributes *item in items) {
            // 创建一个吸附行为
            UIAttachmentBehavior *spring = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:item.center];
            spring.length = 0;
            spring.damping = .8;
            spring.frequency = .5;
            [_animator addBehavior:spring];
        }
    }
}
    
// 重写这个方法刷新布局
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    UIScrollView *scrollView = self.collectionView;
    CGFloat scrollDeltaY = newBounds.origin.y - scrollView.bounds.origin.y;
    CGFloat scrollDeltaX = newBounds.origin.x - scrollView.bounds.origin.x;
    CGPoint touchLocation = [scrollView.panGestureRecognizer locationInView:scrollView];
    for (UIAttachmentBehavior *spring in _animator.behaviors) {
        CGPoint anchorPoint = spring.anchorPoint;
        CGFloat distanceFromTouch = fabs(touchLocation.y - anchorPoint.y);
        CGFloat scrollResistance = distanceFromTouch / 2000;
        UICollectionViewLayoutAttributes *item = (id)[spring.items firstObject];
        CGPoint center = item.center;
        center.y += (scrollDeltaY > 0) ? MIN(scrollDeltaY, scrollDeltaY * scrollResistance)
        : MAX(scrollDeltaY, scrollDeltaY * scrollResistance);
        
        CGFloat distanceFromTouchX = fabs(touchLocation.x - anchorPoint.x);
        center.x += (scrollDeltaX > 0) ? MIN(scrollDeltaX, scrollDeltaX * distanceFromTouchX / 2000)
        : MAX(scrollDeltaX, scrollDeltaX * distanceFromTouchX / 2000);
        
        item.center = center;
        [_animator updateItemUsingCurrentState:item];
    }
    return NO;
}

具体效果:

防iMessage滚动效果

2、防摩拜单车贴纸效果:

// 这里需要创建一个监听运动的管理者用来监听重力,然后实时改变重力方向
        _motionManager = [[CMMotionManager alloc] init];
        
        // 设备状态更新帧率
        _motionManager.deviceMotionUpdateInterval = 0.01;
    // 创建view
    for (NSInteger i = 0; i < 40; i++) {
        UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MobikeTest"]];
        imageView.frame = CGRectMake(100, 0, 50, 50);
        imageView.layer.masksToBounds = YES;
        imageView.layer.cornerRadius = 25;
        [self.view addSubview:imageView];
        
        // 添加重力效果
        [self.gravity addItem:imageView];
        // 碰撞效果
        [self.collision addItem:imageView];
        self.dynamicItem.elasticity = .7;
        // 添加动力学属性
        [self.dynamicItem addItem:imageView];
    }
    
    // 开始监听
    [self.motionManager startDeviceMotionUpdatesToQueue:NSOperationQueue.mainQueue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
        // 设置重力方向
        self.gravity.gravityDirection = CGVectorMake(motion.gravity.x * 3, -motion.gravity.y * 3);
    }];

具体效果:

防摩拜单车贴纸效果

3、防百度外卖首页重力感应:

         // 这里需要创建一个监听运动的管理者用来监听重力方向,然后实时改变UI
        _motionManager = [[CMMotionManager alloc] init];
        // 加速计更新频率,我这里设置每隔0.06s更新一次,也就是说,每隔0.06s会调用一次下边这个监听的block
    self.motionManager.accelerometerUpdateInterval = 0.06;
       // 开始监听
    [self.motionManager startAccelerometerUpdatesToQueue:NSOperationQueue.mainQueue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
        // 获取加速计在x方向上的加速度
        CGFloat x = accelerometerData.acceleration.x;
        
        // collectionView的偏移量
        CGFloat offSetX = self.collectionView.contentOffset.x;
        CGFloat offSetY = self.collectionView.contentOffset.y;
        
        // 动态修改偏移量
        offSetX -= 15 * x;
        CGFloat maxOffset = self.collectionView.contentSize.width + 15 - self.view.frame.size.width;
        
        // 判断最大和最小的偏移量
        if (offSetX > maxOffset) {
            offSetX = maxOffset;
        } else if (offSetX < -15) {
            offSetX = -15;
        }
        
        // 动画修改collectionView的偏移量
        [UIView animateWithDuration:0.06 animations:^{
            [self.collectionView setContentOffset:CGPointMake(offSetX, offSetY) animated:NO];
        }];
    }];

具体效果:

防百度外卖首页重力感应

总结


  • 本篇文章参考了
  • 最后几个效果图有点失真,具体效果可以找我要demo看
  • 最近急着招人,平时能挤出的时间也不多,所以这篇文章写的比较粗糙,有任何疑问都可以私信我
  • 喜欢就点个赞吧

我的更多文章:老司机带你飞

请不要吝惜,随手点个喜欢或者关注一下吧!您的支持是我最大的动力😊!
您可以关注我以便及时查看我的最新文章,如果您对本篇文章有任何疑问,请随时私信我。

码字实在不易,可以请我喝杯咖啡嘛~ 期待的搓搓小手❤️


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

推荐阅读更多精彩内容

  • 概念介绍 UIDynamic从ios7才开始有的,其他2D仿真引擎:BOX2D:C语言框架,免费Chipmunk:...
    我是滕先生阅读 2,252评论 5 23
  • 本文中所有代码演示均有GitHub源码,点击下载 UIDynamic简介 简介:UIKit动力学最大的特点是将现实...
    si1ence阅读 10,223评论 8 79
  • 最近产品提了个需求(电商的APP-两鲜),需要在APP背景加上几个水果图案在那里无规则缓慢游荡。。。模仿 天天果园...
    Yan青天阅读 2,525评论 5 18
  • ————————————【 iOS—UIDynamic 】————————————— 一、简单介绍 1.什么是UI...
    北辰青阅读 1,624评论 0 1
  • 古典老师在《你的身份,被什么决定》中,说到现在新职业价值体系是由圈子+能力+特色组成的,未来职业中只有这三种人:媒...
    smallfen阅读 475评论 0 0