主頁 > 移動端開發 > iOS核心影片高級技巧-4

iOS核心影片高級技巧-4

2020-09-11 20:57:29 移動端開發

8. 顯式影片

顯式影片

如果想讓事情變得順利,只有靠自己 -- 夏爾·紀堯姆

上一章介紹了隱式影片的概念,隱式影片是在iOS平臺創建動態用戶界面的一種直接方式,也是UIKit影片機制的基礎,不過它并不能涵蓋所有的影片型別,在這一章中,我們將要研究一下顯式影片,它能夠對一些屬性做指定的自定義影片,或者創建非線性影片,比如沿著任意一條曲線移動,

8.1 屬性影片

屬性影片

CAAnimationDelegate在任何頭檔案中都找不到,但是可以在CAAnimation頭檔案或者蘋果開發者檔案中找到相關函式,在這個例子中,我們用-animationDidStop:finished:方法在影片結束之后來更新圖層的backgroundColor

當更新屬性的時候,我們需要設定一個新的事務,并且禁用圖層行為,否則影片會發生兩次,一個是因為顯式的CABasicAnimation,另一次是因為隱式影片,具體實作見訂單8.3,

一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS交流群:1012951431, 分享BAT,阿里面試題、面試經驗,討論技術, 大家一起交流學習成長!希望幫助開發者少走彎路,

清單8.3 影片完成之后修改圖層的背景色

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create sublayer
    self.colorLayer = [CALayer layer];
    self.colorLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
    self.colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add it to our view
    [self.layerView.layer addSublayer:self.colorLayer];
}

- (IBAction)changeColor
{
    //create a new random color
    CGFloat red = arc4random() / (CGFloat)INT_MAX;
    CGFloat green = arc4random() / (CGFloat)INT_MAX;
    CGFloat blue = arc4random() / (CGFloat)INT_MAX;
    UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
    //create a basic animation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"backgroundColor";
    animation.toValue = (__bridge id)color.CGColor;
    animation.delegate = self;
    //apply animation to layer
    [self.colorLayer addAnimation:animation forKey:nil];
}

- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
    //set the backgroundColor property to match animation toValue
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
    [CATransaction commit];
}

@end

 

CAAnimation而言,使用委托模式而不是一個完成塊會帶來一個問題,就是當你有多個影片的時候,無法在在回呼方法中區分,在一個視圖控制器中創建影片的時候,通常會用控制器本身作為一個委托(如清單8.3所示),但是所有的影片都會呼叫同一個回呼方法,所以你就需要判斷到底是那個圖層的呼叫,

考慮一下第三章的鬧鐘,“圖層幾何學”,我們通過簡單地每秒更新指標的角度來實作一個鐘,但如果指標動態地轉向新的位置會更加真實,

我們不能通過隱式影片來實作因為這些指標都是UIView的實體,所以圖層的隱式影片都被禁用了,我們可以簡單地通過UIView的影片方法來實作,但如果想更好地控制影片時間,使用顯式影片會更好(更多內容見第十章),使用CABasicAnimation來做影片可能會更加復雜,因為我們需要在-animationDidStop:finished:中檢測指標狀態(用于設定結束的位置),

影片本身會作為一個引數傳入委托的方法,也許你會認為可以控制器中把影片存盤為一個屬性,然后在回呼叫比較,但實際上并不起作用,因為委托傳入的影片引數是原始值的一個深拷貝,從而不是同一個值,

當使用-addAnimation:forKey:把影片添加到圖層,這里有一個到目前為止我們都設定為nil的key引數,這里的鍵是-animationForKey:方法找到對應影片的唯一識別符號,而當前影片的所有鍵都可以用animationKeys獲取,如果我們對每個影片都關聯一個唯一的鍵,就可以對每個圖層回圈所有鍵,然后呼叫-animationForKey:來比對結果,盡管這不是一個優雅的實作,

