iOS 動(dòng)畫 第八章 顯示動(dòng)畫

      //CABasicAnimation
    //[self basicAnimationTest];
    
    //animationDelegate
    //[self animationDelegateTest];
    
    //clockViewAnimationTest
    //[self clockViewAnimationTest];
    
    //keyframeAnimation
    //[self keyframeAnimationTest];
    
    //CGPath
    //[self keyframeAnimationPathTest];
    
    //虛擬屬性
    //[self valueRotationTest];
    //[self virtualValueRotationTest];

    //動(dòng)畫數(shù)組
    //[self animationGroupTest];
    
    //過渡
    //[self transitionTest];
    //自定義動(dòng)畫
    //[self transitionSelfTest];
    
    //截圖
    //[self performTransition];
    
    //取消動(dòng)畫
    //[self cancelAnimationTest];
    
    //duration repeatCount
    //[self durationAndRepeatCountTest];
    
    //repeatDuration
    //[self repeatDurationTest];
    
    //timeOffset and speed
    //[self timeOffsetAndSpeedTest];
    
    //handle animation
    //[self handleAnimationTest];

屬性動(dòng)畫

//屬性動(dòng)畫:作用于圖層的某個(gè)單一屬性,并指定了它的一個(gè)目標(biāo)值,或者一連串將要做動(dòng)畫的值。 分為兩種:基礎(chǔ)和關(guān)鍵幀

基礎(chǔ)動(dòng)畫

//CABasicAnimation是CAPropertyAnimation的一個(gè)子類,而CAPropertyAnimation的父類是CAAnimation,CAAnimation同時(shí)也是Core Animation所有動(dòng)畫類型的抽象基類。
//CAAnimation提供了一個(gè)計(jì)時(shí)函數(shù),一個(gè)委托(用于反饋動(dòng)畫狀態(tài))以及一個(gè)removedOnCompletion,用于標(biāo)識動(dòng)畫是否該在結(jié)束后自動(dòng)釋放(默認(rèn)YES,為了防止內(nèi)存泄露)。CAAnimation同時(shí)實(shí)現(xiàn)了一些協(xié)議,包括CAAction(允許CAAnimation的子類可以提供圖層行為),以及CAMediaTiming
//CAPropertyAnimation : keyPath 僅可以作用于圖層本身的屬性,而且還包含了它的子成員的屬性,甚至是一些虛擬的屬性
//CABasicAnimation繼承于CAPropertyAnimation,add property id fromValue id toValue id byValue
//fromValue代表了動(dòng)畫開始之前屬性的值,toValue代表了動(dòng)畫結(jié)束之后的值,byValue代表了動(dòng)畫執(zhí)行過程中改變的值。 只需要指定toValue或者byValue
//Type Object Type Code Example
//CGFloat NSNumber id obj = @(float);
//CGPoint NSValue id obj = [NSValue valueWithCGPoint:point);
//CGSize NSValue id obj = [NSValue valueWithCGSize:size);
//CGRect NSValue id obj = [NSValue valueWithCGRect:rect);
//CATransform3D NSValue id obj = [NSValue valueWithCATransform3D:transform);
//CGImageRef id id obj = (__bridge id)imageRef;
//CGColorRef id id obj = (__bridge id)colorRef;

- (void)basicAnimationTest {
    layerView  = [[UIView alloc] init];
    layerView.frame = CGRectMake(0, 20, 320, 320);
    [self.view addSubview:layerView];
    //add change color button
    UIButton *changeColorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    changeColorBtn.frame = CGRectMake(50, 200, 200, 44.0);
    [changeColorBtn setTitle:@"change Color" forState:(UIControlStateNormal)];
    [changeColorBtn addTarget:self action:@selector(changeColorBasicAnimationAction2) forControlEvents:UIControlEventTouchUpInside];
    changeColorBtn.backgroundColor = [UIColor redColor];
    [layerView addSubview:changeColorBtn];
    
    //create sublayer
    colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0f);
    colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add it to our view
    [layerView.layer addSublayer:colorLayer];
}

