简体   繁体   中英

iphone : remove CALayer when animation stop, CALayer flash before disappear

  • create a simple project in XCode
  • set view to receive multi-touch events
  • respond in touchesBegan, create CALayer when detect touch event
  • make a opacity fade out animation for CALayer
  • when animation stop, remove CALayer from parent

Expect: CALayer disappear normally

Actual: CALayer flash (blink) before disappear

full source code:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.view.multipleTouchEnabled = YES;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (UITouch* touch in touches) {
        CGPoint p = [touch locationInView:self.view];

        //NSLog(@"touch=%@ p=%@", touch, NSStringFromCGPoint(p));

        CALayer *layer = [CALayer layer];
        layer.position = p;
        layer.bounds = CGRectMake(0, 0, 70, 70);
        layer.cornerRadius = 30;
        layer.masksToBounds = NO;
        layer.backgroundColor = [UIColor colorWithRed:102.0/255.0 green:156.0/255.0 blue:255.0/255.0 alpha:0.8].CGColor;
        layer.shouldRasterize = YES;

        CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
        fadeOutAnimation.fromValue = [NSNumber numberWithFloat:1.0];
        fadeOutAnimation.toValue = [NSNumber numberWithFloat:0.0];
        fadeOutAnimation.duration = 0.5;
        fadeOutAnimation.delegate = self;
        fadeOutAnimation.removedOnCompletion = NO;
        [fadeOutAnimation setValue:layer forKey:@"parentLayer"];
        [layer addAnimation:fadeOutAnimation forKey:@"opacity"];

        [self.view.layer addSublayer:layer];
    }
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
    if(flag) {
        CALayer *layer = [theAnimation valueForKey:@"parentLayer"];
        if(layer) {
            layer.opaque = NO;
            layer.opacity = 0.0;
            //layer.hidden = YES;
            //NSLog(@"The layer object was: %@ (%@)", layer, [layer name]);
            [layer removeFromSuperlayer];
            [layer removeAllAnimations];
        }
    }
}

@end

tl;dr: Set the fillMode on the animation to kCAFillModeForwards or change the values to their final value prior to adding the animation to the layer.


A basic animation is only a visual animation during the time of the animation, no actual values are changed. When you set the animation to not be removed upon completion it means that the layer will still reference the animation object as one of its animations. It has however already ran its animation.

The default behavior of how animations look (their fill mode) is kCAFillModeRemoved which means that just after the duration of the animation the layer will look as if the animation never happened. By changing the fill mode to either kCAFillModeForwards or kCAFillModeBoth you can make the layer look as if the layer remained in the end state of the animation.

You can do the same thing in the beginning of the animation with kCAFillModeBackwards but it mostly applies when you have set a begin time for the animation.

So to make the animation look as it did in the final state of the animation you can either set the fill mode to ...Forwards and not remove the animation or change the actual values of the layer to the values you expect them to be just before you add the animation to the view. This will change the values and then animate from the old value to the new.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM