UIKit Dynamic基礎(chǔ)

前言

iOS7在視覺元素上去除了擬物化,UI偏向了扁平化風(fēng)格。但也從iOS7開始,iOS卻在“物理”上擬真了,這兒指的是交互動(dòng)畫。比如,它能模擬自由落體,碰撞反彈,吸附等和現(xiàn)實(shí)物理現(xiàn)象一致的交互效果。


基礎(chǔ)知識

  • ** UIDynamicAnimator:**動(dòng)力學(xué)容器。要實(shí)現(xiàn)動(dòng)力學(xué)動(dòng)畫,就必須有這個(gè)容器。它的初始化方法是- (instancetype)initWithReferenceView:(UIView *)view。其UIView類型的view參數(shù)表示view的子類UI才可有動(dòng)力學(xué)動(dòng)畫。
  • ** UIDynamicBehavior:**動(dòng)力學(xué)行為。這是一個(gè)基類,我們一般使用它的子類們來給視圖添加不同的行為。包括:重力行為(UIGravityBehavior)、碰撞行為(UICollisionBehavior)、吸附行為(UIAttachmentBehavior)、瞬移行為(UISnapBehavior)、推力行為(UIPushBehavior)、動(dòng)力項(xiàng)行為(UIDynamicItemBehavior)等。
    行為的初始化方法是- (instancetype)initWithItems:(NSArray<id <UIDynamicItem>> *)items
  • ** UIDynamicItem:**動(dòng)力項(xiàng),即要做動(dòng)畫的元素。最常見的項(xiàng)目是視圖,但任何遵從UIDynamicItem協(xié)議的動(dòng)力項(xiàng)都可以。該協(xié)議要求包含bounds、center、transform等屬性。
** 關(guān)于碰撞行為的邊界:**

translatesReferenceBoundsIntoBoundary將整個(gè)參照view(也就是self.view)的邊框作為碰撞邊界(另外你還可以使用setTranslatesReferenceBoundsIntoBoundaryWithInsets:這樣的方法來設(shè)定某一個(gè)區(qū)域作為碰撞邊界,更復(fù)雜的邊界可以使用addBoundaryWithIdentifier:forPath:來添加UIBezierPath,或者addBoundaryWithIdentifier:fromPoint:toPoint:來添加一條線段為邊界,詳細(xì)地還請查閱文檔)。

** 關(guān)于動(dòng)力項(xiàng)行為:**

動(dòng)力項(xiàng)行為(UIDynamicItemBehavior)和其他幾個(gè)內(nèi)置的行為不太一樣,它用于對動(dòng)力項(xiàng)賦物理屬性值。比如,密度,速度,阻力等。

** 需要注意的是,我們知道,對某動(dòng)力項(xiàng)移除普通動(dòng)力行為后,該動(dòng)力項(xiàng)目會立馬停止動(dòng)畫。比如,正在下落的ImgView移除重力行為后,并沒有繼續(xù)隨重力繼續(xù)下落,而是當(dāng)即停止下落。然而,若一個(gè)動(dòng)力項(xiàng)擁有UIDynamicItemBehavior行為,即便移除了重力行為,該動(dòng)力項(xiàng)還是會根據(jù)設(shè)置的物理屬性來進(jìn)行運(yùn)動(dòng)。在實(shí)踐中,若想讓我們的動(dòng)力項(xiàng)表現(xiàn)得更“真實(shí)自然”,給他們附加一個(gè)空的UIDynamicItemBehavior會很有幫助。**


代碼例子

下面代碼創(chuàng)建了一個(gè)UIImageView,并為其添加了一個(gè)拖動(dòng)手勢,拖動(dòng)手勢的事件回調(diào)是為imgView添加了一個(gè)“吸附行為”;并為imgView添加了“重力行為”和“碰撞行為”,當(dāng)拖動(dòng)松開時(shí),imgView便自由落體撞擊“地面”;最后還給self.view添加了單擊手勢,其事件回調(diào)是為imgView添加了一個(gè)“瞬移行為”,點(diǎn)到self.view的某點(diǎn),imgView就瞬移到該點(diǎn)。

** 注意:** 重力行為是沒有“地面”的,所以我們要給imgView添加一個(gè)碰撞行為,并設(shè)置屏幕底部為碰撞邊界來模擬落地撞擊地面。

gif01.gif
gif02.gif
#import "DynamicViewController.h"

@interface DynamicViewController ()<UICollisionBehaviorDelegate>
{
    UIImageView                 *_imgView;
    UIDynamicAnimator           *_dynamicAnimator;
    UIGravityBehavior           *_gravityBehavior;
    UICollisionBehavior         *_collisionBehavior;
    UIAttachmentBehavior        *_attachmentBehavior;
    UISnapBehavior              *_snapBehavior;
}
@end

