Cocoa动画编程指南
Cocoa提供了在有限或不確定的時間內對某些類型的操作進行動畫化的工具。NSAnimation提供的基本動畫,其主要目的是提供一個動畫時機和管理的源。盡管Animation一詞或許會讓你想起卡通或其他電影,但是animation對象主要設計用于程序用戶界面中的動畫部分。比如,你可以使用NSViewAnimation類(NSAnimation的子類)對視圖或窗口的大小、位置或者透明度創建平滑過渡。這個動畫外觀讓您創建一個更加流暢的用戶界面。
本文的組織結構:
本文包含下列文章:
NSAnimation 對象的使用。該部分描述動畫對象的基本特征以及如何自定義它們。
視圖和窗口的動畫。講解視圖動畫對象的使用,該視圖動畫為平滑調整大小、重新定位和更改視圖和窗口對象的不透明度提供高級接口。
NSAnimation 對象的使用
NSAnimation類為在有限時間內發生的動畫提供復雜的行為。OS X使用animation對象對界面元素執行過渡效果。你可以自定義動畫對象來為自己的代碼實現動畫。不同于NSTimer,動畫的通知可以在不固定的時間間隔內發送,以方便我們去創建顯示加速或減速的動畫。
下面的部分介紹了創建自定義NSAnimation對象并使用它管理動畫內容的基本步驟。如果要給視圖和窗口添加動畫,NSViewAnimation`類或許能提供給你需要的功能。在下一節視圖和窗口動畫中描述的視圖動畫對視圖縮放、隨時間移動視圖等提供了復雜的功能支持。
注意:動畫對象在OS X10.4以后才能使用。
創建和配置Animation Timer
一個NSAnimation對象有如下幾個重要的屬性:
當前進度——介于0.0到1.0之間的值,該值表示動畫完成的比例
幀率——每秒更新的次數
時長——動畫發生的時長(秒為單位)
動畫曲線——動畫的相對速度;例如,動畫可以在開始時緩慢加速,之后逐漸減速直到結束,或者保持勻速。
阻塞模式——根據應用程序對用戶操作的響應性運行動畫的模式。
當你去配置一個新的NSAnimation對象時,你至少要設置時長、動畫曲線、幀率和阻塞模式這幾個屬性,也應當設置一個代理去監聽動畫的進度。當動畫開始、結束、顯式地停止、或者達到標記的進度時( Setting and Handling Progress Marks ),該動畫對象就會發送消息給當前代理。如果不想使用代理,你必須繼承NSAnimation類去接收進度信息,可查看Subclassing NSAnimation.
以下代碼示范創建和配置一個標準的NSAnimation對象的簡單方法。創建動畫的對象充當委托并處理任何進度消息。
Listing 1:初始化一個NSAnimation對象
- (id)init {self = [super init];if (self){// theAnim 是一個NSAnimation的實例對象.theAnim = [[NSAnimation alloc] initWithDuration:10.0animationCurve:NSAnimationEaseIn];[theAnim setFrameRate:20.0];[theAnim setAnimationBlockingMode:NSAnimationNonblocking];[theAnim setDelegate:self];}return self; }initWithDuration:animationCurve:方法是NSAnimation類的指定初始化器,該方法允許設置動畫的兩個屬性。對其他屬性,你可以使用默認值或者使用正確的存取方法顯式地設置屬性值。默認屬性如下:
- 默認的動畫曲線為NSAnimationEaseInOut
- 默認的阻塞模式為NSAnimationBlocking
- 默認的幀率是一個合理值,平常為60Hz,但確切的值不確定
一旦你已經準備好了一個可使用的NSAnimation對象,你可以通過向該對象發送startAnimation消息運行該動畫。如果需要在動畫執行期間停止,則要想該對象發送一條stopAnimation消息。NSAnimation對象的委托(如果存在的話)接收到通知它這兩個事件的消息,以及通知它動畫是否按計劃完成的消息。
設置和處理進度標記
NSAnimation有進度標記的概念——表示動畫完成進度的浮點數。當你開始一個動畫并達到一個進度標記(特別地,當前進度值等于進度標記值時),動畫對象會發送消息給它的代理,代理方法會去更新自定義的進度標記、播放聲音、完成一些適合于動畫的其他效果。
重要:盡管您可以使用進度標記來“時間切片”對象的動畫,但它不是實現一個流暢動畫的理想方式。建議做法是繼承NSAnimation并在每個frame改變時重新繪制該對象。在 Smooth Animations中查看更多信息。
通常在第一次創建和初始化對象時設置Animation對象的進度標記。以下代碼展示設置20個等間距的進度標記。
Listing 2:設置一個NSAnimation對象的進度標記
- (void)awakeFromNib {NSAnimationProgress progMarks[] = {0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5,0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0 };int i, count = 20;// theAnim 是一個NSAnimation實例變量NSAnimation *theAnim = [[NSAnimation alloc] initWithDuration:10.0animationCurve:NSAnimationEaseInOut];[theAnim setFrameRate:20.0];[theAnim setDelegate:self];for (i=0; i<count; i++)[theAnim addProgressMark:progMarks[i]]; }不像上例那樣在循環中添加progress-mark值,您可以通過使用setProgressMarks:方法在一次調用中設置它們,該方法需要封裝浮點值的NSNumber對象數組為參數。
當一個運行中的動畫對象達到進度標記值時,它會發送animation:didReachProgressMark:消息給它的代理,委托應該以與傳入的進度標記相適應的方式處理此消息。下面代碼示例代理執行這個方法去定時播放一個音頻。
Listing 3:代理方法animation:didReachProgressMark:的執行
- (void)animation:(NSAnimation *)animationdidReachProgressMark:(NSAnimationProgress)progress {if (animation == theAnim)[[NSSound soundNamed:@"chug"] play]; }自定義NSAnimation
盡管在很多時候可以使用NSAnimation對象,但是更多時候需要繼承它實現我們的功能。自定義NSAnimation有三個主要原因:
- 通過每幀間隔重新繪制來實現平滑的動畫
- 當在主線程的非阻塞模式下運行動畫時指定可用的run-loop模式
- 沒有需要響應代理方法 animation:valueForProgress:的開銷就可以返回自定義的曲線值
完成前兩個目標的程序將在下面的部分中描述。第三條中,不用遵守代理就可以返回自定義的curve值,但是必須要重寫currentValue方法。請在NSAnimation類文檔中查看更多信息。
平滑的動畫
如上文中設置和處理進度標記中提到的,您可以給一個NSAnimation對象添加一系列的進度標記并且在每個進度值的地方執行代理方法animation:didReachProgressMark: 去重繪一個對象。然而,這不是對對象執行動畫的最佳方式。除非你設置了一個很大的進度標記值(30每秒或更多),不然動畫可能會非常不流暢。
更好的方式就是自定義NSAnimation的子類,并重寫setCurrentProgress:方法,如下代碼所示。NSAnimation對象在每一幀之后調用這個方法來改變進度值。通過攔截這個方法,你可以對那一幀執行其他任何重繪或你需要的更新。如果重寫了該方法,請確保調用super的實現,以便它可以更新當前進度。
Listing 4:重寫setCurrentProgress:方法
- (void)setCurrentProgress:(NSAnimationProgress)progress {//調用父類方法更新進度值.[super setCurrentProgress:progress];// 更新窗口位置.NSRect theWinFrame = [[NSApp mainWindow] frame];NSRect theScreenFrame = [[NSScreen mainScreen] visibleFrame];theWinFrame.origin.x = progress *(theScreenFrame.size.width - theWinFrame.size.width);[[NSApp mainWindow] setFrame:theWinFrame display:YES animate:YES]; }自定義Run-Loop模式集
具有NSAnimationNonblocking阻塞模式的NSAnimation對象以接受用戶輸入的運行循環模式在進程的主線程中運行。在運行動畫之前,動畫對象向自己發送一條runLoopModesForAnimation消息,以獲得當前有效的運行循環模式。默認情況下,這個方法返回nil,它告訴NSAnimation使用默認模式(NSDefaultRunLoopMode),模態面板模式(NSModalPanelRunLoopMode)和事件跟蹤運行循環模式(NSEventTrackingRunLoopMode)。
您可以重寫此方法以返回一組不同的運行循環模式,其中可以包含自定義模式。下面代碼顯示了一個返回默認模式數組減去事件跟蹤模式(NSEventTrackingRunLoopMode)的實現。
Listing 5: 從runLoopModesForAnimating返回一個run-loop模式
- (NSArray *)runLoopModesForAnimating {return [NSArray arrayWithObjects: NSDefaultRunLoopMode,NSModalPanelRunLoopMode, nil]; }鏈接動畫
您可以鏈接兩個動畫對象,以便其中一個對象在另一個對象到達指定的動畫標記時開始運行(或停止運行)。NSAnimation的這個特性對于協調不同的效果很有用。Listing 6演示了如何使用startWhenAnimation:reachesProgress:方法在另一個動畫到達中途點時啟動動畫。
Listing 6:鏈接兩個動畫對象
- (IBAction)startAnim:(id)sender {// theAnim 和 theOtherAnim 是NSAnimation類型的變量.[theOtherAnim startWhenAnimation:theAnim reachesProgress:0.5];[theAnim startAnimation]; }如果您想在另一個動畫達到進度標記時停止動畫,請使用stopWhenAnimation:reachesProgess: method。您可以無限地鏈接動畫,一個接一個,然而,在任何時候,只能有一個“開始”和一個“停止”動畫。
如果您有一個正在響應animation:didReachProgressMark: messages的委托,它必須在多個動畫之間進行區分,如Listing 7所示。
Listing 7:處理同時運行動畫的進度標記
- (void)animation:(NSAnimation *)animationdidReachProgressMark:(NSAnimationProgress)progress {if (animation == theOtherAnim){//執行一個適合于進度標記的效果}else if (animation == theAnim){// 執行一個適合于進度標記的效果} }視圖和窗口的動畫
NSViewAnimation是NSAnimation類的子類,它為視圖及窗口提供了一個方便的動畫方式,主要有以下:
- 改變frame 的位置
- 改變frame 的大小
- 改變物體的不透明度,淡入或淡出。
視圖動畫過程
使用視圖動畫對象的方式與使用常規的NSAnimation對象稍有不同。一個視圖動畫對象可以同時管理多個視圖和窗口的動畫過程。不是使用動畫對象的方法設置屬性,而是為要修改的每個視圖或窗口創建一個動畫屬性字典。每個字典指定操作的目標(視圖或窗口),以及您想要應用到該目標的效果。要設置其他因素,比如動畫的持續時間和時間曲線,您需要繼續使用NSAnimation的方法。
一個動畫屬性字典只有一個必須值:目標對象,您可以通過NSViewAnimationTargetKey鍵值添加此對象到字典。但是,這個鍵的存在不會改變視圖或窗口。要進行更改,您必須包含一個或多個額外的鍵來指定所需的行為。
如果您愿意,您可以同時對單個目標對象執行多個操作。例如,您可以調整視圖大小,改變它在屏幕上的位置,將它淡入或淡出。為了簡單起見,以下部分將向您展示如何分別執行這些操作,要執行它們,只需將所有相關鍵添加到屬性字典。
改變矩形frame
更改視圖或窗口的框架矩形可以讓您調整對象的大小并重新定位其相對于父對象的位置。對view來說,這意味著改變它在父視圖中的位置和大小。對窗口來說,意味著改變它在桌面上的位置和大小。表1展示了改變框架矩形可能用到的部分鍵值對。
表1 視圖或窗口調整所需要的鍵屬性
| NSViewAnimationTargetKey | id | 需要調整大小或位置的視圖或窗口的標識符,此鍵必要 |
| NSViewAnimationStartFrameKey | NSValue | 包含目標對象的開始位置,NSValue對象應該包含一個編碼過的NSRect數據類型。此值通常等于窗口或位置的當前結構。此值可選,默認值為目標對象的當前結構矩形。 |
| NSViewAnimationEndFrameKey | NSValue | 包含目標對象的結束位置,NSValue對象應該包含一個編碼過的NSRect數據類型。此鍵建議填寫,如果沒有給出,默認值為目標對象的當前結構矩形。 |
淡入淡出對象
如果你想隱藏一個視圖或窗口,而不是讓對象突然消失,你可以使用視圖動畫使該對象逐漸消失。同樣地,可以使用類似的動畫方式使視圖再次可見,當視圖漸入時,結束位置的frame必須為非零,若沒有設置,該視圖將仍然隱藏。表2列出了讓一個對象淡入淡出時可設置在屬性字典中的鍵值對。
| NSViewAnimationTargetKey | id | 要修改的NSView或NSWindow對象的標識符,此鍵為必須 |
| NSViewAnimationEffectKey | NSString | 包含以下字符串常量之一:NSViewAnimationFadeInEffect使對象可見,NSViewAnimationFadeOutEffect隱藏它。這些效果在動畫過程中改變了物體的不透明度。 |
一個視圖動畫的例子
Listing1說明了視圖動畫對象的基本用法。action方法為兩個不同的視圖對象設置屬性字典,然后在發生動作時運行動畫。對于第一個視圖對象,動畫對象沿著每個軸將視圖的原點移動50個單位。對于第二個視圖,動畫對象將frame大小縮小到零,同時將視圖淡出,直到完全隱藏。動畫使用定制的時間曲線和持續時間,但使用默認的阻塞模式,這將阻塞用戶在主線程上的輸入,直到動畫完成。
Listing 1 兩個視圖對象的動畫
- (IBAction)startAnimations:(id)sender {// firstView, secondView 是通過outlets屬性連接的xib文件中NSViewAnimation *theAnim;NSRect firstViewFrame;NSRect newViewFrame;NSMutableDictionary* firstViewDict;NSMutableDictionary* secondViewDict;{// 對第一個視圖創建屬性字典.firstViewDict = [NSMutableDictionary dictionaryWithCapacity:3];firstViewFrame = [firstView frame];// 指定要修改的視圖對象.[firstViewDict setObject:firstView forKey:NSViewAnimationTargetKey];// 指定view的開始位置[firstViewDict setObject:[NSValue valueWithRect:firstViewFrame]forKey:NSViewAnimationStartFrameKey];// 指定view的動畫結束位置newViewFrame = firstViewFrame;newViewFrame.origin.x += 50;newViewFrame.origin.y += 50;[firstViewDict setObject:[NSValue valueWithRect:newViewFrame]forKey:NSViewAnimationEndFrameKey];}{// 為第二個視圖創建屬性字典.secondViewDict = [NSMutableDictionary dictionaryWithCapacity:3];[secondViewDict setObject:secondView forKey:NSViewAnimationTargetKey];// 將view從當前位置縮放至看不見.NSRect viewZeroSize = [secondView frame];viewZeroSize.size.width = 0;viewZeroSize.size.height = 0;[secondViewDict setObject:[NSValue valueWithRect:viewZeroSize]forKey:NSViewAnimationEndFrameKey];// 讓視圖淡出[secondViewDict setObject:NSViewAnimationFadeOutEffectforKey:NSViewAnimationEffectKey];}// 創建view 的動畫視圖對象.theAnim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArrayarrayWithObjects:firstViewDict, secondViewDict, nil]];//設置動畫的其他屬性[theAnim setDuration:1.5]; [theAnim setAnimationCurve:NSAnimationEaseIn];// 運行動畫[theAnim startAnimation]; }總結
以上是生活随笔為你收集整理的Cocoa动画编程指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VS插件——番茄助手快捷键的使用教程
- 下一篇: conv2d() received an