標籤:style blog http color 使用 os io strong
這篇文章是我的【iOS開發每日小筆記】系列中的一片,記錄的是今天在開發工作中遇到的,可以用很短的文章或很小的demo示範解釋出來的小心得小技巧。該分類的文章,內容涉及的知識點可能是很簡單的、或是用很短程式碼片段就能實現的,但在我看來它們可能會給使用者體驗、代碼效率得到一些提升,或是之前自己沒有接觸過的技術,很開心的學到了,放在這裡得瑟一下(^_^)。其實,90%的作用是協助自己回顧、記憶、複習。如果看官覺得太easy,太片段,則可以有兩個選擇:1,移步【iOS探究】分類,對那裡的文章進行斧正;2,在本文的評論裡狠狠吐槽,再關掉頁面!感謝!
一直很喜歡Zaker這款應用,不論是iPad版本還是iPhone版,都讓閱讀很有效率。Zaker的左右滑動切換上下文,下拉到頂後退出閱讀等等這些手勢操作也讓單手操作很方便。Zaker的封面,下拉後會從上掉落,到螢幕底部時,會有一個像球一樣回彈的效果,印象中之前有看到有模仿的demo,但是之前也沒仔細研究過。
最近後知後覺看到iOS 7的新特性 UIKit Dynamics後,眼前一亮,感覺幾行代碼就能把原先可能需要複雜動畫代碼才能達到的效果做出來啊!趕緊動手試試。
由於是【筆記】分類,對於UIKit Dynamics我就不去做全面的講解,自己覺得對於這個2013年的“新”東西,還沒能完全掌握精通。那麼如果你碰巧看到此文,又想好好認識一下UIKit Dynamics這個東西,建議你可以看看下面幾篇文章:
1,初次嘗新,想3分鐘上手:http://onevcat.com/2013/06/uikit-dynamics-started/
2,想稍微詳細的入門一下:http://www.raywenderlich.com/zh-hans/52617/uikit-%E5%8A%9B%E5%AD%A6%E6%95%99%E7%A8%8B
3,老外的文章:http://www.objc.io/issue-5/collection-views-and-uidynamics.html
好了下面開始“老訪”一個Zaker iPhone用戶端的下拉封面(這裡可以看到demo:https://github.com/pigpigdaddy/UIKitDynamicFakeZakerDemo)
首先引用“http://onevcat.com/2013/06/uikit-dynamics-started/”的表述:
“UIKit動力學其實就是UIKit的一套動畫和互動體系。我們現在進行UI動畫基本都是使用CoreAnimation或者UIView animations。而UIKit動力學最大的特點是將現實世界動力驅動的動畫引入了UIKit,比如重力,鉸鏈串連,碰撞,懸掛等效果。一言蔽之,即是,將2D物理引擎引入了人UIKit。需要注意,UIKit動力學的引入,並不是以替代CA或者UIView動畫為目的的,在絕大多數情況下CA或者UIView動畫仍然是最優方案,只有在需要引入逼真的互動設計的時候,才需要使用UIKit動力學它是作為現有互動設計和實現的一種補充而存在的。”
UIKit Dynamics使用起來可謂非常簡單,其主要的步驟和要素有以下幾點:
1,UIDynamicItem:一個力學物體,也就是實現了<UIDynamicItem>委託的對象(比如UIView本身就實現了<UIDynamicItem>,因此UIView的子類就是一個UIDynamicItem);
2,UIDynamicBehavior:【引用自onevcat】“動力行為的描述,用來指定UIDynamicItem應該如何運動,即定義適用的物理規則。一般我們使用這個類的子類對象來對一組UIDynamicItem應該遵守的行為規則進行描述”,意思就是UIDynamicBehavior有很多子類,比如UIGravityBehavior負責重力、UICollisionBehavior負責碰撞等,我們可以用這些類來對UIDynamicItem施加不同運動行為;
3,UIDynamicAnimator;【引用自onevcat】“動畫的播放者,動力行為(UIDynamicBehavior)的容器,添加到容器內的行為將發揮作用”,也就是說,最後的“動畫”播放,是需要講上面的所有UIDynamicBehavior及其子類,放入這個UIDynamicAnimator容器內部,才能進行統一播放的;
4,ReferenceView:【引用自onevcat】“等同於力學參考系,如果你的初中物理不是語文老師教的話,我想你知道這是啥..只有當想要添加力學的UIView是ReferenceView的子view時,動力UI才發生作用”。
5,另外,需要注意的是,UIDynamicBehavior的子類通常有很多參數可以設定,如摩擦力、彈性係數、重力方向、碰撞範圍等等,還有物體觸發某些狀態的眾多Protocal可以回調使用。以上這些配合使用可以實現很多發雜的動畫效果。
OK,明確了上面這些,我們可以設計我們的demo了,我在空項目中建立了ViewController,用於window的rootViewController,如下:
1 #import "ViewController.h" 2 3 @interface ViewController ()<UICollisionBehaviorDelegate> 4 5 @property (nonatomic, strong)UIDynamicAnimator *animator; 6 7 @end 8 9 @implementation ViewController10 11 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil12 {13 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];14 if (self) {15 // Custom initialization16 }17 return self;18 }19 20 - (void)viewDidLoad21 {22 [super viewDidLoad];23 // Do any additional setup after loading the view.24 25 [self initFakeZakerView];26 }27 28 - (void)initFakeZakerView29 {30 UIView *zakerFaceView = [[UIView alloc] initWithFrame:CGRectMake(0, -self.view.bounds.size.height, self.view.bounds.size.width, self.view.bounds.size.height)];31 zakerFaceView.backgroundColor = [UIColor darkGrayColor];32 [self.view addSubview:zakerFaceView];// ******* 1 *******33 34 self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];// ******* 2 *******35 UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[zakerFaceView]];36 gravityBeahvior.magnitude = 6.0;37 [self.animator addBehavior:gravityBeahvior];// ******* 3 *******38 39 UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[zakerFaceView]];40 [collisionBehavior addBoundaryWithIdentifier:@"collisionBoundary" fromPoint:CGPointMake(0, self.view.bounds.size.height+1) toPoint:CGPointMake(self.view.bounds.size.width, self.view.bounds.size.height+1)];41 collisionBehavior.collisionDelegate = self;42 [self.animator addBehavior:collisionBehavior];// ******* 4 *******43 44 UIDynamicItemBehavior* itemBehaviour = [[UIDynamicItemBehavior alloc] initWithItems:@[zakerFaceView]];45 itemBehaviour.elasticity = 0.4;46 [self.animator addBehavior:itemBehaviour];// ******* 5 *******47 }48 49 @end
解釋一下上面5個步驟:
1,我建立了一個當前Controller.View大小的子View,設定為深灰色,並把它加到螢幕上方以外,用於一會兒從上面掉落下來;
2,初始化我的UIDynamicAnimator property變數,(這裡需要注意一點,該變數是@property strong指標,確保出了本函數範圍,動畫仍然能正常進行,不能使用局部strong指標哦,因為這樣的話,動畫是無法播放的),接著使用self.view作為參考系View。
3,要想螢幕上方以外的深灰色View掉落,必然需要一個重力,因此UIGravityBehavior便出場了。首先設定好item,也就是我的zakerFaceView。接著,設定好重力加速度的“描述值”,gravityBeahvior.magnitude = 6.0;這裡我自創了“描述值”這個名詞,因為:Apple定義了自己的重力學定律,將牛頓力學中的單位米每平方秒用像素每平方秒代替。但是,正像http://www.raywenderlich.com/zh-hans/52617/uikit-%E5%8A%9B%E5%AD%A6%E6%95%99%E7%A8%8B裡說的一樣:“你真的需要瞭解這些嗎?未必,你只需要知道更大的 g 意味著掉落得更快,但是瞭解背後的數學原理有利無弊。”所以我用“描述值”來表述magnitude這個參數,這個值越大,物體(item)掉落的速度越快。這裡經過嘗試,gravityBeahvior.magnitude = 6.0;時效果與Zaker的封面掉落效果最接近。
4,碰撞監測,介面掉落,需要檢測到碰撞到介面下邊緣,於是UICollisionBehavior便登場了。還是一樣,首先設定item為zakerFaceView。接著設定碰撞的邊緣的物體邊緣,UICollisionBehavior提供了以下幾種:
1,@property (nonatomic, readwrite) BOOL translatesReferenceBoundsIntoBoundary;2,- (void)setTranslatesReferenceBoundsIntoBoundaryWithInsets:(UIEdgeInsets)insets;3,- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath*)bezierPath;4,- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;
分別解釋一下:1,是否將Reference View的Bounds範圍作為碰撞的界限;2,設定Reference View的Bounds範圍作為碰撞的界限的UIEdgeInsets;3,添加一個碰撞邊界,該邊界是一個貝賽爾曲線(很方便吧?);4,添加一個碰撞邊界,該邊界是一個點到另一個點的直線。
在本例中,我將使用最後一個方法,添加碰撞邊界為
CGPointMake(0, self.view.bounds.size.height+1) toPoint:CGPointMake(self.view.bounds.size.width, self.view.bounds.size.height+1)
意義在於將邊界設定為“底邊外一個CGPoint”的一條線。
最後設定碰撞的代理,在本例中,我沒有實現代理中的方法,因為沒有用到。不過可以順帶看一下有以下代理方法,用於檢測碰撞的各個事件:
1 - (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2 atPoint:(CGPoint)p;2 - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2;3 4 // The identifier of a boundary created with translatesReferenceBoundsIntoBoundary or setTranslatesReferenceBoundsIntoBoundaryWithInsets is nil5 - (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier atPoint:(CGPoint)p;6 - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier;
5,彈跳設定
能掉落了,能碰撞了,還差一個:彈跳。
通過UIDynamicItemBehavior可設定item的如下參數:
1 @property (readwrite, nonatomic) CGFloat elasticity; // Usually between 0 (inelastic) and 1 (collide elastically) 2 @property (readwrite, nonatomic) CGFloat friction; // 0 being no friction between objects slide along each other3 @property (readwrite, nonatomic) CGFloat density; // 1 by default4 @property (readwrite, nonatomic) CGFloat resistance; // 0: no velocity damping5 @property (readwrite, nonatomic) CGFloat angularResistance; // 0: no angular velocity damping6 @property (readwrite, nonatomic) BOOL allowsRotation; // force an item to never rotate
我們現在需要的是彈性elasticity,OK經過嘗試,itemBehaviour.elasticity = 0.4;比較合適。
別忘了每次要把UIDynamicBehavior子類加到self.animator中。
運行,一切正常運行!效果就像上面的Gif圖片現實的那樣!