@implementation DynamicViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.title = @"UIKit Dynamic";
    
    _imgView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    _imgView.image = [UIImage imageNamed:@"01.jpg"];
    _imgView.userInteractionEnabled = YES;
    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureHandle:)];
    [_imgView addGestureRecognizer:panGesture];
    [self.view addSubview:_imgView];
    
    
    _dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    // 添加重力行為
    _gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[_imgView]];
    [_dynamicAnimator addBehavior:_gravityBehavior];
    
    // 添加碰撞行為
    _collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_imgView]];
    _collisionBehavior.translatesReferenceBoundsIntoBoundary = YES; // 碰撞行為的邊界
    [_dynamicAnimator addBehavior:_collisionBehavior];
    _collisionBehavior.collisionDelegate = self;
    
    //
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandle:)];
    [self.view addGestureRecognizer:tapGesture];
}

- (void)panGestureHandle:(UIPanGestureRecognizer *)panGesture
{
    CGPoint location = [panGesture locationInView:self.view];
    CGPoint boxLocation = [panGesture locationInView:_imgView];
    UIOffset centerOffet = UIOffsetMake(boxLocation.x-CGRectGetMidX(_imgView.bounds), boxLocation.y-CGRectGetMidY(_imgView.bounds));
    
    if(panGesture.state == UIGestureRecognizerStateBegan)
    {
        // 添加追隨行為
        _attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:_imgView offsetFromCenter:centerOffet attachedToAnchor:location];
        _attachmentBehavior.damping = 0.1;
        _attachmentBehavior.frequency = 0.4;
        [_dynamicAnimator addBehavior:_attachmentBehavior];
    }
    else if(panGesture.state == UIGestureRecognizerStateChanged)
    {
        [_attachmentBehavior setAnchorPoint:location];
    }
    else if(panGesture.state == UIGestureRecognizerStateEnded)
    {
        [_dynamicAnimator removeBehavior:_attachmentBehavior];
    }
}


// 吸附在手勢點(diǎn)擊的某點(diǎn)
- (void)tapGestureHandle:(UITapGestureRecognizer *)tapGesture
{
    CGPoint location = [tapGesture locationInView:self.view];
    
    if(_snapBehavior!=nil){
        [_dynamicAnimator removeBehavior:_snapBehavior];
    }
    
    // 添加吸附行為
    _snapBehavior = [[UISnapBehavior alloc] initWithItem:_imgView snapToPoint:location];
    _snapBehavior.damping = 0.1;
    [_dynamicAnimator addBehavior:_snapBehavior];
}


@end


UIKit動(dòng)力學(xué)自定義

我們這里說的動(dòng)力學(xué)自定義是指在系統(tǒng)基礎(chǔ)上打包封裝,而不是完全自定義,完全自定義是很復(fù)雜的。
比如,我們要實(shí)現(xiàn)動(dòng)力項(xiàng)的“自由落體”,但內(nèi)置的重力行為是無“地面”的,也就是說要自己在添加個(gè)“邊界”來模擬地面,用碰撞行為來模擬“落地”。要完成一個(gè)完整的“自由落體”將要用到兩種動(dòng)力行為,我們可以將其封裝為一個(gè)自定義的行為。
** 我們封裝自己的類時(shí),盡量和原有類保持風(fēng)格一致。**

  • 自定義類繼承自基類UIDynamicBehavior;
  • 提供和原有類風(fēng)格一致的初始化方法initWithItems
  • 在定義類中創(chuàng)建內(nèi)置的動(dòng)力行為,然后添加為自己類的子動(dòng)力行為。[self addChildBehavior:gravityBehavior];

FreeFallBehavior.h

#import <UIKit/UIKit.h>

@interface FreeFallBehavior : UIDynamicBehavior


- (instancetype)initWithItems:(NSArray<id <UIDynamicItem>> *)items;


@end

FreeFallBehavior.m

#import "FreeFallBehavior.h"

@interface FreeFallBehavior ()

@end


@implementation FreeFallBehavior

- (instancetype)initWithItems:(NSArray<id <UIDynamicItem>> *)items
{
    if(self = [super init])
    {
        UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:items];
        [self addChildBehavior:gravityBehavior];
        
        UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:items];
        collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
        [self addChildBehavior:collisionBehavior];
    }
    
    return self;
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • iOS 7增加了UIKit Dynamics庫,其集成于UIKit框架中,將2D物理引擎引入了UIKit,提供了以...
    pro648閱讀 2,847評論 2 14
  • UIKit動(dòng)力學(xué)最大的特點(diǎn)是將現(xiàn)實(shí)世界動(dòng)力驅(qū)動(dòng)的動(dòng)畫引入了UIKit,比如重力,鉸鏈連接,碰撞,懸掛等效果,即將2...
    BarleyZ閱讀 1,304評論 0 49
  • 概念介紹 UIDynamic從ios7才開始有的,其他2D仿真引擎:BOX2D:C語言框架,免費(fèi)Chipmunk:...
    我是滕先生閱讀 2,265評論 5 23
  • 打游戲打的眼睛疼,人生吶
    何不去閱讀 165評論 0 0
  • 感恩分享:我們要認(rèn)識到,正在折磨他人那些痛苦的遭遇,是完全有可能也發(fā)生在我身上的,我是在對治全人類的痛苦這種想法...
    雪域紅梅閱讀 115評論 0 0