幸運的是,還有一種更加簡單的方法,像所有的NSObject子類一樣,CAAnimation實作了KVC(鍵-值-編碼)協議,于是你可以用-setValue:forKey:-valueForKey:方法來存取屬性,但是CAAnimation有一個不同的性能:它更像一個NSDictionary,可以讓你隨意設定鍵值對,即使和你使用的影片類所宣告的屬性并不匹配,

這意味著你可以對影片用任意型別打標簽,在這里,我們給UIView型別的指標添加的影片,所以可以簡單地判斷影片到除錯于哪個視圖,然后在委托方法中用這個資訊正確地更新鐘的指標(清單8.4),

清單8.4 使用KVC對影片打標簽

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIImageView *hourHand;
@property (nonatomic, weak) IBOutlet UIImageView *minuteHand;
@property (nonatomic, weak) IBOutlet UIImageView *secondHand;
@property (nonatomic, weak) NSTimer *timer;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //adjust anchor points
    self.secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    self.minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    self.hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    //start timer
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];
    //set initial hand positions
    [self updateHandsAnimated:NO];
}

- (void)tick
{
    [self updateHandsAnimated:YES];
}

- (void)updateHandsAnimated:(BOOL)animated
{
    //convert time to hours, minutes and seconds
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
    NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
    CGFloat hourAngle = (components.hour / 12.0) * M_PI * 2.0;
    //calculate hour hand angle //calculate minute hand angle
    CGFloat minuteAngle = (components.minute / 60.0) * M_PI * 2.0;
    //calculate second hand angle
    CGFloat secondAngle = (components.second / 60.0) * M_PI * 2.0;
    //rotate hands
    [self setAngle:hourAngle forHand:self.hourHand animated:animated];
    [self setAngle:minuteAngle forHand:self.minuteHand animated:animated];
    [self setAngle:secondAngle forHand:self.secondHand animated:animated];
}

- (void)setAngle:(CGFloat)angle forHand:(UIView *)handView animated:(BOOL)animated
{
    //generate transform
    CATransform3D transform = CATransform3DMakeRotation(angle, 0, 0, 1);
    if (animated) {
        //create transform animation
        CABasicAnimation *animation = [CABasicAnimation animation];
        [self updateHandsAnimated:NO];
        animation.keyPath = @"transform";
        animation.toValue = [NSValue valueWithCATransform3D:transform];
        animation.duration = 0.5;
        animation.delegate = self;
        [animation setValue:handView forKey:@"handView"];
        [handView.layer addAnimation:animation forKey:nil];
    } else {
        //set transform directly
        handView.layer.transform = transform;
    }
}

- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
    //set final position for hand view
    UIView *handView = [anim valueForKey:@"handView"];
    handView.layer.transform = [anim.toValue CATransform3DValue];
}

 

我們成功的識別出每個圖層停止影片的時間,然后更新它的變換到一個新值,很好,

不幸的是,即使做了這些,還是有個問題,清單8.4在模擬器上運行的很好,但當真正跑在iOS設備上時,我們發現在-animationDidStop:finished:委托方法呼叫之前,指標會迅速回傳到原始值,這個清單8.3圖層顏色發生的情況一樣,

問題在于回呼方法在影片完成之前已經被呼叫了,但不能保證這發生在屬性影片回傳初始狀態之前,這同時也很好地說明了為什么要在真實的設備上測驗影片代碼,而不僅僅是模擬器,

我們可以用一個fillMode屬性來解決這個問題,下一章會詳細說明,這里知道在影片之前設定它比在影片結束之后更新屬性更加方便,

關鍵幀影片

CABasicAnimation揭示了大多數隱式影片背后依賴的機制,這的確很有趣,但是顯式地給圖層添加CABasicAnimation相較于隱式影片而言,只能說費力不討好,

CAKeyframeAnimation是另一種UIKit沒有暴露出來但功能強大的類,和CABasicAnimation類似,CAKeyframeAnimation同樣是CAPropertyAnimation的一個子類,它依然作用于單一的一個屬性,但是和CABasicAnimation不一樣的是,它不限制于設定一個起始和結束的值,而是可以根據一連串隨意的值來做影片,

