iOS 7增加了UIKit Dynamics庫,其集成于UIKit
框架中而克,將2D物理引擎引入了UIKit
,提供了以最簡單方式實現(xiàn)真實物理動畫功能怔毛。UIKit
動力學(xué)的引入员萍,并不是為了替代CoreAnimation
或UIView
動畫,絕大多數(shù)情況下拣度,CA
或UIView
動畫仍然是最優(yōu)方案碎绎,只有在需要引入逼真交互設(shè)計的時候,才需要使用UIKit
動力學(xué)抗果。如果你在開發(fā)電子游戲筋帖,那么應(yīng)該使用SpritKit
。
動力項(UIDynamicItem)是任何遵守UIDynamicItem
協(xié)議的對象冤馏,相當(dāng)于現(xiàn)實世界中的一個基本物體日麸。自iOS 7開始,UIView
和UICollectionViewLayoutAttributes
默認(rèn)實現(xiàn)了上述協(xié)議逮光,你也可以自行實現(xiàn)該協(xié)議以便在自定義的類中使用動力效果動畫(UIDynamicAnimator)代箭,但很少需要這樣做。
動力行為(UIDynamicBehavior)為動力項(UIDynamicItem)提供不同的2D物理動畫涕刚,即指定UIDynamicItem
應(yīng)該如何運(yùn)動嗡综、適用哪些物理規(guī)則。在這里UIDynamicBehavior
類似一個抽象類杜漠,沒有實現(xiàn)具體行為极景,因此一般使用這個類的子類來對一組UIDynamicItem
應(yīng)遵守的行為規(guī)則進(jìn)行描述。UIDynamicBehavior
可以獨立作用驾茴,多個動力行為同時作用時遵守力的合成戴陡。
UIKit Dynamics庫的核心在于UIDynamicAnimator
,其封裝了底層iOS物理引擎沟涨,是動力行為(UIDynamicBehavior)的容器恤批,動力行為添加到容器內(nèi)才會發(fā)揮作用,為動力項(UIDynamicItem)提供物理相關(guān)的功能和動畫裹赴。
當(dāng)所有item處于暫停時喜庞,
animator
自動暫停诀浪,并且當(dāng)動力行為參數(shù)改變、添加或移除動力行為或動力項時自動恢復(fù)延都。
使用動力學(xué)(dynamics)的步驟是:配置一個或多個UIDynamicBehavior
雷猪,其中為每個UIDynamicBehavior
指定一個或多個UIDynamicItem
,最后添加這些UIDynamicBehavior
到UIDynamicAnimator
晰房。
UIDynamciBehavior
有以下六個子類:
- UIGravityBehavior:重力行為求摇,重力的大小和方向可以配置,并會同等作用于所有關(guān)聯(lián)對象殊者。
- UICollisionBehavior:碰撞行為与境,提供兩個或多個視圖對象碰撞的能力,或視圖對象和邊界碰撞的能力猖吴。
- UIPushBehavior:對一個或多個動力項施加連續(xù)(continuous)或瞬時(instantaneous)力的行為摔刁,導(dǎo)致這些動力項改變位置。
- UIAttachmentBehavior:描述一個view和一個錨點相連接的情況海蔽,也可以描述view和view之間的連接情況共屈。
- UISnapBehavior:將view通過動畫吸附到某個點上。吸附的過程類似彈簧作用党窜,使動力項朝向目標(biāo)點甩出拗引,到達(dá)目標(biāo)點后其初始運(yùn)動隨著時間的推移而減弱,最終物體停止在目標(biāo)點幌衣。
- UIFieldBehavior:將基于場的物理學(xué)應(yīng)用于動力項對象寺擂。場的行為定義了可以應(yīng)用諸如磁力(magnetism)、拖拽(dragging)泼掠、電場(electric)怔软、漩渦(vortex)、輻射(radial)择镇、線性重力(linear gravity)挡逼、噪聲(noise)、渦流(turbulence)腻豌、彈簧場(SpringField)等力的區(qū)域家坎。
還有另外一個重要的類是UIDynamicItemBehavior
,用于修改指定動力項的以下屬性:
- allowsRotation:BOOL型值吝梅,用于指定
UIDynamicItem
是否旋轉(zhuǎn)虱疏。默認(rèn)值是YES
。 - angularResistance:旋轉(zhuǎn)阻力苏携,物體旋轉(zhuǎn)過程的阻力大小做瞪。值的范圍是
0
到CGFLOAT_MAX
,值越大,阻力越大装蓬。 - density:動力項的密度(density)和動力項大兄谩(size),決定了其參與
UIKit
動力行為(包括摩擦牍帚、碰撞儡遮、推動等)的表現(xiàn)。例如暗赶,假設(shè)有兩個相同密度但不同大小的動態(tài)項目鄙币,item1是100*100points,item2是100*200points蹂随,即item2的有效質(zhì)量是item1的二倍十嘿,在彈性碰撞時,item1和item2會展現(xiàn)出與自然界相同的動量守恒糙及。一個100p * 100p的動力項详幽,density
為1.0
筛欢,施加1.0
的力浸锨,會產(chǎn)生100points每平方秒的加速度。 - elasticity:彈力版姑。范圍是
0
到1.0
铡买,0
表示沒有彈性兔毒,1.0
表示完全彈性碰撞。默認(rèn)值為0
。 - friction:摩擦力菇晃。默認(rèn)值
0
表示沒有摩擦力,1.0
表示很強(qiáng)摩擦崎场。為了取得更大摩擦力埋泵,可以使用更大值。 - resistance:線性阻力么介。物體移動過程中受到的阻力娜遵。默認(rèn)值
0
表示沒有線性阻力,上限CGFLOAT_MAX
為完全阻力壤短。如果此屬性值為1.0
设拟,則動態(tài)項一旦沒有作用力會立即停止。 - anchored:BOOL型值久脯,指定動力項是否固定在當(dāng)前位置纳胧。被固定的動力項參與碰撞時不會被移動,而是像邊界一樣參與碰撞帘撰。默認(rèn)值是
NO
跑慕。
目前為止,對UIKit Dynamics的整體介紹已經(jīng)結(jié)束摧找,下一步將通過demo學(xué)習(xí)具體使用相赁。
1. 創(chuàng)建demo
和往常一樣相寇,將在創(chuàng)建的demo上添加代碼來學(xué)習(xí)UIKit Dynamics。
打開Xcode钮科,點擊File > New > File…唤衫,選擇iOS > Application > Single View Application模板,點擊Next绵脯;在Product Name中填寫UIKitDynamics
佳励,Language為Objective-C,點擊Next蛆挫;選擇文件位置赃承,點擊Create創(chuàng)建工程。
該demo將分為五個部分悴侵,為此把UITabBarController
設(shè)置為根控制器瞧剖。每一部分作為單獨視圖控制器添加到UITabBarController
。
選中ViewController.h
和ViewController.m
并刪除可免。使用快捷鍵command+N添加文件抓于,選擇iOS > Source > Cocoa Touch Class模板,點擊Next浇借;Class名稱為FirstViewController捉撮,Subclass of為UIViewController
,點擊Next妇垢;選擇文件位置巾遭,點擊Create創(chuàng)建文件。
重復(fù)上面添加文件步驟闯估,依次添加下面名稱文件:
- SecondViewController
- ThirdViewController
- FourthViewController
- FifthViewController
最后添加一個Class為TabBarController灼舍,Subclass of為UITabBarController
的文件。完成后Project Navigator 如下圖:
進(jìn)入AppDelegate.m
涨薪,導(dǎo)入TabBarController.h
骑素,在application: didFinishLaunchingWithOptions:
方法中設(shè)定根控制器為TabBarController
。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 設(shè)定根控制器
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[TabBarController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
進(jìn)入TabBarController.m
尤辱,導(dǎo)入之前添加的五個視圖控制器砂豌,并將其添加到TabBarController
,同時設(shè)定各視圖控制器標(biāo)題光督。
#import "TabBarController.h"
// 導(dǎo)入視圖控制器
#import "FirstViewController.h"
#import "SecondViewController.h"
#import "ThirdViewController.h"
#import "FourthViewController.h"
#import "FifthViewController.h"
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化視圖控制器 設(shè)定標(biāo)題 tabBarItem圖片
UIImage *image = [UIImage imageNamed:@"circle"];
FirstViewController *firstVC = [[FirstViewController alloc] init];
firstVC.title = @"First";
firstVC.tabBarItem.image = image;
SecondViewController *secVC = [[SecondViewController alloc] init];
secVC.title = @"Second";
secVC.tabBarItem.image = image;
ThirdViewController *thirdVC = [[ThirdViewController alloc] init];
thirdVC.title = @"Third";
thirdVC.tabBarItem.image = image;
FourthViewController *fourthVC = [[FourthViewController alloc] init];
fourthVC.title = @"Fourth";
fourthVC.tabBarItem.image = image;
FifthViewController *fifthVC = [[FifthViewController alloc] init];
fifthVC.title = @"Fifth";
fifthVC.tabBarItem.image = image;
// 設(shè)置標(biāo)簽欄控制器的根視圖
[self setViewControllers:@[firstVC, secVC, thirdVC, fourthVC, fifthVC] animated:YES];
}
上面的代碼非常簡單阳距,記得添加圖片到Assets.xcassets,可以通過文章底部鏈接下載源碼獲取结借。
2. 重力行為筐摘、碰撞行為之初體驗
2.1 重力行為 UIGravityBehavior
為了體現(xiàn)重力行為作用,我們在FirstViewController
添加一個圓,填充顏色以使其看起來像一個球咖熟。最后為其添加UIGravityBehavior
圃酵。
進(jìn)入FirstViewController.m
文件,在私有接口部分聲明以下屬性:
@interface FirstViewController ()
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UIView *orangeBall;
@end
第一個聲明的為UIDynamicAnimator
對象馍管。之前已經(jīng)說過郭赐,UIDynamicAnimator
自身不能工作,添加其他行為后才可以确沸,稍后添加重力行為捌锭。聲明的UIView
對象用于演示重力行為。
打開FirstViewController.m
罗捎,進(jìn)入viewDidLoad
方法中观谦,配置剛聲明的兩個屬性。
- (void)viewDidLoad {
[super viewDidLoad];
// 1.初始化桨菜、配置orangeBall
self.orangeBall = [[UIView alloc] initWithFrame:CGRectMake(self.view.bounds.size.width/2-25, 100, 50, 50)];
self.orangeBall.backgroundColor = [UIColor orangeColor];
self.orangeBall.layer.cornerRadius = 25;
[self.view addSubview:self.orangeBall];
// 2.初始化animator
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
}
上述代碼分步說明:
- 初始化豁状、配置
orangeBall
。圓形由self.orangeBall.layer.cornerRadius = 25;
代碼生成倒得。 - 初始化
animator
泻红。初始化時需要Reference View,相當(dāng)于力學(xué)參考系屎暇,只有當(dāng)想要添加力學(xué)的UIView
是Reference View的子視圖時承桥,UIDynamicAnimator
才發(fā)生作用驻粟。
在FirstViewController.m
底部添加以下方法根悼,并在其中初始化UIGravityBehavior
。
- (void)testGravity {
// 1.初始化重力行為
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.orangeBall]];
// 2.添加重力行為到UIDynamicAnimator
[self.animator addBehavior:gravity];
}
上述代碼分步說明:
- 初始化
UIGravityBehavior
蜀撑,它的參數(shù)為NSArray
類型挤巡,可以把所有需要添加重力作用的視圖添加到該數(shù)組。在這個示例中酷麦,只需要添加orangeBall
矿卑。 - 如果想要讓行為有效,必需添加行為到
UIDynamicAnimator
容器沃饶。使用animator
對象的addBehavior:
方法添加重力行為母廷。
進(jìn)入到viewDidLoad
方法,調(diào)用上述方法糊肤。
- (void)viewDidLoad {
...
// 調(diào)用testGravity方法
[self testGravity];
}
在模擬器運(yùn)行app琴昆,如下所示:
現(xiàn)在重力效果帶動orangeBall
向下跌落。事實上馆揉,orangeBall
在跌出可見區(qū)域后业舍,仍然在繼續(xù)下滑,下面進(jìn)行驗證。
UIDynamicBehavior
類有void(^action)(void)
塊屬性舷暮,其子類也會繼承該屬性态罪。animator
會在每步(step)動畫調(diào)用該塊,也就是action
中添加的代碼會在動力動畫運(yùn)行過程中不斷執(zhí)行下面。在testGravity
方法初始化graviy
后添加以下代碼:
- (void)testGravity {
...
gravity.action = ^{
NSLog(@"%f",self.orangeBall.center.y);
};
...
}
在上面的代碼中复颈,把塊賦值給重力行為gravity
的action
屬性,在塊中用NSLog
輸出orangeBall
中心的y坐標(biāo)沥割,用以表示其位置券膀。運(yùn)行app,可以在控制臺看到不斷輸出的數(shù)字驯遇,并且相鄰值的差不斷增大芹彬,在orangeBall
離開可見區(qū)域后,控制臺繼續(xù)輸出叉庐。事實上舒帮,此時orangeBall
正在做自由落體運(yùn)動。
如果對塊不熟悉陡叠,可以查看我的另一篇文章:Block的用法
2.2 碰撞行為 UICollisionBehavior
通過上面代碼玩郊,我們?yōu)?code>orangeBall添加了UIGravityBehavior
,但讓視圖在重力行為作用下無限下滑沒有意義枉阵。如果orangeView
在撞到tar bar頂部后開始彈跳译红,最終停止會更有用途,就像生活中的球跌落到地上兴溜。
UICollisionBehavior
可以實現(xiàn)碰撞功能侦厚。使用UICollisionBehavior
的步驟是初始化碰撞行為并制定碰撞邊界,添加碰撞行為到animator
拙徽。更新后代碼如下:
- (void)testGravity {
...
// 3.初始化碰撞行為刨沦、制定邊界
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.orangeBall]];
[collision addBoundaryWithIdentifier:@"tabbar" fromPoint:self.tabBarController.tabBar.frame.origin toPoint:CGPointMake(self.tabBarController.tabBar.frame.origin.x + self.tabBarController.tabBar.frame.size.width, self.tabBarController.tabBar.frame.origin.y)];
[self.animator addBehavior:collision];
}
在初始化時指定orangeBall
為UICollisionBehavior
的作用對象,使用addBoundaryWithIdentifier: fromPoint: toPoint:
方法添加tab bar上部為邊界膘怕。最后添加collision
到animator
想诅。
除上面的添加邊界方法外,還有以下三種添加邊界的方法:
-
translatesReferenceBoundsIntoBoundary
:BOOL類型值岛心,指定是否把reference view作為碰撞邊界来破。默認(rèn)為NO
。 -
addBoundaryWithIdentifier: forPath:
:添加指定Bezier Path作為碰撞邊界忘古。 -
setTranslatesReferenceBoundsIntoBoundaryWithInsets:
:設(shè)定某一區(qū)域作為碰撞邊界徘禁。
運(yùn)行demo,如下所示:
現(xiàn)在orangeBall
遇到邊界會彈跳存皂,不會直接穿過晌坤。我們在開始部分介紹到UIDynamicItemBehavior
用于修改動力項屬性逢艘,現(xiàn)在初始化一個UIDynamicItemBehavior
,修改其elasticity
屬性骤菠。更新后testGravity
方法如下:
- (void)testGravity {
...
// 4.初始化UIDynamicItemBehavior 修改elasticity
UIDynamicItemBehavior *ballBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.orangeBall]];
ballBehavior.elasticity = 0.7;
[self.animator addBehavior:ballBehavior];
}
elasticity
默認(rèn)值為0
它改,范圍是0
到1.0
,0
表示沒有彈性商乎,1.0
表示完全彈性碰撞央拖。這里把值設(shè)為0.7
。
運(yùn)行app鹉戚,顯示如下:
現(xiàn)在已經(jīng)了解了UIDynamicItemBehavior
的使用方法鲜戒,你可以嘗試一下其他屬性。
UICollisionBehavior
類遵守UICollisionBehaviorDelegate
協(xié)議抹凳,該協(xié)議內(nèi)有以下方法:
-
collisionBehavior: beganContactForItem: withBoundaryIdentifier: atPoint:
動力項與邊界碰撞開始時被調(diào)用遏餐。 -
collisionBehavior: beganContactForItem: withItem: atPoint:
動力項與動力項之間碰撞開始時被調(diào)用。 -
collisionBehavior: endedContactForItem: withBoundaryIdentifier:
動力項與邊界碰撞結(jié)束時調(diào)用赢底。 -
collisionBehavior: endedContactForItem: withItem:
動力項與動力項之間碰撞結(jié)束時調(diào)用失都。
實際使用中,可以根據(jù)上面代理方法執(zhí)行其他操作幸冻。
3. 重力行為粹庞、碰撞行為和推動行為具體應(yīng)用
通過第一部分的學(xué)習(xí),我們已經(jīng)基本了解了UIKit Dynamics的使用洽损,但這個彈跳球在實際中沒有太大用途庞溜。基于此碑定,在第二部分將創(chuàng)建一個更有實用價值的示例流码。
在顯示、隱藏菜單組件時不傅,使用UIKit Dynamics可以獲得更好的動畫效果旅掂。這一部分示例想要達(dá)到的效果為:使用UISwipeGestureRecognizer
手勢自左向右滑赏胚,從左側(cè)彈出一個占據(jù)半個屏幕的菜單访娶。使用同樣手勢自右向左滑動,隱藏彈出菜單觉阅。在彈出菜單后面崖疤,另一個半透明的視圖將會顯示在主視圖之上,防止誤點擊到主視圖典勇;當(dāng)彈出菜單隱藏時劫哼,半透明視圖隱藏。如下所示:
上面彈出菜單中的選項將不可使用割笙,只需要實現(xiàn)顯示权烧、隱藏功能眯亦。
為實現(xiàn)彈出、隱藏菜單功能我們添加一個UIView
般码,而沒有添加另一個視圖控制器妻率。對于整個app的菜單,你應(yīng)該創(chuàng)建一個視圖控制器板祝,以便于你可以從任何視圖控制器訪問菜單宫静。這里將不會這樣做,因為這已成為自定義視圖控制器轉(zhuǎn)換券时,不再符合這里要說明的問題孤里。
彈出菜單中的選項將使用UITableView
顯示。最終橘洞,菜單視圖捌袜、表視圖和背景視圖共同用于顯示彈出項,UIDynamicAnimator
處理所有動畫炸枣。
進(jìn)入SecondViewController.m
琢蛤,聲明以下屬性。
#import "SecondViewController.h"
@interface SecondViewController ()
@property (strong, nonatomic) UIView *menuView;
@property (strong, nonatomic) UIView *backgroundView;
@property (strong, nonatomic) UITableView *menuTable;
@property (strong, nonatomic) UIDynamicAnimator *animator;
@end
在實現(xiàn)部分前抛虏,添加以下預(yù)定義博其,用于指定menuView
的寬度為視圖控制器視圖寬度的二分之一。
@end
#define menuWidth self.view.frame.size.width/2
@implementation SecondViewController
在實現(xiàn)部分使用懶加載初始化剛聲明的屬性迂猴。
// 1.設(shè)置背景視圖
- (UIView *)backgroundView {
if (!_backgroundView) {
_backgroundView = [[UIView alloc] initWithFrame:self.view.frame];
_backgroundView.backgroundColor = [UIColor lightGrayColor];
_backgroundView.alpha = 0.0;
}
return _backgroundView;
}
// 2.設(shè)置菜單視圖
- (UIView *)menuView {
if (!_menuView) {
_menuView = [[UIView alloc] initWithFrame:CGRectMake(-menuWidth, 20, menuWidth, self.view.frame.size.height - self.tabBarController.tabBar.frame.size.height)];
_menuView.backgroundColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1.0];
}
return _menuView;
}
// 3.設(shè)置表視圖
- (UITableView *)menuTable {
if (!_menuTable) {
_menuTable = [[UITableView alloc] initWithFrame:self.menuView.bounds style:UITableViewStylePlain];
_menuTable.backgroundColor = [UIColor clearColor];
_menuTable.alpha = 1.0;
_menuTable.scrollEnabled = NO;
_menuTable.separatorStyle = UITableViewCellSeparatorStyleNone;
_menuTable.delegate = self;
_menuTable.dataSource = self;
}
return _menuTable;
}
// 4.初始化UIDynamicAnimator
- (UIDynamicAnimator *)animator {
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
}
return _animator;
}
上述代碼的分步說明如下:
- 背景視圖大小與當(dāng)前視圖大小一致慕淡,設(shè)置為透明色。
- 菜單視圖寬度為窗口寬度一半沸毁,初始化時使其位于左側(cè)不可見區(qū)域峰髓。
- 設(shè)置表視圖大小和菜單視圖大小一致,初始化完成后將其添加到菜單視圖息尺。
- 初始化
UIDynamicAnimator
携兵。
上面的代碼都很簡單,就不再詳細(xì)說明搂誉。Xcode此時會發(fā)出警告徐紧,這是因為我們已經(jīng)將我們的類設(shè)置為表視圖的委托和數(shù)據(jù)源,但卻沒有遵守各自的協(xié)議炭懊。在interface后添加協(xié)議后代碼如下:
@interface SecondViewController () <UITableViewDelegate, UITableViewDataSource>
遵守上述協(xié)議后并级,Xcode會提示沒有實現(xiàn)必需實現(xiàn)的協(xié)議方法。現(xiàn)在實現(xiàn)所有我們需要的方法侮腹,其中表視圖共五行嘲碧,每行一個選項,點擊每行時其狀態(tài)從選中變?yōu)槿∠x中父阻。
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"Option %li",indexPath.row + 1];
cell.textLabel.textColor = [UIColor lightGrayColor];
cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.backgroundColor = [UIColor clearColor];
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 50;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[[tableView cellForRowAtIndexPath:indexPath] setSelected:NO];
}
開始下一步之前愈涩,在實現(xiàn)部分前定義cell的標(biāo)識符如下:
@end
#define menuWidth self.view.frame.size.width
static NSString * const reuseIdentifier = @"CellIdentifier";
@implementation SecondViewController
進(jìn)入viewDidLoad
方法望抽,將背景視圖和菜單視圖添加到視圖控制器視圖,將表視圖添加到菜單視圖履婉,最后注冊cell糠聪。更新后如下:
- (void)viewDidLoad {
[super viewDidLoad];
// 添加背景視圖 菜單視圖 表視圖
[self.view addSubview:self.backgroundView];
[self.view addSubview:self.menuView];
[self.menuView addSubview:self.menuTable];
// 注冊cell
[self.menuTable registerClass:[UITableViewCell class] forCellReuseIdentifier:reuseIdentifier];
}
backgroundView
和menuView
添加到視圖控制器視圖上,menuTable
添加到menuView
上谐鼎。
現(xiàn)在開始實現(xiàn)彈出舰蟆、隱藏動畫部分。如果我們仔細(xì)考慮一下狸棍,你會發(fā)現(xiàn)菜單視圖彈出和隱藏的動畫是相同的身害,只是方向相反。這意味著可以在一個方法內(nèi)定義動力行為草戈,其方向根據(jù)menuView
所處狀態(tài)而定塌鸯。
在實現(xiàn)之前,先看一下要顯示菜單的動力行為唐片。首先丙猬,需要一個碰撞行為,以便讓視圖在指定邊界發(fā)生碰撞费韭,而不是從屏幕一邊滑動到另一邊茧球。其次,添加方向向右的重力行為星持,以便讓menuView
看起來像被邊界拖拽著滑動抢埋。再添加一個UIDynamicItemBehavior
屬性中的elasticity
,這樣看起來就很好了督暂。但還可以添加一個UIPushBehavior
讓menuView
移動的快一些揪垄。在隱藏menuView
時使用相同動力行為,只是方向相反逻翁。
我們將在實現(xiàn)部分添加以下方法饥努,用于實現(xiàn)動畫的主要部分。
- (void)toggleMenu:(BOOL)shouldOpenMenu {
// 移除所有動力行為
[self.animator removeAllBehaviors];
// 根據(jù)參數(shù)shouldOpenMenu 獲取重力方向 推力方向 邊界位置
CGFloat gravityDirectionX = shouldOpenMenu ? 1.0 : -1.0;
CGFloat pushMagnitude = shouldOpenMenu ? 20.0 : -20.0;
CGFloat boundaryPointX = shouldOpenMenu ? menuWidth : -menuWidth;
// 添加重力行為
UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.menuView]];
gravityBehavior.gravityDirection = CGVectorMake(gravityDirectionX, 0);
[self.animator addBehavior:gravityBehavior];
// 添加碰撞行為
UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.menuView]];
[collisionBehavior addBoundaryWithIdentifier:@"menuBoundary" fromPoint:CGPointMake(boundaryPointX, 20) toPoint:CGPointMake(boundaryPointX, self.tabBarController.tabBar.frame.origin.y)];
[self.animator addBehavior:collisionBehavior];
// 添加推力行為
UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.menuView] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.magnitude = pushMagnitude;
[self.animator addBehavior:pushBehavior];
// 設(shè)置menuView的elasticity屬性
UIDynamicItemBehavior *menuViewBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.menuView]];
menuViewBehavior.elasticity = 0.4;
[self.animator addBehavior:menuViewBehavior];
// 設(shè)置backgroundView alpha值
self.backgroundView.alpha = shouldOpenMenu ? 0.5 : 0;
}
上述代碼每一步都很簡單八回,這里有以下幾點需要注意:
- 在代碼開始部分酷愧,移除
animator
內(nèi)所有動力行為。因為當(dāng)使用相反手勢辽社,隱藏menuView
時伟墙,需要添加的行為與已存在的行為沖突,同時存在動畫不能正常運(yùn)行滴铅。 - 初始化推力行為中參數(shù)
mode:
有兩個可用參數(shù),UIPushBehaviorModeInstantaneous
表示施加瞬時力就乓,即一個沖量汉匙;UIPushBehaviorModeContinuous
表示施加連續(xù)的力拱烁。 -
backgroundView
的alpha
由現(xiàn)在所處狀態(tài)決定。
如果你想要施加的重力需要結(jié)合物體自身質(zhì)量噩翠,請使用后面講到的
UIFieldBehavior
戏自,場行為支持線性和徑向引力場,其引力大小和動力項自身質(zhì)量有關(guān)伤锚,且力大小與距場行為中心遠(yuǎn)近相關(guān)擅笔,導(dǎo)致施加到不同物體上的力大小不同。
在主屏幕上使用手勢右滑屯援,menuView
出現(xiàn)猛们;在menuView
上左滑,menuView
隱藏狞洋。最方便的方法就是當(dāng)手勢觸發(fā)時調(diào)用同一個方法弯淘,在調(diào)用方法內(nèi)根據(jù)手勢方向調(diào)用toggleMenu:
方法,并設(shè)置對應(yīng)參數(shù)吉懊。
進(jìn)入viewDidLoad
庐橙,添加手勢。
- (void)viewDidLoad {
...
// 添加右滑手勢
UISwipeGestureRecognizer *showMenuGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
showMenuGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:showMenuGesture];
// 添加左滑手勢
UISwipeGestureRecognizer *hideMenuGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
hideMenuGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.menuView addGestureRecognizer:hideMenuGesture];
}
上面添加手勢的代碼有兩點不同借嗽。其一态鳖,手勢方向相反;其二恶导,手勢添加到的視圖不同郁惜,右滑手勢添加到view
,左滑手勢添加到menuView
甲锡。你也可以在視圖控制器視圖上再添加一個左滑手勢兆蕉,方便用戶在主視圖上左滑隱藏menuView
,如果遇到問題缤沦,可以通過文章底部網(wǎng)址下載demo源碼查看虎韵。
在實現(xiàn)部分實現(xiàn)手勢中handleGesture:
方法,根據(jù)手勢方向為toggleMenu:
設(shè)置不同參數(shù)缸废。
- (void)handleGesture:(UISwipeGestureRecognizer *)gesture {
if (gesture.direction == UISwipeGestureRecognizerDirectionRight) {
[self toggleMenu:YES];
} else {
[self toggleMenu:NO];
}
}
現(xiàn)在運(yùn)行app包蓝,嘗試使用手勢彈出、隱藏menuView
企量,也可以自行修改其他屬性测萎。
這篇文章將多次用到手勢識別器(UIGestureRecognizer),如果你還不熟悉届巩,可以查看我的另一篇文章:手勢控制:點擊硅瞧、滑動、平移恕汇、捏合腕唧、旋轉(zhuǎn)或辖、長按、輕掃
4. 附著行為UIAttachmentBehavior
在這一部分枣接,將添加UIPushBehavior
颂暇、UIAttachmentBehavior
和UIDynamicItemBehavior
幾種動力行為到image
,隨后拖動image
產(chǎn)生如下效果:
4.1 構(gòu)建界面
下載圖片并添加到Assets.xcassets但惶。進(jìn)入到ThirdViewController.m
耳鸯,添加以下聲明。
@interface ThirdViewController ()
@property (strong, nonatomic) UIImageView *imageView;
@property (strong, nonatomic) UIView *redView;
@property (strong, nonatomic) UIView *blueView;
@property (assign, nonatomic) CGRect orangeBounds;
@property (assign, nonatomic) CGPoint orangeCenter;
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UIAttachmentBehavior *attachmentBehavior;
@property (strong, nonatomic) UIPushBehavior *pushBehavior;
@property (strong, nonatomic) UIDynamicItemBehavior *itemBehavior;
@end
redView
和blueView
用于表示UIDynamics
物理引擎動畫的點膀曾,blueView
表示觸摸開始的點县爬,redView
表示表示手指觸摸點,也是imageView
的錨點妓肢。
使用懶加載初始化捌省、配置前三個視圖屬性。
- (UIImageView *)imageView {
if (!_imageView) {
CGPoint center = self.view.center;
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 240, 240)];
_imageView.center = CGPointMake(center.x, center.y*2/3);
_imageView.image = [UIImage imageNamed:@"AppleLogo.jpg"];
_imageView.userInteractionEnabled = YES;
}
return _imageView;
}
- (UIView *)redView {
if (!_redView) {
_redView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
_redView.center = CGPointMake(self.view.center.x, self.view.center.y*2/3);
_redView.backgroundColor = [UIColor redColor];
}
return _redView;
}
- (UIView *)blueView {
if (!_blueView) {
_blueView = [[UIView alloc] initWithFrame:CGRectMake(80, 400, 10, 10)];
_blueView.backgroundColor = [UIColor blueColor];
}
return _blueView;
}
將上面初始化的屬性添加到view
碉钠。
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.imageView];
[self.view addSubview:self.redView];
[self.view addSubview:self.blueView];
}
在viewDidLoad
中為imageView
添加平移手勢識別器UIPanGestureRecognizer
纲缓,用于拖動圖片視圖。
- (void)viewDidLoad {
...
// 為imageView添加平移手勢識別器
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleAttachmentGesture:)];
[self.imageView addGestureRecognizer:pan];
}
在imageView
識別到平移手勢時喊废,會調(diào)用以下方法祝高。在該方法內(nèi)用view
和imageView
不同坐標(biāo)系統(tǒng)輸出當(dāng)前手勢位置。
- (void)handleAttachmentGesture:(UIPanGestureRecognizer *)panGesture {
CGPoint location = [panGesture locationInView:self.view];
CGPoint boxLocation = [panGesture locationInView:self.imageView];
switch (panGesture.state) {
// 手勢開始
case UIGestureRecognizerStateBegan:
NSLog(@"Touch started position: %@",NSStringFromCGPoint(location));
NSLog(@"Location in image started is %@",NSStringFromCGPoint(boxLocation));
break;
// 手勢結(jié)束
case UIGestureRecognizerStateEnded:
NSLog(@"Touch ended position: %@",NSStringFromCGPoint(location));
NSLog(@"Location in image ended is %@",NSStringFromCGPoint(boxLocation));
break;
default:
break;
}
}
現(xiàn)在運(yùn)行app污筷,拖動視圖上的Apple logo工闺,可以看到控制臺輸出如下:
Touch started position: {237.66665649414062, 294.66665649414062}
Location in image started is {150.66665649414062, 169.33332316080728}
Touch ended position: {144, 288.66665649414062}
Location in image ended is {57, 163.33332316080728}
4.2 添加UIDynamicAnimator
和UIAttachmentBehavior
現(xiàn)在要做的就是通過使用UIAttachmentBehavior
讓imageView
隨手指移動。在此之前瓣蛀,先將imageView
的frame保存到屬性中陆蟆。更新后viewDidLoad
方法如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.orangeBounds = self.imageView.bounds;
self.orangeCenter = self.imageView.center;
...
}
當(dāng)手勢識別器識別到手勢開始時,初始化UIAttachmentBehavior
惋增,更新redView
和blueView
位置叠殷。最后添加UIAttachmentBehavior
到UIDynamicAnimator
。更新后的handleAttachmentGesture:
方法如下:
- (void)handleAttachmentGesture:(UIPanGestureRecognizer *)panGesture {
...
switch (panGesture.state) {
// 手勢開始
case UIGestureRecognizerStateBegan:
// NSLog(@"Touch started position: %@",NSStringFromCGPoint(location));
// NSLog(@"Location in image started is %@",NSStringFromCGPoint(boxLocation));
// 1.移除所有動力行為
[self.animator removeAllBehaviors];
// 2.通過改變錨點移動imageView
UIOffset centerOffset = UIOffsetMake(boxLocation.x - CGRectGetMidX(self.imageView.bounds), boxLocation.y - CGRectGetMidY(self.imageView.bounds));
self.attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self.imageView offsetFromCenter:centerOffset attachedToAnchor:location];
// 3.redView中心設(shè)置為attachmentBehavior的anchorPoint blueView的中心設(shè)置為手勢手勢開始的位置
self.redView.center = self.attachmentBehavior.anchorPoint;
self.blueView.center = location;
// 4.添加附著行為到animator
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[self.animator addBehavior:self.attachmentBehavior];
break;
// 手勢結(jié)束
...
}
上述代碼分布說明如下:
- 首先移除已存在的動力行為诈皿,不然動力行為間會產(chǎn)生沖突林束。
- 先計算出手勢與
imageView
中心偏移,初始化attachmentBehavior
稽亏。 -
redView
中心設(shè)置為attachmentBehavior
的anchorPoint
壶冒,blueView
的中心設(shè)置為手勢手勢開始的位置location
。 - 初始化
animator
時指定其坐標(biāo)系統(tǒng)為self.view
截歉,添加附著行為到animator
胖腾。
現(xiàn)在讓anchorPoint
跟隨手指移動,在上面代碼后添加如下代碼:
- (void)handleAttachmentGesture:(UIPanGestureRecognizer *)panGesture {
...
// 手勢改變
case UIGestureRecognizerStateChanged:
self.attachmentBehavior.anchorPoint = [panGesture locationInView:self.view];
self.redView.center = self.attachmentBehavior.anchorPoint;
break;
// 手勢結(jié)束
...
}
上面的代碼可以讓anchorPoint
和redView
中心與當(dāng)前手指位置一致,當(dāng)手指移動時胸嘁,手勢識別器調(diào)用該方法更新anchorPoint
瓶摆,animator
會自動動態(tài)更新視圖位置凉逛。如果手動更新了動力項位置性宏,則需要使用animator
調(diào)用updateItemUsingCurrentState:
方法更新動力項位置。
運(yùn)行demo状飞,拖拽圖片毫胜,顯示如下:
在拖拽結(jié)束后,讓imageView
恢復(fù)到原來位置更美觀一些诬辈。在實現(xiàn)部分添加如下方法:
- (void)resetDemo {
[self.animator removeAllBehaviors];
[UIView animateWithDuration:0.5 animations:^{
self.imageView.bounds = self.originalBounds;
self.imageView.center = self.originalCenter;
self.imageView.transform = CGAffineTransformIdentity;
}];
}
在handleAttachmentGesture:
方法UIGestureRecognizerStateEnded
部分調(diào)用該方法酵使。
- (void)handleAttachmentGesture:(UIPanGestureRecognizer *)panGesture {
...
// 手勢結(jié)束
case UIGestureRecognizerStateEnded:
// NSLog(@"Touch ended position: %@",NSStringFromCGPoint(location));
// NSLog(@"Location in image ended is %@",NSStringFromCGPoint(boxLocation));
[self resetDemo];
break;
default:
break;
}
}
運(yùn)行demo,這次拖拽完畢imageView
會自動回到初始位置焙糟。
4.3 添加UIPushBehavior
在這一部分口渔,需要在停止拖動時分離imageView
,并施加沖量穿撮,以便在釋放時可以繼續(xù)其軌跡缺脉。
在實現(xiàn)部分前添加以下兩個常量。
@end
static CGFloat const ThrowingThreshold = 1000;
static CGFloat const ThrowingVelocityPadding = 15;
@implementation ThirdViewController
ThrowingThreshold
表示imageView
要想繼續(xù)移動而不立即返回到原始位置所需要的最低速度悦穿。ThrowingVelocityPadding
是一個魔術(shù)數(shù)字攻礼,用于影響推力大小。
繼續(xù)在handleAttachmentGesture:
方法栗柒,更新UIGestureRecognizerStateEnded
內(nèi)代碼后如下:
- (void)handleAttachmentGesture:(UIPanGestureRecognizer *)panGesture {
...
// 手勢結(jié)束
case UIGestureRecognizerStateEnded:
// NSLog(@"Touch ended position: %@",NSStringFromCGPoint(location));
// NSLog(@"Location in image ended is %@",NSStringFromCGPoint(boxLocation));
// 1.移除attachmentBehavior
[self.animator removeBehavior:self.attachmentBehavior];
// 2.當(dāng)前運(yùn)行速度 推動行為力向量大小
CGPoint velocity = [panGesture velocityInView:self.view];
CGFloat magnitude = sqrtf(velocity.x * velocity.x + velocity.y * velocity.y);
if (magnitude > ThrowingThreshold) {
// 3.添加pushBehavior
UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.imageView] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.pushDirection = CGVectorMake(velocity.x / 10, velocity.y / 10);
pushBehavior.magnitude = magnitude / ThrowingVelocityPadding;
self.pushBehavior = pushBehavior;
[self.animator addBehavior:self.pushBehavior];
// 4.修改itemBehavior屬性 以便讓imageView產(chǎn)生飛起來的感覺
NSInteger angle = arc4random_uniform(20) - 10;
self.itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.imageView]];
self.itemBehavior.friction = 0.2;
self.itemBehavior.allowsRotation = YES;
[self.itemBehavior addAngularVelocity:angle forItem:self.imageView];
[self.animator addBehavior:self.itemBehavior];
// 5.一定時間后 imageView回到初始位置
[self performSelector:@selector(resetDemo) withObject:nil afterDelay:0.3];
}
else
{
[self resetDemo];
}
break;
default:
break;
}
}
上述代碼的分布說明如下:
- 移除
attachmentBehavior
礁扮,否則手勢停止時imageView
會立即停止。這里的移除不需要判斷被移除項是否存在瞬沦,如果被移除項為nil
太伊,或不是該animator
的動力項,animator
會自動忽略該移除方法。 - 通過手勢獲得當(dāng)前速度构资,計算出力大小乾闰。
- 若
imageView
與手勢分離時的速度大于ThrowingThreshold
,添加UIPushBehavior
叠赐。這里的推力為瞬時力,所以mode:
參數(shù)為UIPushBehaviorModeInstantaneous
屡江。力的方向根據(jù)向量的x
和y
決定芭概。最后添加pushBehavior
到animator
。 - 這一部分設(shè)置
UIDynamicItemBehavior
屬性惩嘉,讓imageView
產(chǎn)生飛起來的感覺罢洲。你可以自行修改其他屬性,體會各種屬性產(chǎn)生的不同效果。 - 通過
performSelector: withObject: afterDelay:
方法惹苗,在一定時間后殿较,讓imageView
回歸到初始位置。
運(yùn)行demo桩蓉,可以看到效果如下:
5. 吸附行為UISnapBehavior
使用UISnapBehavior
可以把視圖吸附到一個新位置淋纲,視圖移動的過程就像受彈簧拉力而動。在這一部分院究,我們將通過點擊屏幕將視圖吸附到點擊處洽瞬。
進(jìn)入到FourthViewController.m
,聲明以下屬性业汰。
@interface FourthViewController ()
@property (strong, nonatomic) UIView *purpleView;
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UISnapBehavior *snapBehavior;
@end
初始化purpleView
和animator
伙窃,并設(shè)置purpleView
背景顏色為purpleColor
。
- (UIView *)purpleView {
if (!_purpleView) {
_purpleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
_purpleView.center = self.view.center;
_purpleView.backgroundColor = [UIColor purpleColor];
}
return _purpleView;
}
- (UIDynamicAnimator *)animator {
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
}
return _animator;
}
在viewDidLoad
方法中添加點擊手勢識別器样漆,手指點擊的位置用于吸附purpleView
为障。在添加點擊手勢前,添加purpleView
到view
放祟。
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.purpleView];
// 添加點擊手勢
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self.view addGestureRecognizer:tapGesture];
}
當(dāng)用戶點擊屏幕時鳍怨,點擊手勢識別器會調(diào)用handleTapGesture:
方法,下面實現(xiàn)該方法舞竿。
- (void)handleTapGesture:(UITapGestureRecognizer *)tapGesture {
// 1.獲得點擊屏幕位置
CGPoint touchPoint = [tapGesture locationInView:self.view];
// 2.snapBehavior存在時 移除
if (self.snapBehavior) {
[self.animator removeBehavior:self.snapBehavior];
}
// 3.初始化snapBehavior 設(shè)定damping值
self.snapBehavior = [[UISnapBehavior alloc] initWithItem:self.purpleView snapToPoint:touchPoint];
self.snapBehavior.damping = 0.3;
[self.animator addBehavior:self.snapBehavior];
}
上述代碼的分布說明如下:
- 獲得點擊屏幕位置京景。
- 因為只能存在一個激活的
snapBehavior
,當(dāng)snapBehavior
存在時骗奖,將其從animator
移除确徙。這里也可不做判斷直接移除。 - 初始化
snapBehavior
执桌,初始化時指定動力項為purpleView
鄙皇,snapToPoint:
參數(shù)指定吸附點為屏幕點擊位置。減震系數(shù)damping
值范圍是0.0
至1.0
仰挣,值越小震動幅度越大伴逸,默認(rèn)值是0.5
。最后添加snapBehavior
到animator
膘壶。
運(yùn)行demo错蝴,點擊屏幕任意位置查看效果。
6. 場行為UIFieldBehavior
UIFieldBehavior
將基于場的物理學(xué)應(yīng)用于動力項對象颓芭,場行為定義了可以應(yīng)用諸如重力(gravity)顷锰、磁力(magnetism)、阻力(drag)亡问、速度(velocity)官紫、湍流(turbulence)等力的區(qū)域。使用UIFieldBehavior
時,要先創(chuàng)建所需場行為束世,之后配置場強(qiáng)度(strength)屬性和其他所需屬性酝陈。
創(chuàng)建UIFieldBehavior
后,使用addItem:
方法將該場行為與動力項關(guān)聯(lián)起來毁涉。對于很多場行為沉帮,你還必須為動力項配置UIDynamicItemBehavior
屬性以便讓場可以對其施加作用,如density
屬性薪丁。最后將配置好的UIFieldBehavior
添加到UIDynamicAnimator
以便在用戶界面執(zhí)行動畫遇西。
場行為的position
屬性用于定義場行為在用戶界面中的位置馅精,場行為的region
屬性定義了場行為作用的區(qū)域严嗜。region
區(qū)域以position
為中心,region
可以是圓形或長方形洲敢,也可以用不同方式組建區(qū)域復(fù)雜的region
漫玄。
所有場行為都有strength
屬性,用于定義場強(qiáng)度压彭。大部分場行為也會用到falloff
屬性睦优,使場強(qiáng)隨距離變化。根據(jù)場的類型和需要配置其他屬性壮不,如minimumRadius
汗盘、direction
、smoothness
和animationSpeed
等屬性询一。
在這里將創(chuàng)建radialGravityField
和vortexField
兩個場行為隐孽,并與UIView
對象blueView
關(guān)聯(lián)起來。因這里場行為的類型健蕊,需要使用UIDynamicItemBehavior
對象配置blueView
的density
屬性菱阵。
進(jìn)入FifthViewController.m
,聲明以下屬性缩功。
@interface FifthViewController ()
@property (strong, nonatomic) UIView *blueView;
@property (strong, nonatomic) UIDynamicAnimator *animator;
@property (strong, nonatomic) UIFieldBehavior *radialGravityField;
@property (strong, nonatomic) UIFieldBehavior *vortexField;
@end
使用懶加載配置blueView
和animator
晴及,初始化后在viewDidLoad
方法中將blueView
添加到view
。
- (UIView *)blueView {
if (!_blueView) {
_blueView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
_blueView.center = CGPointMake(self.view.center.x*2/3, self.view.center.y*2/3);
_blueView.layer.cornerRadius = _blueView.frame.size.width/2;
_blueView.layer.backgroundColor = [UIColor blueColor].CGColor;
}
return _blueView;
}
- (UIDynamicAnimator *)animator {
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
}
return _animator;
}
使用懶加載初始化radialGravityField
嫡锌。
- (UIFieldBehavior *)radialGravityField {
if (!_radialGravityField) {
// 1.使用類方法初始化radialGravityField
_radialGravityField = [UIFieldBehavior radialGravityFieldWithPosition:self.view.center];
// 2.指定radialGravityField區(qū)域
_radialGravityField.region = [[UIRegion alloc] initWithRadius:300];
// 3.設(shè)置場強(qiáng)
_radialGravityField.strength = 1.5;
// 4.場強(qiáng)隨距離變化
_radialGravityField.falloff = 4.0;
// 5.場強(qiáng)變化的最小半徑
_radialGravityField.minimumRadius = 50.0;
}
return _radialGravityField;
}
上述代碼分步說明如下:
- 使用類方法
radialGravityFieldWithPosition:
初始化radialGravityBehavior
虑稼,模擬地心引力,指定場行為的位置在視圖的中心势木。 -
region
指定場行為作用區(qū)域為半徑300point的圓蛛倦。radialGravityBehavior
對區(qū)域外動力項不能施加作用。 - 設(shè)定場強(qiáng)為
1.5
跟压,strength
的默認(rèn)值為1.0
胰蝠。相同strength
值在不同類型場行為中有不同作用力大小,確定該值最佳方法是嘗試不同值,直到獲得所需的效果茸塞。 -
fallOff
定義了場強(qiáng)strength
隨距離場行為中心距離增加而減小的速度躲庄。距離每超過minimumRadius
時,falloff
會施加一次作用钾虐。該屬性默認(rèn)值是0
噪窘,產(chǎn)生場強(qiáng)不會隨距離變化的均勻區(qū)域。在這里效扫,設(shè)置fallOff
值為4.0
倔监。 - 開始計算場強(qiáng)度新值的最小距離。距離小于
minimumRadius
時菌仁,按照等于minimumRadius
計算浩习。除此之外,只有到達(dá)最小距離時falloff
才會有效济丘。minimumRadius
默認(rèn)值很小谱秽,但不為零。在這里摹迷,設(shè)置minimumRadius
值為50.0
疟赊。
初始化vortexField
。
- (UIFieldBehavior *)vortexField {
if (!_vortexField) {
// 1.初始化vortexField
_vortexField = [UIFieldBehavior vortexField];
// 2.設(shè)置position為視圖中心
_vortexField.position = self.view.center;
_vortexField.region = [[UIRegion alloc] initWithRadius:200];
_vortexField.strength = 0.005;
}
return _vortexField;
}
上述代碼的分布說明如下:
- 使用類方法
vortexField
初始化vortexField
峡碉,模擬渦流力近哟。 - 設(shè)定
vortexField
的position
為視圖控制器中心。
在viewDidAppear:
方法添加動力項到動力行為鲫寄,將動力行為添加到animator
吉执。
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// 1.開啟調(diào)試模式
[self.animator setValue:[NSNumber numberWithBool:YES] forKey:@"debugEnabled"];
// 2.配置動力項密度
UIDynamicItemBehavior *blueViewBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.blueView]];
blueViewBehavior.density = 0.5;
[self.animator addBehavior:blueViewBehavior];
// 3.添加動力項到動力行為
[self.vortexField addItem:self.blueView];
[self.radialGravityField addItem:self.blueView];
// 4.添加動力行為到animator
[self.animator addBehavior:self.vortexField];
[self.animator addBehavior:self.radialGravityField];
}
在1中開啟調(diào)試模式,可以讓我們看到場行為的強(qiáng)度和方向(下圖中紅線)塔拳。強(qiáng)度越大鼠证,紅色虛線越顯著。下圖1strength
為1.5
靠抑,圖2strength
為50
量九,其他屬性相同。
運(yùn)行demo颂碧,顯示如下:
你可以添加其他場行為到blueView
荠列,查看各種場行為的效果。
總結(jié)
UIKit Dynamics是一個非常有用的庫载城,利用系統(tǒng)提供的各種動力行為并設(shè)置適當(dāng)?shù)膶傩约∷疲梢詾閁I添加很多逼真動畫。使用物理引擎是要耗費一定CPU資源的诉瓦,特別是在碰撞檢測時川队。使用中可以組合使用多種動力行為力细,但不要讓彼此沖突。UIKit Dynamics可以與他動畫結(jié)合使用固额,以創(chuàng)建更復(fù)雜的UI動畫眠蚂。
Demo名稱:UIKitDynamics
源碼地址:https://github.com/pro648/BasicDemos-iOS
參考資料: