iOS UIDynamic物理效果

各种碰撞、吸附、点击展开等物理视觉效果大都是通过UIDynamic来实现的

直接上代码看各种效果:


#import "ViewController.h"

@interface ViewController ()<UICollisionBehaviorDelegate,UIGestureRecognizerDelegate>
@property (strong, nonatomic) UIView *squareView;
@property (strong, nonatomic) UIDynamicAnimator *animator;

@property (nonatomic, strong) UIAttachmentBehavior *attachmentB;//附着行为
@property (nonatomic, strong) NSMutableArray *itemsA;
@property (nonatomic, strong) UIGravityBehavior *gravityB;//重力行为

@property (assign, nonatomic) CGPoint curTickleStart;
@property (nonatomic, assign) CGPoint oldCenter;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    self.squareView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
    _squareView.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:self.squareView];
    self.oldCenter = _squareView.center;
    
    self.itemsA = [NSMutableArray arrayWithCapacity:0];
    for (int i = 0; i<5; i++) {
        UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(10 + i * 30, 100, 20, 20)];
        view1.backgroundColor = [UIColor colorWithRed:arc4random()% 255 / 255.0 green:arc4random()% 255 / 255.0 blue:arc4random()% 255 / 255.0 alpha:1.0];
        [self.view addSubview:view1];
        [_itemsA addObject:view1];
    }
    

    
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
//   NSArray *itemsArr = [self.animator itemsInRect:CGRectMake(0, 100, self.view.bounds.size.width, 50)];
//    - (NSArray*)itemsInRect:(CGRect)rect; //只有在添加行为后,才有值
    //- (void)removeBehavior:(UIDynamicBehavior *)behavior;
    //- (void)removeAllBehaviors;
    //- (void)addBehavior:(UIDynamicBehavior *)behavior;
    
    
    
    
    self.gravityB = [[UIGravityBehavior alloc] initWithItems:_itemsA]; // 创建一个重力行为
    // 在进行动画时的 block 回调 @property(nonatomic, copy) void (^action)(void)
//    _gravityB.action = ^{
//        NSLog(@"ing animation");
//    };
    _gravityB.gravityDirection = CGVectorMake(0.5, 0.8); // 在垂直向下方向 1000 点/平方秒 的速度 , 第一个参数是方向,第二个是速度
    [self.animator addBehavior:_gravityB];
    // 添加到该动态行为中的子动态行为 @property(nonatomic, readonly, copy) NSArray *childBehaviors
    //    gravity.childBehaviors;
    
    //  该动态行为相关联的dynamicAnimator
    //    @property(nonatomic, readonly) UIDynamicAnimator *dynamicAnimator
    
    //添加一个子动态行为
    //    - (void)addChildBehavior:(UIDynamicBehavior *)behavior
    
    // 移除一个子动态行为
    //    - (void)removeChildBehavior:(UIDynamicBehavior *)behavior
    
    // 当该动态行为将要被添加到一个UIDynamicAnimator中时,这个方法会被调用。
    //    - (void)willMoveToAnimator:(UIDynamicAnimator *)dynamicAnimator
    
    
    
    
    // 创建碰撞行为
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:_itemsA];
    
    // 指定 Reference view 的边界为可碰撞边界
    collision.translatesReferenceBoundsIntoBoundary = YES;
    collision.collisionDelegate = self;
    // UICollisionBehaviorModeItems:item 只会和别的 item 发生碰撞;UICollisionBehaviorModeBoundaries:item 只和碰撞边界进行碰撞;UICollisionBehaviorModeEverything:item 和 item 之间会发生碰撞,也会和指定的边界发生碰撞。
    collision.collisionMode = UICollisionBehaviorModeEverything;
    
    [self.animator addBehavior:collision];
    
    
    //UICollisionBehavior通过下面两个方法来添加碰撞边界,可以根据贝塞尔曲线或者一条直线生成碰撞边界。
    //    - (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath*)bezierPath;
    //    - (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;
    
    //UICollisionBehavior 里的 item 每次发生碰撞都可以通过 delegate 来监听事件。<UICollisionBehaviorDelegate>
    // item 与 item 之间开始碰撞。
    //    - (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2 atPoint:(CGPoint)p;
    //
    //    // item 与 item 之间结束碰撞。
    //    - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2;
    //
    //
    //    // item 和边界开始碰撞
    //    - (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier atPoint:(CGPoint)p;
    //
    //    // item 和边界结束碰撞
    //    - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier;
    
    
    
    
    
    
    //UIAttachmentBehavior 附着行为,让物体附着在某个点或另外一个物体上。可以设置附着点的到物体的距离,阻尼系数和振动频率等。
    /**
     / UIAttachmentBehaviorTypeAnchor类型的依赖行为的锚点,锚点与行为相关的动力动画的坐标系统有关。
     @property(readwrite, nonatomic) CGPoint anchorPoint
     
     // 吸附行为的类型
     @property(readonly, nonatomic) UIAttachmentBehaviorType attachedBehaviorType
     
     // 描述吸附行为减弱的阻力大小
     @property(readwrite, nonatomic) CGFloat damping
     
     // 吸附行为震荡的频率
     @property(readwrite, nonatomic) CGFloat frequency
     
     // 与吸附行为相连的动态项目,当吸附行为类型是UIAttachmentBehaviorTypeItems时有2个元素,当吸附行为类型是UIAttachmentBehaviorTypeAnchor时只有一个元素。
     @property(nonatomic, readonly, copy) NSArray *items
     
     // 吸附行为中的两个吸附点之间的距离,通常用这个属性来调整吸附的长度,可以创建吸附行为之后调用。系统基于你创建吸附行为的方法来自动初始化这个长度
     @property(readwrite, nonatomic) CGFloat length
     */