關鍵幀起源于傳動影片,意思是指主導的影片在顯著改變發生時重繪當前幀(也就是關鍵幀),每幀之間剩下的繪制(可以通過關鍵幀推算出)將由熟練的藝術家來完成,CAKeyframeAnimation也是同樣的道理:你提供了顯著的幀,然后Core Animation在每幀之間進行插入,

我們可以用之前使用顏色圖層的例子來演示,設定一個顏色的陣列,然后通過關鍵幀影片播放出來(清單8.5)

清單8.5 使用CAKeyframeAnimation應用一系列顏色的變化

- (IBAction)changeColor
{
    //create a keyframe animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"backgroundColor";
    animation.duration = 2.0;
    animation.values = @[
                         (__bridge id)[UIColor blueColor].CGColor,
                         (__bridge id)[UIColor redColor].CGColor,
                         (__bridge id)[UIColor greenColor].CGColor,
                         (__bridge id)[UIColor blueColor].CGColor ];
    //apply animation to layer
    [self.colorLayer addAnimation:animation forKey:nil];
}

 

注意到序列中開始和結束的顏色都是藍色,這是因為CAKeyframeAnimation并不能自動把當前值作為第一幀(就像CABasicAnimation那樣把fromValue設為nil),影片會在開始的時候突然跳轉到第一幀的值,然后在影片結束的時候突然恢復到原始的值,所以為了影片的平滑特性,我們需要開始和結束的關鍵幀來匹配當前屬性的值,

當然可以創建一個結束和開始值不同的影片,那樣的話就需要在影片啟動之前手動更新屬性和最后一幀的值保持一致,就和之前討論的一樣,

我們用duration屬性把影片時間從默認的0.25秒增加到2秒,以便于影片做的不那么快,運行它,你會發現影片通過顏色不斷回圈,但效果看起來有些奇怪,原因在于影片以一個恒定的步調在運行,當在每個影片之間過渡的時候并沒有減速,這就產生了一個略微奇怪的效果,為了讓影片看起來更自然,我們需要調整一下緩沖,第十章將會詳細說明,

提供一個陣列的值就可以按照顏色變化做影片,但一般來說用陣列來描述影片運動并不直觀,CAKeyframeAnimation有另一種方式去指定影片,就是使用CGPathpath屬性可以用一種直觀的方式,使用Core Graphics函式定義運動序列來繪制影片,

我們來用一個宇宙飛船沿著一個簡單曲線的實體演示一下,為了創建路徑,我們需要使用一個三次貝塞爾曲線,它是一種使用開始點,結束點和另外兩個控制點來定義形狀的曲線,可以通過使用一個基于C的Core Graphics繪圖指令來創建,不過用UIKit提供的UIBezierPath類會更簡單,

我們這次用CAShapeLayer來在螢屏上繪制曲線,盡管對影片來說并不是必須的,但這會讓我們的影片更加形象,繪制完CGPath之后,我們用它來創建一個CAKeyframeAnimation,然后用它來應用到我們的宇宙飛船,代碼見清單8.6,結果見圖8.1,

清單8.6 沿著一個貝塞爾曲線對圖層做影片

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a path
    UIBezierPath *bezierPath = [[UIBezierPath alloc] init];
    [bezierPath moveToPoint:CGPointMake(0, 150)];
    [bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];
    //draw the path using a CAShapeLayer
    CAShapeLayer *pathLayer = [CAShapeLayer layer];
    pathLayer.path = bezierPath.CGPath;
    pathLayer.fillColor = [UIColor clearColor].CGColor;
    pathLayer.strokeColor = [UIColor redColor].CGColor;
    pathLayer.lineWidth = 3.0f;
    [self.containerView.layer addSublayer:pathLayer];
    //add the ship
    CALayer *shipLayer = [CALayer layer];
    shipLayer.frame = CGRectMake(0, 0, 64, 64);
    shipLayer.position = CGPointMake(0, 150);
    shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
    [self.containerView.layer addSublayer:shipLayer];
    //create the keyframe animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 4.0;
    animation.path = bezierPath.CGPath;
    [shipLayer addAnimation:animation forKey:nil];
}

