CAMediaTiming`協議(9.1 圖層時間)

来源:http://www.cnblogs.com/EchoHG/archive/2017/10/04/7627866.html
-Advertisement-
Play Games

#CAMediaTiming`協議 CAMediaTiming協議定義了在一段動畫內用來控制逝去時間的屬性的集合,CALayer和CAAnimation都實現了這個協議,所以時間可以被任意基於一個圖層或者一段動畫的類控制。 持續和重覆 我們在第八章“顯式動畫”中簡單提到過duration(CAMed ...


#CAMediaTiming`協議

CAMediaTiming協議定義了在一段動畫內用來控制逝去時間的屬性的集合,CALayerCAAnimation都實現了這個協議,所以時間可以被任意基於一個圖層或者一段動畫的類控制。

持續和重覆

我們在第八章“顯式動畫”中簡單提到過durationCAMediaTiming的屬性之一),duration是一個CFTimeInterval的類型(類似於NSTimeInterval的一種雙精度浮點類型),對將要進行的動畫的一次迭代指定了時間。

這裡的一次迭代是什麼意思呢?CAMediaTiming另外還有一個屬性叫做repeatCount,代表動畫重覆的迭代次數。如果duration是2,repeatCount設為3.5(三個半迭代),那麼完整的動畫時長將是7秒。

durationrepeatCount預設都是0。但這不意味著動畫時長為0秒,或者0次,這裡的0僅僅代表了“預設”,也就是0.25秒和1次,你可以用一個簡單的測試來嘗試為這兩個屬性賦多個值,如清單9.1,圖9.1展示了程式的結果。

清單9.1 測試durationrepeatCount

 1 @interface ViewController ()
 2 
 3 @property (nonatomic, weak) IBOutlet UIView *containerView;
 4 @property (nonatomic, weak) IBOutlet UITextField *durationField;
 5 @property (nonatomic, weak) IBOutlet UITextField *repeatField;
 6 @property (nonatomic, weak) IBOutlet UIButton *startButton;
 7 @property (nonatomic, strong) CALayer *shipLayer;
 8 
 9 @end
10 
11 @implementation ViewController
12 
13 - (void)viewDidLoad
14 {
15     [super viewDidLoad];
16     //add the ship
17     self.shipLayer = [CALayer layer];
18     self.shipLayer.frame = CGRectMake(0, 0, 128, 128);
19     self.shipLayer.position = CGPointMake(150, 150);
20     self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
21     [self.containerView.layer addSublayer:self.shipLayer];
22 }
23 
24 - (void)setControlsEnabled:(BOOL)enabled
25 {
26     for (UIControl *control in @[self.durationField, self.repeatField, self.startButton]) {
27         control.enabled = enabled;
28         control.alpha = enabled? 1.0f: 0.25f;
29     }
30 }
31 
32 - (IBAction)hideKeyboard
33 {
34     [self.durationField resignFirstResponder];
35     [self.repeatField resignFirstResponder];
36 }
37 
38 - (IBAction)start
39 {
40     CFTimeInterval duration = [self.durationField.text doubleValue];
41     float repeatCount = [self.repeatField.text floatValue];
42     //animate the ship rotation
43     CABasicAnimation *animation = [CABasicAnimation animation];
44     animation.keyPath = @"transform.rotation";
45     animation.duration = duration;
46     animation.repeatCount = repeatCount;
47     animation.byValue = @(M_PI * 2);
48     animation.delegate = self;
49     [self.shipLayer addAnimation:animation forKey:@"rotateAnimation"];
50     //disable controls
51     [self setControlsEnabled:NO];
52 }
53 
54 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
55 {
56     //reenable controls
57     [self setControlsEnabled:YES];
58 }
59 
60 @end
View Code

圖9.1 演示durationrepeatCount的測試程式

創建重覆動畫的另一種方式是使用repeatDuration屬性,它讓動畫重覆一個指定的時間,而不是指定次數。你甚至設置一個叫做autoreverses的屬性(BOOL類型)在每次間隔交替迴圈過程中自動回放。這對於播放一段連續非迴圈的動畫很有用,例如打開一扇門,然後關上它(圖9.2)。

 

圖9.2 擺動門的動畫

對門進行擺動的代碼見清單9.2。我們用了autoreverses來使門在打開後自動關閉,在這裡我們把repeatDuration設置為INFINITY,於是動畫無限迴圈播放,設置repeatCountINFINITY也有同樣的效果。註意repeatCountrepeatDuration可能會相互衝突,所以你只要對其中一個指定非零值。對兩個屬性都設置非0值的行為沒有被定義。

清單9.2 使用autoreverses屬性實現門的搖擺

 

@interface ViewController ()

