WWDC 2013 Session筆記 - UIKit Dynamics入門

来源:http://www.cnblogs.com/jgCho/archive/2016/04/13/5387309.html
-Advertisement-
Play Games

本文涉及到的WWDC2013 Session有 1.Session 206 Getting Started with UIKit Dynamics 2.Session 221 Advanced Techniques with UIKit Dynamics 什麼是UIKit動力學(UIKit Dyna ...


本文涉及到的WWDC2013 Session有 1.Session 206 Getting Started with UIKit Dynamics 2.Session 221 Advanced Techniques with UIKit Dynamics   什麼是UIKit動力學(UIKit Dynamics) 其實就是UIKit的一套動畫和交互體系。我們現在進行UI動畫基本都是使用CoreAnimation或者UIView animations。而UIKit動力學最大的特點是將現實世界動力驅動的動畫引入了UIKit,比如重力,鉸鏈連接,碰撞,懸掛等效果。一言蔽之,即是,將2D物理引擎引入了人UIKit。需要註意,UIKit動力學的引入,並不是以替代CA或者UIView動畫為目的的,在絕大多數情況下CA或者UIView動畫仍然是最優方案,只有在需要引入逼真的交互設計的時候,才需要使用UIKit動力學它是作為現有交互設計和實現的一種補充而存在的。   目的當然是更加自然和炫目的UI動畫效果,比如模擬現實的拖拽和彈性效果,放在以前如果單用iOS SDK的動畫實現起來還是相當困難的,而在UIKit Dynamics的幫助下,複雜的動畫效果可能也只需要很短的代碼(基本100行以內…其實現在用UIView animation想實現一個不太複雜的動畫所要的代碼行數都不止這個數了吧)。總之,便利多多,配合UI交互設計,以前很多不敢想和不敢寫(至少不敢自己寫)的效果實現起來會非常方便,也相信在iOS7的時代各色使用UIKit動力學的應用的在動畫效果肯定會上升一個檔次。   那麼,應該怎麼做呢?   UIKit動力學實現的結構 為了實現動力UI,需要註冊一套UI行為的體系,之後UI便會按照預先的設定進行運動了。我們應該瞭解的新的基本概念有如下四個:   UIDynamicItem:用來描述一個力學物體的狀態,其實就是實現了UIDynamicItem委托的對象,或者抽象為有面積有旋轉的質點; UIDynamicBehavior:動力行為的描述,用來指定UIDynamicItem應該如何運動,即定義適用的物理規則。一般我們使用這個類的子類對象來對一組UIDynamicItem應該遵守的行為規則進行描述; UIDynamicAnimator:動畫的播放者,動力行為(UIDynamicBehavior)的容器,添加到容器內的行為將發揮作用; ReferenceView:等同於力學參考系,如果你的初中物理不是語文老師教的話,我想你知道這是啥..只有當想要添加力學的UIView是ReferenceView的子view時,動力UI才發生作用。   光說不練假把式,來做點簡單的demo吧。比如為一個view添加重力行為:  
  1. - (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.   
  4.     UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(100, 50, 100, 100)]; 
  5.     aView.backgroundColor = [UIColor lightGrayColor]; 
  6.     [self.view addSubview:aView]; 
  7.   
  8.     UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
  9.     UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[aView]]; 
  10.     [animator addBehavior:gravityBeahvior]; 
  11.     self.animator = animator; 
    代碼很簡單:   1.以現在ViewController的view為參照系(ReferenceView),來初始化一個UIDynamicAnimator。   2.然後分配並初始化一個動力行為,這裡是UIGravityBehavior,將需要進行物理模擬的UIDynamicItem傳入。UIGravityBehavior的initWithItems:接受的參數為包含id的數組,另外UIGravityBehavior實例還有一個addItem:方法接受單個的id。就是說,實現了UIDynamicItem委托的對象,都可以看作是被力學特性影響的,進而參與到計算中。UIDynamicItem委托需要我們實現bounds,center和transform三個屬性,在UIKit Dynamics計算新的位置時,需要向Behavior內的item詢問這些參數,以進行正確計算。iOS7中,UIView和UICollectionViewLayoutAttributes已經預設實現了這個介面,所以這裡我們直接把需要模擬重力的UIView添加到UIGravityBehavior里就行了。   3.把配置好的UIGravityBehavior添加到animator中。   4.strong持有一下animator,避免當前scope結束被ARC釋放掉(後果當然就是UIView在哪兒傻站著不掉)   運行結果,view開始受重力影響了:   重力作用下的UIview 碰撞,我要碰撞 沒有碰撞的話,物理引擎就沒有任何意義了。和重力行為類似,碰撞也有一個UIDynamicBehavior子類來描述碰撞行為,即UICollisionBehavior。在上面的demo中加上幾句:  
  1. - (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.  
  4.     UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(100, 50, 100, 100)]; 
  5.     aView.backgroundColor = [UIColor lightGrayColor]; 
  6.     [self.view addSubview:aView]; 
  7.  
  8.     UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
  9.     UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[aView]]; 
  10.     [animator addBehavior:gravityBeahvior]; 
  11.  
  12.     UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[aView]]; 
  13.     collisionBehavior.translatesReferenceBoundsIntoBoundary = YES; 
  14.     [animator addBehavior:collisionBehavior]; 
  15.     collisionBehavior.collisionDelegate = self; 
  16.  
  17.     self.animator = animator; 
  也許聰明的你已經看到了,還是一樣的,創建新的行為規則(UICollisionBehavior),然後加到animator中…唯一區別的地方是碰撞需要設定碰撞邊界範圍translatesReferenceBoundsIntoBoundary將整個參照view(也就是self.view)的邊框作為碰撞邊界(另外你還可以使用setTranslatesReferenceBoundsIntoBoundaryWithInsets:這樣的方法來設定某一個區域作為碰撞邊界,更複雜的邊界可以使用addBoundaryWithIdentifier:forPath:來添加UIBezierPath,或者addBoundaryWithIdentifier:fromPoint:toPoint:來添加一條線段為邊界,詳細地還請查閱文檔);另外碰撞是有回調的,可以在self中實現UICollisionBehaviorDelegate。   最後,只是直直地掉下來的話未免太無聊了,加個角度吧:  
  1. aView.transform = CGAffineTransformRotate(aView.transform, 45); 