@end

 

這么做是可行的,但看起來更因為是運氣而不是設計的原因,如果我們把旋轉的值從M_PI(180度)調整到2 * M_PI(360度),然后運行程式,會發現這時候飛船完全不動了,這是因為這里的矩陣做了一次360度的旋轉,和做了0度是一樣的,所以最后的值根本沒變,

現在繼續使用M_PI,但這次用byValue而不是toValue,也許你會認為這和設定toValue結果一樣,因為0 + 90度 == 90度,但實際上飛船的圖片變大了,并沒有做任何旋轉,這是因為變換矩陣不能像角度值那樣疊加,

那么如果需要獨立于角度之外單獨對平移或者縮放做影片呢?由于都需要我們來修改transform屬性,實時地重新計算每個時間點的每個變換效果,然后根據這些創建一個復雜的關鍵幀影片,這一切都是為了對圖層的一個獨立做一個簡單的影片,

幸運的是,有一個更好的解決方案:為了旋轉圖層,我們可以對transform.rotation關鍵路徑應用影片,而不是transform本身(清單8.9),

清單8.9 對虛擬的transform.rotation屬性做影片

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //add the ship
    CALayer *shipLayer = [CALayer layer];
    shipLayer.frame = CGRectMake(0, 0, 128, 128);
    shipLayer.position = CGPointMake(150, 150);
    shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
    [self.containerView.layer addSublayer:shipLayer];
    //animate the ship rotation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform.rotation";
    animation.duration = 2.0;
    animation.byValue = @(M_PI * 2);
    [shipLayer addAnimation:animation forKey:nil];
}

@end

 

結果運行的特別好,用transform.rotation而不是transform做影片的好處如下:

  • 我們可以不通過關鍵幀一步旋轉多于180度的影片,

  • 可以用相對值而不是絕對值旋轉(設定byValue而不是toValue),

  • 可以不用創建CATransform3D,而是使用一個簡單的數值來指定角度,

  • 不會和transform.position或者transform.scale沖突(同樣是使用關鍵路徑來做獨立的影片屬性),

transform.rotation屬性有一個奇怪的問題是它其實并不存在,這是因為CATransform3D并不是一個物件,它實際上是一個結構體,也沒有符合KVC相關屬性,transform.rotation實際上是一個CALayer用于處理影片變換的虛擬屬性,

你不可以直接設定transform.rotation或者transform.scale,他們不能被直接使用,當你對他們做影片時,Core Animation自動地根據通過CAValueFunction來計算的值來更新transform屬性,

CAValueFunction用于把我們賦給虛擬的transform.rotation簡單浮點值轉換成真正的用于擺放圖層的CATransform3D矩陣值,你可以通過設定CAPropertyAnimationvalueFunction屬性來改變,于是你設定的函式將會覆寫默認的函式,

CAValueFunction看起來似乎是對那些不能簡單相加的屬性(例如變換矩陣)做影片的非常有用的機制,但由于CAValueFunction的實作細節是私有的,所以目前不能通過繼承它來自定義,你可以通過使用蘋果目前已近提供的常量(目前都是和變換矩陣的虛擬屬性相關,所以沒太多使用場景了,因為這些屬性都有了默認的實作方式),

8.2 影片組

影片組

CABasicAnimationCAKeyframeAnimation僅僅作用于單獨的屬性,而CAAnimationGroup可以把這些影片組合在一起,CAAnimationGroup是另一個繼承于CAAnimation的子類,它添加了一個animations陣列的屬性,用來組合別的影片,我們把清單8.6那種關鍵幀影片和調整圖層背景色的基礎影片組合起來(清單8.10),結果如圖8.3所示,

