1. 什麼是UIDynamic
UIDynamic是從iOS 7開始引入的一種新技術,隸屬於UIKit框架 可以認爲是一種物理引擎,能模擬和仿真物理現象 如:重力、彈性碰撞等現象
注意:UIKit動力學的引入,並不是爲了替代CA或者UIView動畫,在絕大多數情況下CA或 者UIView動畫仍然是最優方案,只有在需要引入逼真的交互設計的時候,才需要使用 UIKit動力學它是作爲現有交互設計和實現的一種補充
2. 物理引擎的價值 廣泛用於遊戲開發,經典成功案例是“憤怒的小鳥” 讓開發人員可以在遠離物理學公式的情況下,實現炫酷的物理仿真效果 提高了遊戲開發效率,產生更多優秀好玩的物理仿真遊戲
4. 相關概念
a. 誰要進行物理仿真?(要誰做)
物理仿真元素(Dynamic Item),是任何遵守了UIDynamicItem協議的對象
b. 執行怎樣的物理仿真效果?怎樣的動畫效果?(做什麼動畫)
物理仿真行爲(DynamicBehavior) 仿真行爲,是動力學行爲的父類
基本的動力學行爲類 UIGravityBehavior、UICollisionBehavior、UIAttachmentBehavior、UISnapBehavior、UIPushBehavior以及UIDynamicItemBehavior均繼承自該父類,可以組合使用
c. 讓物理仿真元素執行具體的物理仿真行爲(開始做) 物理仿真器(Dynamic Animator)
爲動力學元素提供物理學相關的能力及動畫,同時爲這些元素提供相關的上下文,是動力學元素與底層iOS物理引擎之間的中介,將Behavior對象添加到 Animator即可實現動力仿真
d. 注意不是任何對象都可以做物理仿真效果 物理仿真元素要素:
任何遵守了UIDynamicItem協議的對象 UIView默認已經遵守了UIDynamicItem協議,因此任何UI控件都能做物理仿真 UICollectionViewLayoutAttributes類默認也遵守UIDynamicItem協議
二、相關類和使用步驟
1. 物理仿真元素(誰) UIDynamicItem
2. 物理仿真行爲(做什麼)
UIGravityBehavior:重力行爲 UICollisionBehavior:碰撞行爲(有代理) UISnapBehavior:捕捉行爲 UIPushBehavior:推動行爲 UIAttachmentBehavior:附着行爲 UIDynamic ItemBehavior:動力元素行爲
3. 物理仿真器(開始做) UIDynamicAnimator
4. 使用步驟
a. 創建一個UIDynamicAnimator對象
b. 創建行爲對象(UIDynamicBehavior)
c. 將要執行動畫的對象添加到UIDynamicBehavior中
一般會將 UIView 添加到行爲對象中 UIView 遵守了UIDynamicItem協議
三、物理仿真行爲 UIDynamic的物理仿真行爲只能針對矩形操作。不支持其它形狀
01-重力行爲(Gravity)
1. 重力行爲用於給動力學元素指定一個重力向量
2. 代碼示例
//1 創建物理仿真器
UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.redView];
self.animator = animator;
//2 創建重力行爲對象(物理行爲)
UIGravityBehavior *behavior = [[UIGravityBehavior alloc] initWithItems:@[self.redView]];
//量級(用來控制加速度,1.0 代表 加速度是1000 points/second2)
behavior.magnitude = 0.2;
//方向
behavior.gravityDirection = CGVectorMake(1, 1);
behavior.angle = 0;
//3 把物理行爲添加到物理仿真器中 開始動畫
[animator addBehavior:behavior];
02-碰撞行爲(Collision)
1. 相關屬性
translatesReferenceBoundsIntoBoundary
translatesReferenceBoundsIntoBoundary設置爲YES而不是明確的添加邊界的座標。這樣會使這
個邊界使用 UIDynamicAnimator 提供的參考系的邊界。
collisionMode
UICollisionBehaviorModeItems 碰到元素碰撞,邊界不碰撞
UICollisionBehaviorModeBoundaries 碰到邊界碰撞,元素不碰撞
UICollisionBehaviorModeEverything 默認,碰到邊界或元素會發生碰撞
2. 代碼示例
//1 創建物理仿真器,動畫的範圍
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
//2 創建物理仿真行爲
//2.1 重力行爲
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.redView]];
//2.2 碰撞行爲
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.redView]];
//2.3 碰撞的邊界,translatesReferenceBoundsIntoBoundary設置爲YES而不是明確的添加邊界的坐
//標。這樣會使這個邊界使用 UIDynamicAnimator 提供的參考系的邊界。
collision.translatesReferenceBoundsIntoBoundary = YES;
//3 把物理仿真行爲添加到物理仿真器中
[self.animator addBehavior:gravity];
[self.animator addBehavior:collision];
碰撞行爲-其它
1. 兩種自定義邊界的方式,設置直線
a. 添加邊界,設置兩個點
[collision addBoundaryWithIdentifier:@"b1" fromPoint:CGPointMake(0, 200) toPoint:CGPointMake(180, 250)];
b. 使用路徑的方式
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, 200)];
[path addLineToPoint:CGPointMake(180, 250)];
[collision addBoundaryWithIdentifier:@"b1" forPath:path];
2. 自定義邊界,設置矩形的邊界
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 200, 180, 20)];
[collision addBoundaryWithIdentifier:@"b2" forPath:path];
3. 碰撞過程中監聽frame的變化 [collision setAction:^{
NSLog(@"%@",NSStringFromCGRect(self.redView.frame)); }];
4. 碰撞行爲的彈力系數(0-1之間)
UIDynamicItemBehavior *item = [[UIDynamicItemBehavior alloc] initWithItems: @[self.redView]];
item.elasticity = 1;
添加到物理仿真器中
[self.animator addBehavior:item];
5. 設置碰撞行爲的代理,碰撞到邊界以後改變物體的狀態
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id<UIDynamicItem>)item withBoundaryIdentifier:(id<NSCopying>)identifier atPoint:(CGPoint)p {
//開始的碰撞物體
UIView *view = (UIView *)item;
//邊界的id
NSString *strId = (NSString *)identifier;
//當碰撞到黃色邊界以後改變當前物體的顏色
if ([strId isEqualToString:@"b2"]) {
[UIView animateWithDuration:0.3 animations:^{
view.backgroundColor = [UIColor blackColor];
} completion:^(BOOL finished) {
view.backgroundColor = [UIColor redColor]; }];
} }
四、捕捉(也叫吸附)行爲(Snap)
1. 代碼示例
//1 物理仿真器
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
//2 創建捕捉行爲
UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.redView snapToPoint:location]; //阻尼,減幅,衰減 取值(0-1)
snap.damping = 0;
//3 把行爲添加到物理仿真器
[self.animator addBehavior:snap];
五、附着行爲(Attachment)
1. 附着行爲描述一個視圖與一個錨點或者另一個視圖相連接的情況附着 行爲描述的是兩點之間的連接情況,可以模擬剛性或者彈性連接在多 個物體間設定多個UIAttachmentBehavior,可以模擬多物體連接
2. 演示附着行爲
3. 演示附着行爲-連線
4. 演示附着行爲-剛性行爲和彈性行爲
5. 演示附着行爲-和紅色的左上角連接
6. 屬性:
attachedBehaviorType:連接類型(連接到錨點或視圖) items:連接視圖數組
anchorPoint:連接錨點
length:距離連接錨點的距離
只要設置了以下兩個屬性,即爲彈性連接
damping:振幅大小
frequency:振動頻率
7. 示例代碼
//1 物理仿真器
self.animator = [[UIDynamicAnimator alloc]
initWithReferenceView:self.view];
//2 重力行爲
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:
@[self.redView]];
[gravity setAction:^{
view.startPoint = self.redView.center;
view.endPoint = point; }];
//2.1 添加附着行爲
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.redView attachedToAnchor:point];
//彈性行爲 (取值範圍:0---1)
attachment.frequency = 0.5;
attachment.damping = 0.5;
//3 把行爲添加到仿真器
[self.animator addBehavior:gravity];
[self.animator addBehavior:attachment];
六、推動行爲(Push)
1. 推行爲可以爲一個視圖施加一個作用力,該力可以是持續的,也可以是一次性的可以設置力的大小,方
向和作用點等信息
2. 屬性: mode:推動類型(一次性或是持續推) angle:推動角度
magnitude:推動力量
3. 演示推動行爲
4. 演示推動行爲-點擊界面推動
5. 示例代碼
//1 物理仿真器
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
//2 一次性推動行爲(持續性推動行爲)
UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:@[self.redView]
mode:UIPushBehaviorModeContinuous];
//
//設置推動的方向和推力的大小
push.angle = M_PI_2;
push.magnitude = 1;
//設置向量
push.pushDirection = CGVectorMake(0, 100);
//3 把推動行爲添加都物理仿真器
[self.animator addBehavior:push];
七、UIDynamicItemBehavior
1. UIDynamicItemBehavior:元素行爲
DynamicItem是一個輔助的行爲,用來設置運動學元素參與物理仿真過程中的參數,如:彈 性係數、摩擦係數、密度、阻力、角阻力以及是否允許旋轉等
elasticity(彈性係數):決定了碰撞的彈性程度,比如碰撞時物體的彈性
friction(摩擦係數) :決定了沿接觸面滑動時的摩擦力大小
density(密度): 跟size結合使用,計算物體的總質量。質量越大,物體加速或減速就越 困難
resistance(阻力):決定線性移動的阻力大小,與摩擦係數不同,摩擦係數只作用於滑動 運動
angularResistance(角阻力) :決定旋轉運動時的阻力大小
allowsRotation(允許旋轉):這個屬性很有意思,它在真實的物理世界沒有對應的模型。 設置這個屬性爲 NO 物體就完全不會轉動,而無論施加多大的轉動力
八、毛毛蟲案例
1. 搭建項目,實現以下樣子
a.創建毛毛蟲視圖 (用懶加載的方式創建 然後放到數組中)
//示例代碼
//懶加載蟲子
- (NSArray *)wormViews
{
if (_wormViews == nil)
{
// 1. 新建9個視圖
CGFloat startX = 20;
CGFloat startY = 80;
CGFloat r = 10;
NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:9];
for (NSInteger i = 0; i < 9; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(startX + i * 2 * r, startY, 2 * r, 2 * r)];
view.backgroundColor = [UIColor blueColor];
view.layer.cornerRadius = r;
if (i == 8) {
view.frame = CGRectMake(startX + i * 2 * r, startY - r, 4 * r, 4 * r);
view.layer.cornerRadius = 2 * r;
view.backgroundColor = [UIColor greenColor];
}
[self.view addSubview:view];
[arrayM addObject:view];
}
_wormViews = arrayM;
}
return _wormViews;
}
b.特殊處理蟲子頭部
//蟲子 頭
self.headView = self.wormViews.lastObject;
2. 添加物理仿真行爲
創建動畫着對象
//1.創建動畫者
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
a. 添加蟲子view的附着行爲
// 2.1 創建附着行爲
for (int i = 0; i < self.wormViews.count - 1; i++) {
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.wormViews[i] attachedToItem:self.wormViews[i + 1]];
[self.animator addBehavior:attachment];
}
b. 添加重力行爲
// 2.2 添加重力行爲
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:self.wormViews];
[self.animator addBehavior:gravity];
c. 添加碰撞行爲
// 2.3 添加碰撞行爲和邊界
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:self.wormViews];
collision.translatesReferenceBoundsIntoBoundary = YES;
[self.animator addBehavior:collision];
d.添加拖拽手勢事件 (也可以是觸摸事件)
// 拖動手勢
UIGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
- (void)pan:(UIPanGestureRecognizer *)recognizer
{
CGPoint location = [recognizer locationInView:self.view];
if (UIGestureRecognizerStateBegan == recognizer.state) {
// 實例化一個附加行爲,拖腦袋
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:self.headView attachedToAnchor:location];
[_animator addBehavior:attachment];
self.attachment = attachment;
} else if (UIGestureRecognizerStateChanged == recognizer.state) {
// 移動
self.attachment.anchorPoint = location;
} else if (UIGestureRecognizerStateEnded == recognizer.state) {
// 把蟲子扔地上
[self.animator removeBehavior:self.attachment];
}
}