@property (nonatomic, weak) UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //add the door
    CALayer *doorLayer = [CALayer layer];
    doorLayer.frame = CGRectMake(0, 0, 128, 256);
    doorLayer.position = CGPointMake(150 - 64, 150);
    doorLayer.anchorPoint = CGPointMake(0, 0.5);
    doorLayer.contents = (__bridge id)[UIImage imageNamed: @"Door.png"].CGImage;
    [self.containerView.layer addSublayer:doorLayer];
    //apply perspective transform
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    self.containerView.layer.sublayerTransform = perspective;
    //apply swinging animation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform.rotation.y";
    animation.toValue = @(-M_PI_2);
    animation.duration = 2.0;
    animation.repeatDuration = INFINITY;
    animation.autoreverses = YES;
    [doorLayer addAnimation:animation forKey:nil];
}

@end
View Code

相對時間

每次討論到Core Animation,時間都是相對的,每個動畫都有它自己描述的時間,可以獨立地加速,延時或者偏移。

beginTime指定了動畫開始之前的的延遲時間。這裡的延遲從動畫添加到可見圖層的那一刻開始測量,預設是0(就是說動畫會立刻執行)。

speed是一個時間的倍數,預設1.0,減少它會減慢圖層/動畫的時間,增加它會加快速度。如果2.0的速度,那麼對於一個duration為1的動畫,實際上在0.5秒的時候就已經完成了。

timeOffsetbeginTime類似,但是和增加beginTime導致的延遲動畫不同,增加timeOffset只是讓動畫快進到某一點,例如,對於一個持續1秒的動畫來說,設置timeOffset為0.5意味著動畫將從一半的地方開始。

beginTime不同的是,timeOffset並不受speed的影響。所以如果你把speed設為2.0,把timeOffset設置為0.5,那麼你的動畫將從動畫最後結束的地方開始,因為1秒的動畫實際上被縮短到了0.5秒。然而即使使用了timeOffset讓動畫從結束的地方開始,它仍然播放了一個完整的時長,這個動畫僅僅是迴圈了一圈,然後從頭開始播放。

可以用清單9.3的測試程式驗證一下,設置speedtimeOffset滑塊到隨意的值,然後點擊播放來觀察效果(見圖9.3)

清單9.3 測試timeOffsetspeed屬性

 

 1 @interface ViewController ()
 2 
 3 @property (nonatomic, weak) IBOutlet UIView *containerView;
 4 @property (nonatomic, weak) IBOutlet UILabel *speedLabel;
 5 @property (nonatomic, weak) IBOutlet UILabel *timeOffsetLabel;
 6 @property (nonatomic, weak) IBOutlet UISlider *speedSlider;
 7 @property (nonatomic, weak) IBOutlet UISlider *timeOffsetSlider;
 8 @property (nonatomic, strong) UIBezierPath *bezierPath;
 9 @property (nonatomic, strong) CALayer *shipLayer;
10 
11 @end
12 
13 @implementation ViewController
14 
15 - (void)viewDidLoad
16 {
17     [super viewDidLoad];
18     //create a path
19     self.bezierPath = [[UIBezierPath alloc] init];
20     [self.bezierPath moveToPoint:CGPointMake(0, 150)];
21     [self.bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];
22     //draw the path using a CAShapeLayer
23     CAShapeLayer *pathLayer = [CAShapeLayer layer];
24     pathLayer.path = self.bezierPath.CGPath;
25     pathLayer.fillColor = [UIColor clearColor].CGColor;
26     pathLayer.strokeColor = [UIColor redColor].CGColor;
27     pathLayer.lineWidth = 3.0f;
28     [self.containerView.layer addSublayer:pathLayer];
29     //add the ship
30     self.shipLayer = [CALayer layer];
31     self.shipLayer.frame = CGRectMake(0, 0, 64, 64);
32     self.shipLayer.position = CGPointMake(0, 150);
33     self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
34     [self.containerView.layer addSublayer:self.shipLayer];
35     //set initial values
36     [self updateSliders];
37 }
38 
39 - (IBAction)updateSliders
40 {
41     CFTimeInterval timeOffset = self.timeOffsetSlider.value;
42     self.timeOffsetLabel.text = [NSString stringWithFormat:@"%0.2f", timeOffset];
43     float speed = self.speedSlider.value;
44     self.speedLabel.text = [NSString stringWithFormat:@"%0.2f", speed];
45 }
46 
47 - (IBAction)play
48 {
49     //create the keyframe animation
50     CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
51     animation.keyPath = @"position";
52     animation.timeOffset = self.timeOffsetSlider.value;
53     animation.speed = self.speedSlider.value;
54     animation.duration = 1.0;
55     animation.path = self.bezierPath.CGPath;
56     animation.rotationMode = kCAAnimationRotateAuto;
57     animation.removedOnCompletion = NO;
58     [self.shipLayer addAnimation:animation forKey:@"slide"];
59 }
60 
61 @end
View Code