- (void)changeColorBasicAnimationAction {
    //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";
    //因?yàn)閯?dòng)畫并沒有改變圖層的模型,而只是呈現(xiàn), 一旦動(dòng)畫結(jié)束并從圖層上移除之后,圖層就立刻恢復(fù)到之前定義的外觀狀態(tài) 從沒改變過backgroundColor屬性,所以圖層就返回到原始的顏色
    //在使用隱式動(dòng)畫的時(shí)候,實(shí)際上它就是用例子中CABasicAnimation來實(shí)現(xiàn)的(回憶第七章,我們在-actionForLayer:forKey:委托方法打印出來的結(jié)果就是CABasicAnimation)。
    //animation.fromValue = (__bridge id)colorLayer.backgroundColor;
    //colorLayer.backgroundColor = color.CGColor;
    
    //由于這里的圖層并不是UIView關(guān)聯(lián)的圖層,我們需要用CATransaction來禁用隱式動(dòng)畫行為,否則默認(rèn)的圖層行為會干擾我們的顯式動(dòng)畫
    CALayer *layer = colorLayer.presentationLayer ?: colorLayer;
    animation.fromValue = (__bridge id)layer.backgroundColor;
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    colorLayer.backgroundColor = color.CGColor;
    [CATransaction commit];
    
    animation.toValue = (__bridge id)color.CGColor;
    //apply animation to layer
    [colorLayer addAnimation:animation forKey:nil];
}
//避免在每次動(dòng)畫時(shí)候都重復(fù)CATransaction的代碼。
- (void)applyBasicAnimation:(CABasicAnimation *)animation toLayer:(CALayer *)layer {
    //set the from value (using presentation layer if available)
    animation.fromValue = [layer.presentationLayer ?: layer valueForKeyPath:animation.keyPath];
    //update the property in advance
    //note: this approach will only work if toValue != nil
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    [layer setValue:animation.toValue forKey:animation.keyPath];
    [CATransaction commit];
    //apply animation to layer
    [layer addAnimation:animation forKey:nil];
}

- (void)changeColorBasicAnimationAction2 {
    //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;
    //apply animation without snap-back
    [self applyBasicAnimation:animation toLayer:colorLayer];
}

//CAAnimationDelegate
- (void)animationDelegateTest {
    layerView  = [[UIView alloc] init];
    layerView.frame = CGRectMake(0, 20, 320, 320);
    [self.view addSubview:layerView];
    //add change color button
    UIButton *changeColorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    changeColorBtn.frame = CGRectMake(50, 200, 200, 44.0);
    [changeColorBtn setTitle:@"change Color" forState:(UIControlStateNormal)];
    [changeColorBtn addTarget:self action:@selector(changeColorAnimationDelegateAction) forControlEvents:UIControlEventTouchUpInside];
    changeColorBtn.backgroundColor = [UIColor redColor];
    [layerView addSubview:changeColorBtn];
    
    //create sublayer
    colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0f);
    colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add it to our view
    [layerView.layer addSublayer:colorLayer];

}

- (void)changeColorAnimationDelegateAction {
    //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
    [colorLayer addAnimation:animation forKey:nil];
}
/*
- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag {
    //set backgroundColor property to match animation toValue
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
    [CATransaction commit];
}
 */

//clockViewAnimation
- (void)clockViewAnimationTest {
    //adjust anchor points
    hourHand = [[UIImageView alloc] init];
    hourHand.frame = CGRectMake(100.0, 100.0f, 2.f, 60.0f);
    hourHand.backgroundColor = [UIColor redColor];
    hourHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    [self.view addSubview:hourHand];
    
    minuteHand = [[UIImageView alloc] init];
    minuteHand.frame = CGRectMake(100.0, 100.0f, 2.f, 60.0f);
    minuteHand.backgroundColor = [UIColor blueColor];
    minuteHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    [self.view addSubview:minuteHand];
    
    secondHand = [[UIImageView alloc] init];
    secondHand.frame = CGRectMake(100.0, 100.0f, 2.f, 60.0f);
    secondHand.backgroundColor = [UIColor greenColor];
    secondHand.layer.anchorPoint = CGPointMake(0.5f, 0.9f);
    [self.view addSubview:secondHand];
    //start timer
    timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tickAnimation) userInfo:nil repeats:YES];
    //set initial hand positions
    [self updateHandsAnimated:NO];
}

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

- (void)updateHandsAnimated:(BOOL)animated {
    //convert time to hours, minutes and seconds
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
    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:hourHand animated:animated];
    [self setAngle:minuteAngle forHand:minuteHand animated:animated];
    [self setAngle:secondAngle forHand: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];
}
 */