清單8.10 組合關鍵幀影片和基礎影片

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a path
    UIBezierPath *bezierPath = [[UIBezierPath alloc] init];
    [bezierPath moveToPoint:CGPointMake(0, 150)];
    [bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];
    //draw the path using a CAShapeLayer
    CAShapeLayer *pathLayer = [CAShapeLayer layer];
    pathLayer.path = bezierPath.CGPath;
    pathLayer.fillColor = [UIColor clearColor].CGColor;
    pathLayer.strokeColor = [UIColor redColor].CGColor;
    pathLayer.lineWidth = 3.0f;
    [self.containerView.layer addSublayer:pathLayer];
    //add a colored layer
    CALayer *colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(0, 0, 64, 64);
    colorLayer.position = CGPointMake(0, 150);
    colorLayer.backgroundColor = [UIColor greenColor].CGColor;
    [self.containerView.layer addSublayer:colorLayer];
    //create the position animation
    CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animation];
    animation1.keyPath = @"position";
    animation1.path = bezierPath.CGPath;
    animation1.rotationMode = kCAAnimationRotateAuto;
    //create the color animation
    CABasicAnimation *animation2 = [CABasicAnimation animation];
    animation2.keyPath = @"backgroundColor";
    animation2.toValue = (__bridge id)[UIColor redColor].CGColor;
    //create group animation
    CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
    groupAnimation.animations = @[animation1, animation2]; 
    groupAnimation.duration = 4.0;
    //add the animation to the color layer
    [colorLayer addAnimation:groupAnimation forKey:nil];
}

 

8.3 過渡

過渡

有時候對于iOS應用程式來說,希望能通過屬性影片來對比較難做影片的布局進行一些改變,比如交換一段文本和圖片,或者用一段網格視圖來替換,等等,屬性影片只對圖層的可影片屬性起作用,所以如果要改變一個不能影片的屬性(比如圖片),或者從層級關系中添加或者移除圖層,屬性影片將不起作用,

于是就有了過渡的概念,過渡并不像屬性影片那樣平滑地在兩個值之間做影片,而是影響到整個圖層的變化,過渡影片首先展示之前的圖層外觀,然后通過一個交換過渡到新的外觀,

為了創建一個過渡影片,我們將使用CATransition,同樣是另一個CAAnimation的子類,和別的子類不同,CATransition有一個type和subtype來標識變換效果,type屬性是一個NSString型別,可以被設定成如下型別:

kCATransitionFade 
kCATransitionMoveIn 
kCATransitionPush 
kCATransitionReveal

 

到目前為止你只能使用上述四種型別,但你可以通過一些別的方法來自定義過渡效果,后續會詳細介紹,

默認的過渡型別是kCATransitionFade,當你在改變圖層屬性之后,就創建了一個平滑的淡入淡出效果,

我們在第七章的例子中就已經用到過kCATransitionPush,它創建了一個新的圖層,從邊緣的一側滑動進來,把舊圖層從另一側推出去的效果,

kCATransitionMoveInkCATransitionRevealkCATransitionPush類似,都實作了一個定向滑動的影片,但是有一些細微的不同,kCATransitionMoveIn從頂部滑動進入,但不像推送影片那樣把老土層推走,然而kCATransitionReveal把原始的圖層滑動出去來顯示新的外觀,而不是把新的圖層滑動進入,

后面三種過渡型別都有一個默認的影片方向,它們都從左側滑入,但是你可以通過subtype來控制它們的方向,提供了如下四種型別:

kCATransitionFromRight 
kCATransitionFromLeft 
kCATransitionFromTop 
kCATransitionFromBottom

 

一個簡單的用CATransition來對非影片屬性做影片的例子如清單8.11所示,這里我們對UIImage的image屬性做修改,但是隱式影片或者CAPropertyAnimation都不能對它做影片,因為Core Animation不知道如何在插圖圖片,通過對圖層應用一個淡入淡出的過渡,我們可以忽略它的內容來做平滑影片(圖8.4),我們來嘗試修改過渡的type常量來觀察其它效果,