//    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.squareView attachedToAnchor:self.squareView.center];
//    attachment.length = 200;
//    attachment.damping = 0.5;
//    attachment.frequency = 1;
//    [self.animator addBehavior:attachment];
    
    
    
    
    
    
    //UIDynamicItemBehavior  物体属性,如密度、弹性系数、摩擦系数、阻力、转动阻力等。 接下来我们修改物体的物理属性,为了能看到这个效果,我们先删除 UIAttachmentBehavior 相关的代码,并在 - (void)viewDidAppear:(BOOL)animated 末尾添加如下代码:
    /**
     // 弹力,通常设置 0~1 之间
     @property (readwrite, nonatomic) CGFloat elasticity;
     
     // 摩擦力,0表示完全光滑无摩擦
     @property (readwrite, nonatomic) CGFloat friction;
     
     // 密度,一个 100x100 points(1 point 在 retina 屏幕上等于2像素,在普通屏幕上为1像素。)大小的物体,密度1.0,在上面施加 1.0 的力,会产生 100 point/平方秒 的加速度。
     @property (readwrite, nonatomic) CGFloat density;
     
     // 线性阻力,物体在移动过程中受到的阻力大小
     @property (readwrite, nonatomic) CGFloat resistance;
     
     // 旋转阻力,物体旋转过程中的阻力大小
     @property (readwrite, nonatomic) CGFloat angularResistance;
     
     // 是否允许旋转
     @property (readwrite, nonatomic) BOOL allowsRotation;
     */
    
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:_itemsA];
    itemBehavior.elasticity = 1; // 改变弹性
    itemBehavior.allowsRotation = YES; // 允许旋转
    [itemBehavior addAngularVelocity:1 forItem:self.squareView]; // 让物体旋转
    
    [self.animator addBehavior:itemBehavior];
    
    
    
    
    
    
    //UIPushBehavior 对物体施加力,可以是持续性的力也可以是一次性的力。用一个向量(CGVector)来表示力的方向和大小。
    //这次我们通过手势来动态的为物体添加推力,首先注释重力行为的相关代码,然后在 - (void)viewDidAppear:(BOOL)animated 末尾添加如下代码
    /**
     // 推力模式,UIPushBehaviorModeContinuous:持续型。UIPushBehaviorModeInstantaneous:一次性推力。
     @property (nonatomic, readonly) UIPushBehaviorMode mode;
     
     // 推力是否被激活,在激活状态下,物体才会受到推力效果
     @property(nonatomic, readwrite) BOOL active
     
     // 推力的大小和方向
     @property (readwrite, nonatomic) CGVector pushDirection;
     */
    
//    UITapGestureRecognizer *viewTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapViewHandler:)];
//    [self.view addGestureRecognizer:viewTapGesture];
    
    
    
    
    
    
    //UISnapBehavior 将一个物体钉在某一点。它只有一个初始化方法和一个属性。
    // 根据 item 和 point 来确定一个 item 要被定到哪个点上。