圖9.3 測試時間偏移和速度的簡單的應用程式

fillMode

對於beginTime非0的一段動畫來說,會出現一個當動畫添加到圖層上但什麼也沒發生的狀態。類似的,removeOnCompletion被設置為NO的動畫將會在動畫結束的時候仍然保持之前的狀態。這就產生了一個問題,當動畫開始之前和動畫結束之後,被設置動畫的屬性將會是什麼值呢?

一種可能是屬性和動畫沒被添加之前保持一致,也就是在模型圖層定義的值(見第七章“隱式動畫”,模型圖層和呈現圖層的解釋)。

另一種可能是保持動畫開始之前那一幀,或者動畫結束之後的那一幀。這就是所謂的填充,因為動畫開始和結束的值用來填充開始之前和結束之後的時間。

這種行為就交給開發者了,它可以被CAMediaTimingfillMode來控制。fillMode是一個NSString類型,可以接受如下四種常量:

kCAFillModeForwards 
kCAFillModeBackwards 
kCAFillModeBoth 
kCAFillModeRemoved

預設是kCAFillModeRemoved,當動畫不再播放的時候就顯示圖層模型指定的值剩下的三種類型向前,向後或者即向前又向後去填充動畫狀態,使得動畫在開始前或者結束後仍然保持開始和結束那一刻的值。

這就對避免在動畫結束的時候急速返回提供另一種方案(見第八章)。但是記住了,當用它來解決這個問題的時候,需要把removeOnCompletion設置為NO,另外需要給動畫添加一個非空的鍵,於是可以在不需要動畫的時候把它從圖層上移除。

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android Studio(2.3.3) 在給代碼混淆時,提示: 簡單說就是找不到"aapt_rules.txt"這個文件,解決方法也很簡單: 1、菜單欄選擇 build clean project 2、重新打包(build) 這裡只列出解決方法,詳細的說明請看以下這篇文章: http://blo ...
  • iOS9 發佈後,產生了一個使 App Thinning 無法正常運行的 bug。在iOS9.0.2 版本中,這個 bug 已經被修複,App Thinning 已經可以正常使用。當你從應用商店(App Store)下載應用時,請註意這點。iOS9 推出之後,大受歡迎。僅僅數周,已經有超過半數的 i ...
  • 之前沒有設置過打包的命名,每次打包都是預設的"app realease.apk",之後手動修改名字來顯示出它是一個新版本。 晚上學習瞭如何配置打包名稱,很簡單,修改build.gradle里的代碼就行。 詳細記錄如下: 1、打開app這個directory下的build.gradle 2、定義打包時 ...
  • 軟體繪圖 術語繪圖通常在Core Animation的上下文中指代軟體繪圖(意即:不由GPU協助的繪圖)。在iOS中,軟體繪圖通常是由Core Graphics框架完成來完成。但是,在一些必要的情況下,相比Core Animation和OpenGL,Core Graphics要慢了不少。 軟體繪圖不 ...
  • 物理模擬 即使使用了基於定時器的動畫來複制第10章中關鍵幀的行為,但還是會有一些本質上的區別:在關鍵幀的實現中,我們提前計算了所有幀,但是在新的解決方案中,我們實際上實在按需要在計算。意義在於我們可以根據用戶輸入實時修改動畫的邏輯,或者和別的實時動畫系統例如物理引擎進行整合。 Chipmunk 我們 ...
  • 自定義緩衝函數 在第八章中,我們給時鐘項目添加了動畫。看起來很贊,但是如果有合適的緩衝函數就更好了。在顯示世界中,鐘錶指針轉動的時候,通常起步很慢,然後迅速啪地一聲,最後緩衝到終點。但是標準的緩衝函數在這裡每一個適合它,那該如何創建一個新的呢? 除了+functionWithName:之外,CAMe ...
  • 直接寫解決方法吧: 1、將XML里的靜態引入刪除: 2、在代碼里 findViewById 找到 NavigationView,然後引入 Header 和 Menu,再獲取頭部佈局,最後設置監聽事件: 3、完整代碼如下: ...
  • 動畫速度 動畫實際上就是一段時間內的變化,這就暗示了變化一定是隨著某個特定的速率進行。速率由以下公式計算而來: 這裡的變化可以指的是一個物體移動的距離,時間指動畫持續的時長,用這樣的一個移動可以更加形象的描述(比如position和bounds屬性的動畫),但實際上它應用於任意可以做動畫的屬性(比如 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...