關(guān)鍵幀動(dòng)畫

//CAKeyframeAnimation同樣是CAPropertyAnimation的一個(gè)子類,它依然作用于單一的一個(gè)屬性,但是和CABasicAnimation不一樣的是,它不限制于設(shè)置一個(gè)起始和結(jié)束的值,而是可以根據(jù)一連串隨意的值來做動(dòng)畫。

- (void)keyframeAnimationTest {
    layerView  = [[UIView alloc] init];
    layerView.frame = CGRectMake(0, 20, 320, 320);
    [self.view addSubview:layerView];
    //add change color button
    UIButton *changeColorBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    changeColorBtn.frame = CGRectMake(50, 200, 200, 44.0);
    [changeColorBtn setTitle:@"change Color" forState:(UIControlStateNormal)];
    [changeColorBtn addTarget:self action:@selector(keyAnimationChangeColor) forControlEvents:UIControlEventTouchUpInside];
    changeColorBtn.backgroundColor = [UIColor redColor];
    [layerView addSubview:changeColorBtn];
    
    //create sublayer
    colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0f);
    colorLayer.backgroundColor = [UIColor blueColor].CGColor;
    //add it to our view
    [layerView.layer addSublayer:colorLayer];
    
}
- (void)keyAnimationChangeColor {
    //create a key frame animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"backgroundColor";
    animation.duration = 2.0f;
    animation.values = @[
                         //為了動(dòng)畫的平滑特性,我們需要開始和結(jié)束的關(guān)鍵幀來匹配當(dāng)前屬性的值
                         (__bridge id)[UIColor blueColor].CGColor,//不能自動(dòng)把當(dāng)前值作為第一幀
                         (__bridge id)[UIColor redColor].CGColor,
                         (__bridge id)[UIColor greenColor].CGColor,
                         (__bridge id)[UIColor blueColor].CGColor,
                         ];
    //apply animation to layer
    [colorLayer addAnimation:animation forKey:nil];
}

//CGPath
- (void)keyframeAnimationPathTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    //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;
    [containerView.layer addSublayer:pathLayer];
    
    //add the ship
    CALayer *starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 64, 64);
    starLayer.position = CGPointMake(0, 150);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
    
    //create the keyframe animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 4.0;
    animation.path = bezierPath.CGPath;
    //指向曲線切線的方向
    animation.rotationMode = kCAAnimationRotateAuto;
    [starLayer addAnimation:animation forKey:nil];
}

虛擬屬性

- (void)valueRotationTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    //add the ship
    CALayer *starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 128, 128);
    starLayer.position = CGPointMake(150, 150);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
    
    //animate the star rotation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform";
    animation.duration = 2.0;
    //從M_PI(180度)調(diào)整到2 * M_PI(360度),然后運(yùn)行程序,會發(fā)現(xiàn)這時(shí)候飛船完全不動(dòng)了
    //用byValue而不是toValue, 沒有做任何旋轉(zhuǎn),這是因?yàn)樽儞Q矩陣不能像角度值那樣疊加
    animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
    [starLayer addAnimation:animation forKey:nil];
}

//transform.rotation屬性有一個(gè)奇怪的問題是它其實(shí)并不存在。這是因?yàn)镃ATransform3D并不是一個(gè)對象,它實(shí)際上是一個(gè)結(jié)構(gòu)體,也沒有符合KVC相關(guān)屬性,transform.rotation實(shí)際上是一個(gè)CALayer用于處理動(dòng)畫變換的虛擬屬性。
//當(dāng)你對他們做動(dòng)畫時(shí),Core Animation自動(dòng)地根據(jù)通過CAValueFunction來計(jì)算的值來更新transform屬性。
//CAValueFunction用于把我們賦給虛擬的transform.rotation簡單浮點(diǎn)值轉(zhuǎn)換成真正的用于擺放圖層的CATransform3D矩陣值。你可以通過設(shè)置CAPropertyAnimation的valueFunction屬性來改變,于是你設(shè)置的函數(shù)將會覆蓋默認(rèn)的函數(shù)。

//用transform.rotation而不是transform做動(dòng)畫的好處如下:
//我們可以不通過關(guān)鍵幀一步旋轉(zhuǎn)多于180度的動(dòng)畫。
//可以用相對值而不是絕對值旋轉(zhuǎn)(設(shè)置byValue而不是toValue)。
//可以不用創(chuàng)建CATransform3D,而是使用一個(gè)簡單的數(shù)值來指定角度。
//不會和transform.position或者transform.scale沖突(同樣是使用關(guān)鍵路徑來做獨(dú)立的動(dòng)畫屬性)。

