上十章節:
- 圖層樹
- 圖層的寄宿圖
- 圖層幾何學
- 圖層視覺效果
- 圖層變換
- 專用圖層
- 隐式動畫
- 顯式動畫
- 圖層時間
- 圖層緩沖
這篇随筆主要介紹有關基于定時器的動畫。
定時幀:
- 示範例子://使用NSTimer實作彈性球動畫
//add ball image view
UIImage *ballImage = [UIImage imageNamed:@"Ball.png"];
self.ballView = [[UIImageView alloc] initWithImage:ballImage];
[self.containerView addSubview:self.ballView];
//animate
float interpolate(float from, float to, float time){
return (to - from) * time + from;
}
float bounceEaseOut(float t){
if (t < 4/11.0) {
return (121 * t * t)/16.0;
} else if (t < 8/11.0) {
return (363/40.0 * t * t) - (99/10.0 * t) + 17/5.0;
} else if (t < 9/10.0) {
return (4356/361.0 * t * t) - (35442/1805.0 * t) + 16061/1805.0;
}
return (54/5.0 * t * t) - (513/25.0 * t) + 268/25.0;
}
- (id)interpolateFromValue:(id)fromValue toValue:(id)toValue time:(float)time{
if ([fromValue isKindOfClass:[NSValue class]]) {
//get type
const char *type = [(NSValue *)fromValue objCType];
if (strcmp(type, @encode(CGPoint)) == 0) {
CGPoint from = [fromValue CGPointValue];
CGPoint to = [toValue CGPointValue];
CGPoint result = CGPointMake(interpolate(from.x, to.x, time), interpolate(from.y, to.y, time));
return [NSValue valueWithCGPoint:result];
}
}
//provide safe default implementation
return (time < 0.5)? fromValue: toValue;
}
- (void)animate{
//reset ball to top of screen
self.ballView.center = CGPointMake(150, 32);
//configure the animation
self.duration = 1.0;
self.timeOffset = 0.0;
self.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 32)];
self.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 268)];
//stop the timer if it's already running
[self.timer invalidate];
//start the timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1/60.0
target:self
selector:@selector(step:)
userInfo:nil
repeats:YES];
}
- (void)step:(NSTimer *)step{
//update time offset
self.timeOffset = MIN(self.timeOffset + 1/60.0, self.duration);
//get normalized time offset (in range 0 - 1)
float time = self.timeOffset / self.duration;
//apply easing
time = bounceEaseOut(time);
//interpolate position
id position = [self interpolateFromValue:self.fromValue
toValue:self.toValue
time:time];
//move ball view to new position
self.ballView.center = [position CGPointValue];
//stop the timer if we've reached the end of the animation
if (self.timeOffset >= self.duration) {
[self.timer invalidate];
self.timer = nil;
}
}
NSTimer工作原理:
- iOS上的每個線程都管理了一個NSRunloop,用一個循環來完成一些任務清單
- 對于主線程,這些任務包含如下幾項:
- 1.處理觸摸事件
- 2.發送和接受網絡資料包
- 3.執行使用gcd的代碼
- 4.處理計時器行為
- 5.螢幕重繪
- 當設定一個NSTimer,會被插入到目前任務清單中,然後直到指定時間過去之後(他的上一個任務完成之後)才會被執行,這通常會導緻有幾毫秒的延遲,具體看上個任務結束需要多久。
- 于是就不能保證定時器精準地一秒鐘執行六十次,可以通過一些途徑來優化:
1.用CADisplayLink讓更新頻率嚴格控制在每次螢幕重新整理之後
2.基于真實幀的持續時間而不是假設的更新頻率來做動畫
3.調整動畫計時器的run loop模式,這樣就不會被别的事件幹擾
-
-
- 1.CADisplayLink:
- CoreAnimation提供的另一個類似于NSTimer的類
- 它總是在螢幕完成一次更新之前啟動
- 有一個整型的frameInterval屬性,指定了間隔多少幀之後才執行。預設值是1,意味着每次螢幕更新之前都會執行一次
- 如果動畫的代碼執行起來超過了六十分之一秒,你可以指定frameInterval為2,就是說動畫每隔一幀執行一次(一秒鐘30幀)或者3,也就是一秒鐘20次,等等。
- 2.計算幀的持續時間:
- 在每幀開始重新整理的時候用CACurrentMediaTime()記錄目前時間,然後和上一幀記錄的時間去比較。
- 通過比較這些時間,我們就可以得到真實的每幀持續的時間,然後代替寫死的六十分之一秒
- 示範例子:// 通過測量沒幀持續的時間來使得動畫更加平滑
- //add ball image view
- UIImage *ballImage = [UIImage imageNamed:@"Ball.png"];
- self.ballView = [[UIImageView alloc] initWithImage:ballImage];
- [self.containerView addSubview:self.ballView];
- //animate
- float interpolate(float from, float to, float time){
- return (to - from) * time + from;
- }
- float bounceEaseOut(float t){
- if (t < 4/11.0) {
- return (121 * t * t)/16.0;
- } else if (t < 8/11.0) {
- return (363/40.0 * t * t) - (99/10.0 * t) + 17/5.0;
- } else if (t < 9/10.0) {
- return (4356/361.0 * t * t) - (35442/1805.0 * t) + 16061/1805.0;
- }
- return (54/5.0 * t * t) - (513/25.0 * t) + 268/25.0;
- }
- - (id)interpolateFromValue:(id)fromValue toValue:(id)toValue time:(float)time{
- if ([fromValue isKindOfClass:[NSValue class]]) {
- //get type
- const char *type = [(NSValue *)fromValue objCType];
- if (strcmp(type, @encode(CGPoint)) == 0) {
- CGPoint from = [fromValue CGPointValue];
- CGPoint to = [toValue CGPointValue];
- CGPoint result = CGPointMake(interpolate(from.x, to.x, time), interpolate(from.y, to.y, time));
- return [NSValue valueWithCGPoint:result];
- }
- }
- //provide safe default implementation
- return (time < 0.5)? fromValue: toValue;
- }
- - (void)animate{
- //reset ball to top of screen
- self.ballView.center = CGPointMake(150, 32);
- //configure the animation
- self.duration = 1.0;
- self.timeOffset = 0.0;
- self.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 32)];
- self.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 268)];
- //stop the timer if it's already running
- [self.timer invalidate];
- //start the timer
- self.lastStep = CACurrentMediaTime();
- self.timer = [CADisplayLink displayLinkWithTarget:self
- selector:@selector(step:)];
- [self.timer addToRunLoop:[NSRunLoop mainRunLoop]
- forMode:NSDefaultRunLoopMode];
- }
- - (void)step:(CADisplayLink *)timer{
- //calculate time delta
- CFTimeInterval thisStep = CACurrentMediaTime();
- CFTimeInterval stepDuration = thisStep - self.lastStep;
- self.lastStep = thisStep;
- //update time offset
- self.timeOffset = MIN(self.timeOffset + stepDuration, self.duration);
- //get normalized time offset (in range 0 - 1)
- float time = self.timeOffset / self.duration;
- //apply easing
- time = bounceEaseOut(time);
- //interpolate position
- id position = [self interpolateFromValue:self.fromValue toValue:self.toValue
- time:time];
- //move ball view to new position
- self.ballView.center = [position CGPointValue];
- //stop the timer if we've reached the end of the animation
- if (self.timeOffset >= self.duration) {
- [self.timer invalidate];
- self.timer = nil;
- }
- }
- 3.RunLoop模式:
- 當建立CADisplayLink的時候,我們需要指定一個run loop和run loop mode,使用了主線程的run loop
- 因為任何使用者界面的更新都需要在主線程執行
- 一些常見的run loop模式如下:
- NSDefaultRunLoopMode - 标準優先級
- NSRunLoopCommonModes - 高優先級
- UITrackingRunLoopMode - 用于UIScrollView和别的控件的動畫
- 對CADisplayLink指定多個run loop模式,于是我們可以同時加入NSDefaultRunLoopMode和UITrackingRunLoopMode來保證它不會被滑動打斷,也不會被其他UIKit控件動畫影響性能,像這樣:
- self.timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(step:)];
- [self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [self.timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];
- 和CADisplayLink類似,NSTimer同樣也可以使用不同的run loop模式配置,通過别的函數,而不是+scheduledTimerWithTimeInterval:構造器:
- self.timer = [NSTimer timerWithTimeInterval:1/60.0
- target:self
- selector:@selector(step:)
- userInfo:nil
- repeats:YES];
- [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
- 1.CADisplayLink:
-
- 實體模拟:
- ios有自帶的實體引擎UIDynamic。
轉載于:https://www.cnblogs.com/Jk-Chan/p/5270843.html