天天看點

iOS7—Day by day—Day0:UIKit Dynamics

這篇文章是天天品嘗iOS7甜點系列的一部分,你可以檢視完整的系列目錄:天天品嘗iOS7甜點

伴随着介紹蘋果的iOS7,将會使你清晰的認清裝置和現實世界的互相作用,UIKit Dynamics就是衆多新API中的一個, UIKit Dyanmics是UIKit下的一個二維的實體引擎,在今天的文章裡面,我們就介紹一下UIKit Dynamics并且建構一個牛頓模拟重力實驗。

本章的執行個體程式能夠在github上面進行通路,通路位址:github.com/ShinobiControls/iOS7-day-by-day

實體世界

為了模拟現實世界中的實體效果,我們使用

UIDynamicBehavior

的子類來确定對象(這些對象都實作了

UIDynamicItem

協定)的不同行為。一些行為的例子包含:重力作用、碰撞作用和彈性作用。當然了實作了

UIDynamicItem

的對象也可以建立自己獨特的行為,極其重要的

UIView

就已經這麼做了。

UIDynamicBehavior

對象可以融合在一起生成一個新的綜合行為的對象,它包含所有的行為對于一個給定的對象或一組對象。

一旦我們指定了動态對象的行為,我們就可以為他們提供一個

UIDynamicAnimator

執行個體(也就是實體引擎自身).它就可以自己運作根據不同的行為來計算不同對象間的互相影響。下圖用來展示UIKit Dynamics世界中的概念圖:

iOS7—Day by day—Day0:UIKit Dynamics

建構一個擺鐘

我們回憶到大學的實驗,最簡單的牛頓實體實驗就是一個擺鐘。我們建立一個

UIView

來代表一個鐘擺球:

1
2
3
4
5
6
7
      
UIView *ballBearing = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
ballBearing.backgroundColor = [UIColor lightGrayColor];
ballBearing.layer.cornerRadius = 10;
ballBearing.layer.borderColor = [UIColor grayColor].CGColor;
ballBearing.layer.borderWidth = 2;
ballBearing.center = CGPointMake(200, 300);
[self.view addSubview:ballBearing];

           

現在我們可以為鐘擺球添加一些行為,我們将會建立一個複合的行為集合(包含了很多基本行為)

UIDynamicBehavior *behavior = [[UIDynamicBehavior alloc] init];

           

接下來我們将開始添加我們需要模拟的行為,首先是重力行為:

1
2
3
      
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[ballBearing]];
gravity.magnitude = 10;
[behavior addChildBehavior:gravity];

           

UIGravityBehavior

代表了對象本身和地球之間的重力吸引力,它有一些屬性允許你設定矢量的萬有引力(就是重力系數

magnitude

和方向

direction

),我們這裡增加了重力系數,但是保持重力的方向在y軸上面。

另外一個連結物的行為(attachment behavior)需要添加到鐘擺球上面,它代表了繩子對它的牽引作用:

1
2
3
4
      
CGPoint anchor = ballBearing.center;
anchor.y -= 20;
UIAttachmentBehavior *attachment = [[UIAttachmentBeahavior alloc] initWithItem:ballBearing attachedToAnchor:anchor];
[behavior addChildBehavior:attachment];

           

UIAttachmentBehavior

執行個體伴随一個動态的對象(一個錨點或者另外的對象),它們有具體的屬性來控制繩子對它的行為 – 具體就是它的頻繁性、阻尼和長度。預設的設定就是一個完全剛性的連接配接物(沒有任何長度伸縮彈性的物體),剛性的連接配接物正是我們的鐘擺球所需要的。

現在我們已經為鐘擺球指定了一些行為了,此時我們建立實體引擎來控制這些行為,是以我們定了一個類的全局變量(iVar):

UIDynamicAnimator *_animator

;

1
2
      
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[_animator addBehavior:behavior];

           

UIDynamicAnimator

代表了模拟動态系統的實體引擎,在這裡我們建立了它,并且指定了它參照的view(既指定宇宙空間),也添加了我們建立的行為.

到此為止,我們已經建立了第一個UIKit Dynamics系統,然而,如果我們運作app,沒有發生我們所期望的,這是因為此時app是在靜态的裝置中,我們需要讓我們的裝置運動起來就可以看到view的動态行為了。