結果是這樣的,帥死了…這在以前只用iOS SDK的話,夠寫上很長時間了吧..   碰撞和重力同時作用的動力UI   碰撞的delegate可以幫助我們瞭解碰撞的具體情況,包括哪個item和哪個item開始發生碰撞,碰撞接觸點是什麼,是否和邊界碰撞,和哪個邊界碰撞了等信息。這些回調方法保持了Apple一向的命名原則,所以通俗易懂。需要多說一句的是回調方法中對於ReferenceView的Bounds做邊界的情況,BoundaryIdentifier將會是nil,自行添加的其他邊界的話,ID自然是添加時指定的ID了。 1.– collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint: 2.– collisionBehavior:beganContactForItem:withItem:atPoint: 3.– collisionBehavior:endedContactForItem:withBoundaryIdentifier: 4.– collisionBehavior:endedContactForItem:withItem:   其他能實現的效果 除了重力和碰撞,iOS SDK還預先幫我們實現了一些其他的有用的物理行為,它們包括: 1.UIAttachmentBehavior 描述一個view和一個錨相連接的情況,也可以描述view和view之間的連接。attachment描述的是兩個點之間的連接情況,可以通過設置來模擬無形變或者彈性形變的情況(再次希望你還記得這些概念,簡單說就是木棒連接和彈簧連接兩個物體)。當然,在多個物體間設定多個;UIAttachmentBehavior,就可以模擬多物體連接了..有了這些,似乎可以做個老鷹捉小雞的游戲了- -…   2.UISnapBehavior 將UIView通過動畫吸附到某個點上。初始化的時候設定一下UISnapBehavior的initWithItem:snapToPoint:就行,因為API非常簡單,視覺效果也很棒,估計它是今後非游戲app里會被最常用的效果之一了;   3.UIPushBehavior 可以為一個UIView施加一個力的作用,這個力可以是持續的,也可以只是一個沖量。當然我們可以指定力的大小,方向和作用點等等信息。   4.UIDynamicItemBehavior 其實是一個輔助的行為,用來在item層級設定一些參數,比如item的摩擦,阻力,角阻力,彈性密度和可允許的旋轉等等。   UIDynamicItemBehavior有一組系統定義的預設值: 1.allowsRotation YES 2.density 1.0 3.elasticity 0.0 4.friction 0.0 5.resistance 0.0   所有的UIDynamicBehavior都是可以獨立作用的,同時作用時也遵守力的合成。也就是說,組合使用行為可以達到一些較複雜的效果。舉個例子,希望模擬一個drag物體然後drop後下落的過程,可以用如下代碼:  
  1. - (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.   
  4.     UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
  5.     UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.square1]]; 
  6.   
  7.     collisionBehavior.translatesReferenceBoundsIntoBoundary = YES; 
  8.     [animator addBehavior:collisionBehavior]; 
  9.   
  10.     UIGravityBehavior *g = [[UIGravityBehavior alloc] initWithItems:@[self.square1]]; 
  11.     [animator addBehavior:g]; 
  12.   
  13.     self.animator = animator; 
  14.   
  15.   
  16. -(IBAction)handleAttachmentGesture:(UIPanGestureRecognizer*)gesture 
  17.     if (gesture.state == UIGestureRecognizerStateBegan){ 
  18.   
  19.         CGPoint squareCenterPoint = CGPointMake(self.square1.center.x, self.square1.center.y - 100.0); 
  20.         CGPoint attachmentPoint = CGPointMake(-25.0, -25.0); 
  21.   
  22.         UIAttachmentBehavior* attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self.square1 point:attachmentPoint attachedToAnchor:squareCenterPoint]; 
  23.   
  24.         self.attachmentBehavior = attachmentBehavior; 
  25.         [self.animator addBehavior:attachmentBehavior]; 
  26.   
  27.     } else if ( gesture.state == UIGestureRecognizerStateChanged) { 
  28.   
  29.         [self.attachmentBehavior setAnchorPoint:[gesture locationInView:self.view]]; 
  30.   
  31.     } else if (gesture.state == UIGestureRecognizerStateEnded) { 
  32.         [self.animator removeBehavior:self.attachmentBehavior]; 
  33.     } 
    viewDidiLoad時先在現在環境中加入了重力,然後監測到pan時附加一個UIAttachmentBehavior,併在pan位置更新更新其錨點,此時UIAttachmentBehavior和UIGravityBehavior將同時作用(想象成一根木棒連著手指處和view)。在手勢結束時將這個UIAttachmentBehavior移除,view將在重力作用下下落。整個過程如下圖:   Drag & Drop UIKit力學的物理學分析 既然是力學,那顯然各種單位是很重要的。在現實世界中,理想情況下物體的運動符合牛頓第二運動定理,在國際單位制中,力的單位是牛頓(N),距離單位是米(m),時間單位是秒(s),質量單位是千克(kg)。根據地球媽媽的心情,我們生活在這樣一套體制中:重力加速度約為9.8m/s2 ,加速度的單位是m/s2 ,速度單位是m/s,牛頓其實是kg·m/s2 ,即1牛頓是讓質量為1千克的物體產生1米每二次方秒的加速度所需要的力。   以上是幫助您回憶初中知識,而現在這一套體系在UIKit里又怎麼樣呢?這其實是每一個物理引擎都要討論和明白的事情,UIKit的單位體制里由於m這個東西太過誇張,因此用等量化的點(point,之後簡寫為p)來代替。具體是這樣的:UI重力加速度定義為1000p/s2 ,這樣的定義有兩方面的考慮,一時為了簡化,好記,確實1000比9.8來的只觀好看,二是也算符合人們的直感:一個UIView從y=0開始自由落體落到屏幕底部所需的時間,在3.5寸屏幕上為0.98秒,4寸屏幕上為1.07秒,1秒左右的自由落體的視覺效果對人眼來說是很舒服能進行判斷的。   那麼UIView的質量又如何定義呢,這也是很重要的,並涉及到力作用和加速度下UIView的表現。蘋果又給出了自己的“UIKit牛頓第二定律”,定義了1單位作用力相當於將一個100px100p的預設密度的UIView以100p/s2 的加速度移動。這裡需要註意預設密度這個假設,因為在UIDynamicItem的委托中並沒有實現任何密度相關的定義,而是通過UIDynamicItemBehavior來附加指定的。預設情況下,密度值為1,這就相當於質量是10000單位的UIView在1單位的作用力下可以達到1/10的UI重力加速度。   這樣類比之後的結論是,如果將1單位的UI力學中的力等同於1牛頓的話: 1.1000單位的UI質量,與現實世界中1kg的質量相當,即一個點等同一克; 2.屏幕的100像素的長度,約和現實世界中0.99米相當(完全可以看為1米) 3.UI力學中的預設密度,約和現實世界的0.1kg/m2 相當   可以說UIKit為我們構建了一套適應iOS屏幕的相當優雅的力學系統,不僅讓人過目不忘,在實際的物理情景和用戶體驗中也近乎完美。在開發中,我們可以參照這些關係尋找現實中的例子,然後將其帶入UIKit的力學系統中,以得到良好的模擬效果。   UIKit動力學自定義 除了SDK預先定義好的行為以外,我們還可以自己定義想要的行為。這種定義可以發生在兩個層級上,一種是將官方的行為打包,以簡化實現。另一種是完全定義新的計算規則。   對於第一種,其實考慮一下上面的重力+邊界碰撞,或者drag & drop行為,其實都是兩個甚至多個行為的疊加。要是每次都這樣設定一次的話,不是很辛苦麽,還容易遺忘出錯。於是一種好的方式是將它們打包封裝一下。具體地,如下步驟: 1.繼承一下UIDynamicBehavior(在這裡UIDynamicBehavior類似一個抽象類,並沒有具體實現什麼行為) 2.在子類中實現一個類似其他內置行為初始化方法initWithItems:,用以添加物體和想要打包的規則。當然你如果喜歡用其他方式也行..只不過和自帶的行為保持API統一對大家都有好處..添加item的話就用預設規則的initWithItems:就行,對於規則UIDynamicBehavior提供了一個addChildBehavior:的方法,來將其他規則加入到當前規則里 3.沒有第三步了,使用就行了。   一個例子,打包了碰撞和重力兩種行為,定義之後使用時就只需要寫一次了。當然這隻是最簡單的例子和運用,當行為複雜以後,這樣的使用方法是不可避免的,否則管理起來會讓人有想死的心。另外,將手勢等交互的方式也集成之中,進一步封裝調用細節會是不錯的實踐。  
  1. //GravityWithCollisionBehavior.h 
  2. @interface GravityWithCollisionBehavior : UIDynamicBehavior 
  3.   
  4. -(instancetype) initWithItems:(NSArray *)items; 
  5.   
  6. @end 
  7.   
  8.   
  9. //GravityWithCollisionBehavior.m 
  10. @implementation GravityWithCollisionBehavior 
  11.   
  12. -(instancetype) initWithItems:(NSArray *)items 
  13.     if (self = [super init]) { 
  14.         UIGravityBehavior *gb = [[UIGravityBehavior alloc] initWithItems:items]; 
  15.         UICollisionBehavior *cb = [[UICollisionBehavior alloc] initWithItems:items]; 
  16.         cb.translatesReferenceBoundsIntoBoundary = YES; 
  17.         [self addChildBehavior:gb]; 
  18.         [self addChildBehavior:cb]; 
  19.     } 
  20.     return self; 
  21.   
  22. @end 
  另一種比較高級一點,需要對計算完全定義。在預設的行為或者它們組合不能滿足禽獸般的產品經理/設計師的需求是,親愛的騷年..開始自己寫吧..其實說簡單也簡單,UIDynamicBehavior里提供了一個@property(nonatomic, copy) void (^action)(void),animator將在每次animation step(就是需要計算動畫時)調用這個block。就是說,你可以通過設定這個block來實現自己的行為。基本思路就是在這個block中向所有item詢問它們當前的center和transform狀態,然後開始計算,然後把計算後的相應值再賦予item,從而改變在屏幕上的位置,大小,角度等。   UIKit動力學的性能分析和限制 使用物理引擎不是沒有代價的的,特別是在碰撞檢測這塊,是要耗費一定CPU資源的。但是以測試的情況來看,如果只是UI層面上的碰撞檢測還是沒有什麼問題的,我自己實測iPhone4上同時進行數十個碰撞計算完全沒有掉幀的情況。因此如果只是把其用在UI特效上,應該不用太在意資源的耗費。但是如果同時有成百上千的碰撞需要處理的情況,可能會出現卡頓吧。   對於UIDynamicItem來說,當它們被添加到動畫系統後,我們只能通過動畫系統來改變位置,而外部的對於UIDynamicItem的center,transform等設定是被忽略的(其實這也是大多數2D引擎的實現策略,算不上限制)。   主要的限制是在當計算迭代無法得到有效解的時候,動畫將無法正確呈現。這對於絕大多數物理引擎都是一樣的。迭代不能收斂時整個物理系統處於不確定的狀態,比如初始時就設定了碰撞物體位於邊界內部,或者在狹小空間內放入了過多的非彈性碰撞物體等。另外,這個引擎僅僅只是用來呈現UI效果,它並沒有保證物理上的精確度,因此如果要用它來做UI以外的事情,有可能是無法得到很好的結果的。   總結 總之就是一套全新的UI交互的視覺體驗和效果,但是並非處處適用。在合適的地方使用可以增加體驗,但是也會有其他方式更適合的情況。所以拉上你的設計師好基友去開拓新的大陸吧…
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在B/S結構客戶端越來越“胖”的今天,作為一名全端程式員,您很可能會在前端操作html字元串,註意,是操作html字元串,不是操作當前頁面的html。 舉個例子,百度推出的線上HTML富文本編輯器Ueditor,可以線上製作富文本文檔,功能堪比精簡版的Microsoft Word。雖然Ueditor ...
  • 模板復用機制是android,ios等移動開發技術中類似listview,gridview,slideview等等之類組件常用的技術,deviceone的模板復用完全是和他們一致,deviceone有很多組件基本上涉及到多數據和模板的都會用到復用機制,有必要專門介紹一下。以下都是以listview為 ...
  • 場景一:直接選擇一個聯繫人的電話號碼 這裡不需要先獲取所有的聯繫人自己做聯繫人列表,直接使用系統自帶的AddressBookUI/ABPeoplePickerNavigationController.h就好。 首先需要引入如下三個文件 #import <AddressBookUI/ABPeopleP ...
  • title: 寫技術文章必備的幾個小工具 tags: 小書匠,有道雲筆記,gif錄屏工具 今天給大家推薦寫博客必備的幾個工具,也是我自己每次寫文章用到的。 一.小書匠 這個工具是 345大神 告訴我的,非常好用的MakeDown編輯器,比較常見的網站都支持,例如:csdn,簡書。 "官網下載頁面傳送 ...
  • 前言:第一篇文章,EventBus很多項目都在用,本文參照http://blog.csdn.net/harvic880925/article/details/40660137,自己做了一遍。把有用的挑揀出來,記錄一下啊 一、概述 EventBus是一款針對Android優化的發佈/訂閱事件匯流排。主要 ...
  • 把數據請求和風火輪的效果封裝成一個方法,用的時候直接調用這個方法就可以 + (void)startRequest:(NSString*)method baseurl:(NSString*)baseurl param:(NSDictionary*)params success:(DKSuccess)s ...
  • NSURLConnection,在iOS9被宣佈棄用,本文不使用NSURLConnection進行網路編程,有興趣的童鞋可以參考:iOS開發中的同步、非同步,GET、POST請求方法(NSURLConnection篇) NSURLSession,是在2013年的WWDC上,由Apple提出的NSURL ...
  • Swift支持大部分標準C語言的運算符, 且改進許多特性來減少常規編碼錯誤.如賦值符 = 不返回值, 以防止錯把等號 == 寫成賦值號 = 而導致Bug. 數值運算符( + , -, *, /, %等)會檢測並不允許值溢出, 以此來避免保存變數時由於變數大於或小於其類型所能承載的範圍時導致的異常結果 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...