- (void)virtualValueRotationTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    //add the ship
    CALayer *starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 128, 128);
    starLayer.position = CGPointMake(150, 150);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
    
    //animate the star rotation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform.rotation";
    animation.duration = 2.0;
    animation.toValue = @(M_PI *2);
    [starLayer addAnimation:animation forKey:nil];
}

動(dòng)畫組

//CAAnimationGroup是另一個(gè)繼承于CAAnimation的子類,它添加了一個(gè)animations數(shù)組的屬性,用來組合別的動(dòng)畫。

- (void)animationGroupTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 0.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    //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;
    [containerView.layer addSublayer:pathLayer];
    
    //add a colored layer
    colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(0, 0, 64, 64);
    colorLayer.backgroundColor = [UIColor greenColor].CGColor;
    [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 the animation group
    CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
    groupAnimation.animations = @[animation1, animation2];
    groupAnimation.duration = 4.0;
    //add the animation to the color layer
    [colorLayer addAnimation:groupAnimation forKey:nil];
}

過渡

//CATransition:CAAnimation的子類,有一個(gè)type和subtype來標(biāo)識變換效果。
//type屬性是一個(gè)NSString類型
//kCATransitionFade:平滑的淡入淡出效果
//kCATransitionMoveIn:從頂部滑動(dòng)進(jìn)入
//kCATransitionPush:它創(chuàng)建了一個(gè)新的圖層,從邊緣的一側(cè)滑動(dòng)進(jìn)來,把舊圖層從另一側(cè)推出去的效果。
//kCATransitionReveal:把原始的圖層滑動(dòng)出去來顯示新的外觀,而不是把新的圖層滑動(dòng)進(jìn)入

//subtype
//kCATransitionFromRight
//kCATransitionFromLeft
//kCATransitionFromTop
//kCATransitionFromBottom

- (void)transitionTest {
    imageView = [[UIImageView alloc] init];
    imageView.frame = CGRectMake(0, 20, 320, 320);
    imageView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:imageView];
    
    UIButton *btnSwitchImage = [UIButton buttonWithType:UIButtonTypeCustom];
    btnSwitchImage.frame = CGRectMake(20, 350, 200, 44);
    btnSwitchImage.backgroundColor = [UIColor blueColor];
    [btnSwitchImage setTitle:@"Switch Image" forState:UIControlStateNormal];
    [btnSwitchImage addTarget:self action:@selector(switchImageAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnSwitchImage];
    
    //set up images
    images = @[
               [UIImage imageNamed:@"Meal"],
               [UIImage imageNamed:@"Star"],
               [UIImage imageNamed:@"Meal"],
               [UIImage imageNamed:@"Star"]
               ];
}

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

隱式過渡

//當(dāng)設(shè)置了CALayer的content屬性的時(shí)候,CATransition的確是默認(rèn)的行為
//對于視圖關(guān)聯(lián)的圖層,或者是其他隱式動(dòng)畫的行為,這個(gè)特性依然是被禁用的,但是對于你自己創(chuàng)建的圖層,這意味著對圖層contents圖片做的改動(dòng)都會自動(dòng)附上淡入淡出的動(dòng)畫。

//對圖層樹的動(dòng)畫
//to appdelegate first and second viewController

//自定義動(dòng)畫
//更奇怪的是蘋果通過UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的過渡特性。但是這里的可用的過渡選項(xiàng)和CATransition的type屬性提供的常量完全不同。
//UIView過渡方法中options參數(shù)可以由如下常量指定:
//UIViewAnimationOptionTransitionFlipFromLeft
//UIViewAnimationOptionTransitionFlipFromRight
//UIViewAnimationOptionTransitionCurlUp
//UIViewAnimationOptionTransitionCurlDown
//UIViewAnimationOptionTransitionCrossDissolve
//UIViewAnimationOptionTransitionFlipFromTop
//UIViewAnimationOptionTransitionFlipFromBottom