手勢響應行為

我們需要為鐘擺球添加一個手勢操作,這樣可以通過手勢來擺弄鐘擺球模型:

1
2
      
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleBallBearingPan:)];
[ballBearing addGestureRecognizer:gesture];

           

下面來實作手勢的具體行為操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
      
- (void)handleBallBearingPan:(UIPanGestureRecognizer *)recognizer {
  // If we're starting the gesture then create a drag force
  if (recognizer.state == UIGestureRecognizerStateBegan) {
      if (_userDragBehavior) {
          [_animator removeBehavior:_userDragBehavior];
      }
      
      _userDragBehavior = [[UIPushBehavior alloc] initWithItems:@[recognizer.view] mode:UIPushBehaviorModeContinuous];
      [_animator addBehavior:_userDragBehavior];
  }
  
  // Set the force to be proportional to distance the gesture has moved
  _userDragBehavior.pushDirection = CGVectorMake([recognizer translationInView:self].x / 10.f, 0);
  
  // If we're finishing then cancel the behavior to 'let-go' of the ball
  if (recognizer.state = UIGestureRecognizerStateEnded) {
  [_animator removeBehavior:_userDragBehavior];
  _userDragBehavior = nil;
  }
}

           

UIPushBehavior

代表一個簡單的線性力量作用于對象上面。當力消失的時候,我們就回收作用在鐘擺球上面的力,我們定義了一個類全局變量(iVar) 

UIPushBehavior *_userDragBehvior

,這個變量可是在手勢觸發開始的時候指派,需要記得把這個行為添加到dynamics animator中, 我們力的大小與水準位移成正比, 為了讓鐘擺球搖擺,我們需要在手勢結束的時候删除掉push行為。

多個組合的鐘擺球

下圖就是牛頓鐘擺球的一個模型:

iOS7—Day by day—Day0:UIKit Dynamics

為了在創造這些使用

UIKit Dynamics

,我們需要建立多個鐘擺球,具體的方法可以參照上面所實作的,它們放置的控件需要留有一點空隙(具體的實作參見代碼中詳細).

另外我們需要添加一個新的行為來描述兩個鐘擺球的碰撞,我們有一個類的全局變量儲存在一個數組中

NSArray *_ballBearings;

:

1
2
      
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithObjects:_ballBearings];
[behavior addChildBehavior:collision];

           

這裡我們使用了一個碰撞行為作用在模拟系統中的一些對象上面,碰撞行為還可以用于模型對象達到邊界如視圖邊界,或任意的貝塞爾曲線路徑的界限。

如果我們現在運作app,并且移動一個鐘擺球,你将會發現鐘擺并不像我們認為的方式展示,這是因為碰撞不是彈性的,我們需要為行為添加一個指定的類型來指定各種共享的屬性:

1
2
3
4
5
6
      
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:_ballBearings];
// Elasticity governs the efficiency of the collisions
itemBehavior.elasticity = 1.0;
itemBehavior.allowsRotation = NO;
itemBehavior.resistance = 0.5;
[behavior addChildBehavior:itemBehavior];

           

我們使用

UIDynamicItemBehavior

來指定碰撞的彈性,同時也設定了其他的屬性例如resistance(阻尼,空氣産生的阻尼)和rotation(選擇),如果我們運作旋轉,我們可以指定角阻力, dynamic item 行為也可以設定線性和角速度,把它們添加到手勢操作上面是十分有用的。

再次運作app,我們會發現,此時的行為就和我們期望的一樣。也許你需要自己添加一些連接配接線

iOS7—Day by day—Day0:UIKit Dynamics

至此,代碼部分就完成了,使用所有介紹的元素,可以一步到demo app中檢視完整的代碼.

總結

這篇文章對于UIKit Dynamics的介紹還隻是停留在表面上,但是通過這裡介紹的東西可以模拟出更加複雜的實體系統,這隻是入門前的開胃菜而已,通過這個我們可以了解現實實體系統中的動作和互相作用。

本文翻譯自:iOS7 Day-by-Day :: Day 0 :: UIKit Dynamics