//    - (instancetype)initWithItem:(id <UIDynamicItem>)item snapToPoint:(CGPoint)point;
//    
//    // 减震系数,范围在0.0~1.0
//    @property (nonatomic, assign) CGFloat damping;
    
    
    
  //添加拖拽手势
        
        UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
        panGes.delegate = self;
        [self.squareView addGestureRecognizer:panGes];
   
    


}

- (void)tapViewHandler:(UITapGestureRecognizer *)gestureRecognizer
{
    UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:@[self.squareView] mode:UIPushBehaviorModeInstantaneous];
    CGPoint location = [gestureRecognizer locationInView:self.view];
    CGPoint itemCenter = self.squareView.center;
    push.pushDirection = CGVectorMake((location.x - itemCenter.x) / 100, (location.y - itemCenter.y) / 100);
    [self.animator addBehavior:push];
}

//拖拽手势
- (void) handlePan:(UIPanGestureRecognizer*) recognizer
{
    

        CGPoint translation = [recognizer translationInView:self.view];
        NSLog(@"wgj:%lf,%lf",translation.x,translation.y);
        NSLog(@"wgj001:%lf,%lf",recognizer.view.center.x,recognizer.view.center.y);
        recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
                                             recognizer.view.center.y + translation.y);
        [recognizer setTranslation:CGPointZero inView:self.view];//防止移动量累计增加
        
        CGPoint location = recognizer.view.center;
        CGFloat itemW = recognizer.view.bounds.size.width;
        CGFloat itemH = recognizer.view.bounds.size.height;
        CGFloat viewW = self.view.bounds.size.width;
        CGFloat viewH = self.view.bounds.size.height;
        
        if (recognizer.view.center.x < itemW * 0.5) {
            recognizer.view.center = CGPointMake(itemW * 0.5, location.y);
        }
        if (recognizer.view.center.y < itemH * 0.5) {
            recognizer.view.center = CGPointMake(location.x, itemH * 0.5);
        }
        if (recognizer.view.center.x > viewW - itemW * 0.5) {
            recognizer.view.center = CGPointMake(viewW - itemW * 0.5, location.y);
        }
        if (recognizer.view.center.y > viewH - itemH * 0.5) {
            recognizer.view.center = CGPointMake(location.x, viewH - itemH * 0.5);
        }
        

    
    if (recognizer.state == UIGestureRecognizerStateBegan) {
        
        [self.animator removeBehavior:self.attachmentB];
        //        [self.animator removeAllBehaviors];
        
    }
    
    
    if (recognizer.state == UIGestureRecognizerStateEnded) {
        
        [self.animator addBehavior:self.attachmentB];//添加吸附行为
    }
    
    
    //让小物体改变角度
    _gravityB.angle = 3;
    
//
//    if (recognizer.state == UIGestureRecognizerStateEnded) {
//        
//        CGPoint velocity = [recognizer velocityInView:self.view];
//        CGFloat magnitude = sqrtf((velocity.x * velocity.x) + (velocity.y * velocity.y));
//        CGFloat slideMult = magnitude / 200;
//        NSLog(@"magnitude: %f, slideMult: %f", magnitude, slideMult);
//        
//        float slideFactor = 0.1 * slideMult; // Increase for more of a slide
//        CGPoint finalPoint = CGPointMake(recognizer.view.center.x + (velocity.x * slideFactor),
//                                         recognizer.view.center.y + (velocity.y * slideFactor));
//        finalPoint.x = MIN(MAX(finalPoint.x, 0), self.view.bounds.size.width);
//        finalPoint.y = MIN(MAX(finalPoint.y, 0), self.view.bounds.size.height);
//        
//        [UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
//            recognizer.view.center = finalPoint;
//        } completion:nil];
//    }
    
}

- (UIAttachmentBehavior *)attachmentB
{
    if (_attachmentB == nil) {
        self.attachmentB = [[UIAttachmentBehavior alloc] initWithItem:self.squareView attachedToAnchor:CGPointMake(150, 200)];
        _attachmentB.length = 0;
        _attachmentB.damping = 0.6;//阻尼
        _attachmentB.frequency = 1.2; //震频

    }
    return _attachmentB;
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,266评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 虽然自己二十开头的生日都要过两个了,但觉得自己还是总会有不合时宜的幼稚和任性。最让我难过的是,即使到了现在,...
    嚯嚯杨活活阅读 125评论 0 0
  • 一起把日子过成诗,成长为更好的自己 还记得小时候晨读的场景吗?一群懵比的小孩儿们,或困顿或精神或饿着肚子或记挂着还...
    仰心的人生提案阅读 25,922评论 0 2