- (void)transitionSelfTest {
    imageView = [[UIImageView alloc] init];
    imageView.frame = CGRectMake(0, 20, 320, 320);
    imageView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:imageView];
    
    UIButton *btnSwitchImage = [UIButton buttonWithType:UIButtonTypeCustom];
    btnSwitchImage.frame = CGRectMake(20, 350, 200, 44);
    btnSwitchImage.backgroundColor = [UIColor blueColor];
    [btnSwitchImage setTitle:@"Switch Image" forState:UIControlStateNormal];
    [btnSwitchImage addTarget:self action:@selector(selfSwitchImageAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnSwitchImage];
    
    //set up images
    images = @[
               [UIImage imageNamed:@"Meal"],
               [UIImage imageNamed:@"Star"],
               [UIImage imageNamed:@"Meal"],
               [UIImage imageNamed:@"Star"]
               ];
}

- (void)selfSwitchImageAction {
    [UIView transitionWithView:imageView
                      duration:1.0
                       options:UIViewAnimationOptionTransitionFlipFromLeft
                    animations:^{
                        //cycle to next image
                        UIImage *currentImage = imageView.image;
                        NSUInteger index = [images indexOfObject:currentImage];
                        index = (index + 1) % [images count];
                        imageView.image = images[index];
                    }
                    completion:nil];
}

//對圖層做截圖還是很簡單的。CALayer有一個(gè)-renderInContext:方法,可以通過把它繪制到Core Graphics的上下文中捕獲當(dāng)前內(nèi)容的圖片,然后在另外的視圖中顯示出來。
//為了讓事情更簡單,我們用UIView -animateWithDuration:completion:方法來實(shí)現(xiàn)。雖然用CABasicAnimation可以達(dá)到同樣的效果,但是那樣的話我們就需要對圖層的變換和不透明屬性創(chuàng)建單獨(dú)的動(dòng)畫,然后當(dāng)動(dòng)畫結(jié)束的是哦戶在CAAnimationDelegate中把coverView從屏幕中移除。
//用renderInContext:創(chuàng)建自定義過渡效果

- (void)performTransition {
    
    //preserve the current view snapshot
    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
    //insert snapshot view in front of this one
    UIView *coverView = [[UIImageView alloc] initWithImage:coverImage];
    coverView.frame = self.view.bounds;
    [self.view addSubview:coverView];
    //update the view (we'll simply randomize the layer background color)
    CGFloat red = arc4random() / (CGFloat)INT_MAX;
    CGFloat green = arc4random() / (CGFloat)INT_MAX;
    CGFloat blue = arc4random() / (CGFloat)INT_MAX;
    self.view.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
    //perform animation (anything you like)
    [UIView animateWithDuration:1.0 animations:^{
        //scale, rotate and fade the view
        CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01);
        transform = CGAffineTransformRotate(transform, M_PI_2);
        coverView.transform = transform;
        coverView.alpha = 0.0;
        
    } completion:^(BOOL finished) {
        //remove the cover view now we're finished with it
        [coverView removeFromSuperview];
    }];
}

在動(dòng)畫過程中取消動(dòng)畫

//你可以用-addAnimation:forKey:方法中的key參數(shù)來在添加動(dòng)畫之后檢索一個(gè)動(dòng)畫,使用如下方法:
//- (CAAnimation *)animationForKey:(NSString *)key;
//- (void)removeAnimationForKey:(NSString *)key;
//- (void)removeAllAnimations;
//動(dòng)畫在結(jié)束之后被自動(dòng)移除,除非設(shè)置removedOnCompletion為NO,如果你設(shè)置動(dòng)畫在結(jié)束之后不被自動(dòng)移除,那么當(dāng)它不需要的時(shí)候你要手動(dòng)移除它;否則它會一直存在于內(nèi)存中,直到圖層被銷毀。

- (void)cancelAnimationTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    UIButton *startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    startBtn.frame = CGRectMake(0.0, 400, 100, 44.0f);
    [startBtn setTitle:@"Star" forState:UIControlStateNormal];
    [startBtn addTarget:self action:@selector(startAction) forControlEvents:UIControlEventTouchUpInside];
    [startBtn setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:startBtn];
    
    UIButton *stopBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    stopBtn.frame = CGRectMake(120.0, 400, 100, 44.0f);
    [stopBtn setTitle:@"Stop" forState:UIControlStateNormal];
    [stopBtn addTarget:self action:@selector(stopAction) forControlEvents:UIControlEventTouchUpInside];
    [stopBtn setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:stopBtn];

    
    //add the star
    starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 128, 128);
    starLayer.position = CGPointMake(150, 150);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
}

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