清單8.11 使用CATransition來對UIImageView做影片

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIImageView *imageView;
@property (nonatomic, copy) NSArray *images;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //set up images
    self.images = @[[UIImage imageNamed:@"Anchor.png"],
                    [UIImage imageNamed:@"Cone.png"],
                    [UIImage imageNamed:@"Igloo.png"],
                    [UIImage imageNamed:@"Spaceship.png"]];
}

- (IBAction)switchImage
{
    //set up crossfade transition
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionFade;
    //apply transition to imageview backing layer
    [self.imageView.layer addAnimation:transition forKey:nil];
    //cycle to next image
    UIImage *currentImage = self.imageView.image;
    NSUInteger index = [self.images indexOfObject:currentImage];
    index = (index + 1) % [self.images count];
    self.imageView.image = self.images[index];
}

@end

 

你可以從代碼中看出,過渡影片和之前的屬性影片或者影片組添加到圖層上的方式一致,都是通過-addAnimation:forKey:方法,但是和屬性影片不同的是,對指定的圖層一次只能使用一次CATransition,因此,無論你對影片的鍵設定什么值,過渡影片都會對它的鍵設定成“transition”,也就是常量kCATransition

8.4 在影片程序中取消影片

在影片程序中取消影片

之前提到過,你可以用-addAnimation:forKey:方法中的key引數來在添加影片之后檢索一個影片,使用如下方法:

- (CAAnimation *)animationForKey:(NSString *)key;

 

但并不支持在影片運行程序中修改影片,所以這個方法主要用來檢測影片的屬性,或者判斷它是否被添加到當前圖層中,

為了終止一個指定的影片,你可以用如下方法把它從圖層移除掉:

- (void)removeAnimationForKey:(NSString *)key;

 

或者移除所有影片:

- (void)removeAllAnimations;

 

影片一旦被移除,圖層的外觀就立刻更新到當前的模型圖層的值,一般說來,影片在結束之后被自動移除,除非設定removedOnCompletionNO,如果你設定影片在結束之后不被自動移除,那么當它不需要的時候你要手動移除它;否則它會一直存在于記憶體中,直到圖層被銷毀,

我們來擴展之前旋轉飛船的示例,這里添加一個按鈕來停止或者啟動影片,這一次我們用一個非nil的值作為影片的鍵,以便之后可以移除它,-animationDidStop:finished:方法中的flag引數表明了影片是自然結束還是被打斷,我們可以在控制臺列印出來,如果你用停止按鈕來終止影片,它會列印NO,如果允許它完成,它會列印YES

清單8.15是更新后的示例代碼,圖8.6顯示了結果,

清單8.15 開始和停止一個影片

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic, strong) CALayer *shipLayer;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //add the ship
    self.shipLayer = [CALayer layer];
    self.shipLayer.frame = CGRectMake(0, 0, 128, 128);
    self.shipLayer.position = CGPointMake(150, 150);
    self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
    [self.containerView.layer addSublayer:self.shipLayer];
}

- (IBAction)start
{
    //animate the ship rotation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform.rotation";
    animation.duration = 2.0;
    animation.byValue = @(M_PI * 2);
    animation.delegate = self;
    [self.shipLayer addAnimation:animation forKey:@"rotateAnimation"];
}

- (IBAction)stop
{
    [self.shipLayer removeAnimationForKey:@"rotateAnimation"];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    //log that the animation stopped
    NSLog(@"The animation stopped (finished: %@)", flag? @"YES": @"NO");
}

@end

 

8.5 總結

總結

這一章中,我們涉及了屬性影片(你可以對單獨的圖層屬性影片有更加具體的控制),影片組(把多個屬性影片組合成一個獨立單元)以及過度(影響整個圖層,可以用來對圖層的任何內容做任何型別的影片,包括子圖層的添加和移除),

在第九章中,我們繼續學習CAMediaTiming協議,來看一看Core Animation是怎樣處理逝去的時間,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/7501.html

標籤:iOS

上一篇:iOS核心影片高級技巧 - 3

下一篇:如何用Linux重現《黑客帝國》中的經典界面?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more