Iphone: удалить CALayer при остановке анимации, CALayer flash перед исчезновением

  • создать простой проект в XCode
  • установить представление для приема событий с несколькими касаниями
  • ответить в touchsBegan, создать CALayer при обнаружении события касания
  • сделать анимацию прозрачности для CALayer
  • при остановке анимации, удалите CALayer из родительского

Ожидайте: CALayer обычно исчезает

Фактическое: вспышка CALayer (мигает) перед исчезновением

полный исходный код:

@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

Ответ 1

tl; dr: Установите fillMode в анимации на kCAFillModeForwards или измените значения до их окончательного значения до добавления анимации к слою.


Базовая анимация - это только визуальная анимация во время анимации, никакие фактические значения не изменяются. Когда вы устанавливаете анимацию не удаляться по завершении, это означает, что слой все равно будет ссылаться на объект анимации как на одну из своих анимаций. Тем не менее, он уже провел свою анимацию.

Поведение по умолчанию как выглядит анимация (режим заполнения) kCAFillModeRemoved, что означает, что сразу после продолжительности анимации слой будет выглядеть так, как будто анимация никогда не происходила. Изменяя режим заполнения на kCAFillModeForwards или kCAFillModeBoth, вы можете сделать слой похожим, как если бы слой оставался в конечном состоянии анимации.

Вы можете сделать то же самое в начале анимации с помощью kCAFillModeBackwards, но это в основном применяется, когда вы установили время начала анимации.

Итак, чтобы анимация выглядела так же, как в конечном состоянии анимации, вы можете либо установить режим заполнения на ...Forwards, либо не удалить анимацию или изменить фактические значения слоя на значения, которые вы ожидаете от них перед тем как добавить анимацию в представление. Это изменит значения, а затем оживит от старого значения до нового.