- (void)stopAction {
    [starLayer removeAnimationForKey:@"rotateAnimation"];
}
/*
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    //log that the animation stopped
    NSLog(@"The animation stopped (finished:%@", flag? @"Yes":@"No");
}
 */

pragma mark -- 圖層時(shí)間

//CAMediaTiming協(xié)議
//CAMediaTiming協(xié)議定義了在一段動(dòng)畫內(nèi)用來控制逝去時(shí)間的屬性的集合,CALayer和CAAnimation都實(shí)現(xiàn)了這個(gè)協(xié)議,所以時(shí)間可以被任意基于一個(gè)圖層或者一段動(dòng)畫的類控制。

//持續(xù)和重復(fù)
//duration是一個(gè)CFTimeInterval的類型(類似于NSTimeInterval的一種雙精度浮點(diǎn)類型),對將要進(jìn)行的動(dòng)畫的一次迭代指定了時(shí)間。
//CAMediaTiming另外還有一個(gè)屬性叫做repeatCount,代表動(dòng)畫重復(fù)的迭代次數(shù)。如果duration是2,repeatCount設(shè)為3.5(三個(gè)半迭代),那么完整的動(dòng)畫時(shí)長將是7秒。
//duration和repeatCount默認(rèn)都是0,這里的0僅僅代表了“默認(rèn)”,也就是0.25秒和1次

- (void)durationAndRepeatCountTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    startBtn.frame = CGRectMake(0.0, 400, 100, 44.0f);
    [startBtn setTitle:@"Star" forState:UIControlStateNormal];
    [startBtn addTarget:self action:@selector(durationStartAction) forControlEvents:UIControlEventTouchUpInside];
    [startBtn setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:startBtn];
    
    //add the star
    starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 128, 128);
    starLayer.position = CGPointMake(150, 150);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
}

- (void)setControlsEnabled:(BOOL)enbled {
    startBtn.enabled = enbled;
    startBtn.alpha = enbled? 1.0f:0.25;
}

- (void)durationStartAction {
    CFTimeInterval duration = 3.0f;
    float repeatCount = 2.5;
    //animate the star rotation
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform.rotation";
    animation.duration = duration;
    animation.repeatCount = repeatCount;
    animation.byValue = @(M_PI * 2);
    animation.delegate = self;
    [starLayer addAnimation:animation forKey:@"rotateAnimation"];
    //disable controls
    [self setControlsEnabled:NO];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    [self setControlsEnabled:YES];
}

//repeatDuration屬性:它讓動(dòng)畫重復(fù)一個(gè)指定的時(shí)間,而不是指定次數(shù)。
//autoreverses的屬性:(BOOL類型)在每次間隔交替循環(huán)過程中自動(dòng)回放
//把repeatDuration設(shè)置為INFINITY,于是動(dòng)畫無限循環(huán)播放,設(shè)置repeatCount為INFINITY也有同樣的效果

- (void)repeatDurationTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    //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:@"Star"].CGImage;
    [containerView.layer addSublayer:doorLayer];
    //apply perspective transform
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    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];
}

相對時(shí)間

//beginTime指定了動(dòng)畫開始之前的的延遲時(shí)間
//speed是一個(gè)時(shí)間的倍數(shù),默認(rèn)1.0,減少它會減慢圖層/動(dòng)畫的時(shí)間,增加它會加快速度。
//timeOffset:增加timeOffset只是讓動(dòng)畫快進(jìn)到某一點(diǎn);對于一個(gè)持續(xù)1秒的動(dòng)畫來說,設(shè)置timeOffset為0.5意味著動(dòng)畫將從一半的地方開始
//timeOffset并不受speed的影響,。所以如果你把speed設(shè)為2.0,把timeOffset設(shè)置為0.5,那么你的動(dòng)畫將從動(dòng)畫最后結(jié)束的地方開始,因?yàn)?秒的動(dòng)畫實(shí)際上被縮短到了0.5秒。然而即使使用了timeOffset讓動(dòng)畫從結(jié)束的地方開始,它仍然播放了一個(gè)完整的時(shí)長,這個(gè)動(dòng)畫僅僅是循環(huán)了一圈,然后從頭開始播放。

- (void)timeOffsetAndSpeedTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    speedLabel = [[UILabel alloc] init];
    speedLabel.frame = CGRectMake(0.0, 400.0, 100, 40.0f);
    speedLabel.backgroundColor = [UIColor grayColor];
    [self.view addSubview:speedLabel];
    
    speedSlider = [[UISlider alloc] init];
    speedSlider.frame = CGRectMake(0.0, 450, 320, 40);
    speedSlider.backgroundColor = [UIColor purpleColor];
    [speedSlider addTarget:self action:@selector(updateSliders) forControlEvents:UIControlEventValueChanged];

    [self.view addSubview:speedSlider];
    
    
    timeOffsetLabel = [[UILabel alloc] init];
    timeOffsetLabel.frame = CGRectMake(120, 400.0, 100, 40.0f);
    timeOffsetLabel.backgroundColor = [UIColor grayColor];
    [self.view addSubview:timeOffsetLabel];
    
    timeOffsetSlider = [[UISlider alloc] init];
    timeOffsetSlider.frame = CGRectMake(0.0, 500, 320, 40);
    timeOffsetSlider.backgroundColor = [UIColor purpleColor];
    [timeOffsetSlider addTarget:self action:@selector(updateSliders) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:timeOffsetSlider];
    
    startBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    startBtn.frame = CGRectMake(0.0, 550, 100, 44.0f);
    [startBtn setTitle:@"Star" forState:UIControlStateNormal];
    [startBtn addTarget:self action:@selector(playAction) forControlEvents:UIControlEventTouchUpInside];
    [startBtn setBackgroundColor:[UIColor blueColor]];
    [self.view addSubview:startBtn];
    
    //create a path
    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;
    [containerView.layer addSublayer:pathLayer];
    
    //add the star
    starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 64, 64);
    starLayer.position = CGPointMake(0, 150);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
    //set initial values
    [self updateSliders];
}

- (void)updateSliders {
    CFTimeInterval timeOffset = timeOffsetSlider.value;
    timeOffsetLabel.text = [NSString stringWithFormat:@"%f", timeOffset];
    float speed = speedSlider.value;
    speedLabel.text = [NSString stringWithFormat:@"%f", speed];
}

- (void)playAction {
    //create the keyframe animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.timeOffset = timeOffsetSlider.value;
    animation.speed = speedSlider.value;
    animation.duration = 1.0;
    animation.path = bezierPath.CGPath;
    animation.rotationMode = kCAAnimationRotateAuto;
    animation.removedOnCompletion = NO;
    [starLayer addAnimation:animation forKey:@"slide"];
}

fillMode

//一種可能是屬性和動(dòng)畫沒被添加之前保持一致,也就是在模型圖層定義的值(見第七章“隱式動(dòng)畫”,模型圖層和呈現(xiàn)圖層的解釋)。
//另一種可能是保持動(dòng)畫開始之前那一幀,或者動(dòng)畫結(jié)束之后的那一幀。這就是所謂的填充,因?yàn)閯?dòng)畫開始和結(jié)束的值用來填充開始之前和結(jié)束之后的時(shí)間。
//需要把removeOnCompletion設(shè)置為NO,另外需要給動(dòng)畫添加一個(gè)非空的鍵,于是可以在不需要?jiǎng)赢嫷臅r(shí)候把它從圖層上移除。
//fillMode:NSString類型
//kCAFillModeForwards
//kCAFillModeBackwards
//kCAFillModeBoth
//kCAFillModeRemoved 默認(rèn)

層級關(guān)系時(shí)間

//對圖層調(diào)整時(shí)間將會影響到它本身和子圖層的動(dòng)畫,但不會影響到父圖層。另一個(gè)相似點(diǎn)是所有的動(dòng)畫都被按照層級組合(使用CAAnimationGroup實(shí)例)
//對CALayer或者CAGroupAnimation調(diào)整duration和repeatCount/repeatDuration屬性并不會影響到子動(dòng)畫。但是beginTime,timeOffset和speed屬性將會影響到子動(dòng)畫。

全局時(shí)間和本地時(shí)間

//全局時(shí)間
//CACurrentMediaTime函數(shù)來訪問馬赫時(shí)間:
//CFTimeInterval time = CACurrentMediaTime();//它返回了設(shè)備自從上次啟動(dòng)后的秒數(shù),并不是你所關(guān)心的,它真實(shí)的作用在于對動(dòng)畫的時(shí)間測量提供了一個(gè)相對值。注意當(dāng)設(shè)備休眠的時(shí)候馬赫時(shí)間會暫停,也就是所有的CAAnimations(基于馬赫時(shí)間)同樣也會暫停。
//每個(gè)CALayer和CAAnimation實(shí)例都有自己本地時(shí)間的概念,是根據(jù)父圖層/動(dòng)畫層級關(guān)系中的beginTime,timeOffset和speed屬性計(jì)算。就和轉(zhuǎn)換不同圖層之間坐標(biāo)關(guān)系一樣,CALayer同樣也提供了方法來轉(zhuǎn)換不同圖層之間的本地時(shí)間。如下:
//- (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
//- (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
//當(dāng)用來同步不同圖層之間有不同的speed,timeOffset和beginTime的動(dòng)畫,這些方法會很有用。

暫停,倒回和快進(jìn)

//可以利用CAMediaTiming來暫停圖層本身
//如果把圖層的speed設(shè)置成0,它會暫停任何添加到圖層上的動(dòng)畫。類似的,設(shè)置speed大于1.0將會快進(jìn),設(shè)置成一個(gè)負(fù)值將會倒回動(dòng)畫。
//self.window.layer.speed = 100;

手動(dòng)動(dòng)畫

//timeOffset一個(gè)很有用的功能在于你可以它可以讓你手動(dòng)控制動(dòng)畫進(jìn)程,通過設(shè)置speed為0,可以禁用動(dòng)畫的自動(dòng)播放,然后來使用timeOffset來來回顯示動(dòng)畫序列。這可以使得運(yùn)用手勢來手動(dòng)控制動(dòng)畫變得很簡單。

- (void)handleAnimationTest {
    containerView = [[UIView alloc] init];
    containerView.frame = CGRectMake(0.0, 20.0, 320, 320);
    containerView.backgroundColor = [UIColor blackColor];
    [self.view addSubview:containerView];
    
    //add the door
    starLayer = [CALayer layer];
    starLayer.frame = CGRectMake(0, 0, 128, 256);
    starLayer.position = CGPointMake(150-64, 150);
    starLayer.anchorPoint = CGPointMake(0, 0.5);
    starLayer.contents = (__bridge id)[UIImage imageNamed:@"Star"].CGImage;
    [containerView.layer addSublayer:starLayer];
    
    //apply perspective transform
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    containerView.layer.sublayerTransform = perspective;
    
    //add pan gesture recognizer to handle swipes
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
    [pan addTarget:self action:@selector(pan:)];
    [self.view addGestureRecognizer:pan];
    
    //pause all layer animations
    starLayer.speed = 0.0;
    //apply swinging animation (which won't play because layer is paused)
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"transform.rotation.y";
    animation.toValue = @(-M_PI_2);
    animation.duration = 2.0;
    animation.repeatDuration = INFINITY;
    animation.autoreverses = YES;
    [starLayer addAnimation:animation forKey:nil];
}

- (void)pan:(UIPanGestureRecognizer *)pan {
    //get horizontal component of pan gesture
    CGFloat x = [pan translationInView:self.view].x;
    //conver from points to animation duration //using a reasonable scale factor
    x /= 200.0f;
    //update timeOffset and clamp result
    CFTimeInterval timeOffset = starLayer.timeOffset;
    timeOffset = MIN(0.999, MAX(0.0, timeOffset - x));
    starLayer.timeOffset = timeOffset;
    //reset pan gesture
    [pan setTranslation:CGPointZero inView:self.view];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,698評論 6 30
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你可以看...
    F麥子閱讀 5,272評論 5 13
  • 在iOS實(shí)際開發(fā)中常用的動(dòng)畫無非是以下四種:UIView動(dòng)畫,核心動(dòng)畫,幀動(dòng)畫,自定義轉(zhuǎn)場動(dòng)畫。 1.UIView...
    請叫我周小帥閱讀 3,332評論 1 23
  • 書寫的很好,翻譯的也棒!感謝譯者,感謝感謝! iOS-Core-Animation-Advanced-Techni...
    錢噓噓閱讀 2,442評論 0 6
  • Core Animation Core Animation,中文翻譯為核心動(dòng)畫,它是一組非常強(qiáng)大的動(dòng)畫處理API,...
    45b645c5912e閱讀 3,160評論 0 21

友情鏈接更多精彩內(nèi)容