Анимации CADisplayLink и UIView ненадежны

Я пытаюсь создать класс Animations Manager, который будет обрабатывать рекурсивные анимации для меня. Работает, но ненадежно. например, иногда при его использовании анимация происходит как бы «спина к спине», без задержки между ними. В других случаях при его использовании между анимациями остаются задержки (это происходит с другими элементами, расположенными на других контроллерах представления).

Вот мой код:

@implementation customAnimationTimer

-(id) initWithTimeInterval:(NSTimeInterval)timeInterval target:(UIView*)target animationType:(NSInteger)animationType
              fromValue:(CGFloat)fromValue toValue:(CGFloat)toValue withDelegate:(id <customAnimationTimerDelegate>)delegate {

    if (self = [super init]) {
        self.delegate = delegate;
        _timeInterval = timeInterval;
        _target = target;
        _animationState = NO;
        _animationType = animationType;
        _fromValue = fromValue;
        _toValue = toValue;

        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkFire)];
        [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    }
    return self;
}

-(void) displayLinkFire {if (self.delegate) [self.delegate displayLinkUpdate:self];}

@end


@implementation animationManager


static animationManager* sharedHelper = nil;

+ (animationManager*) sharedInstance {
    if (!sharedHelper) sharedHelper = [[animationManager alloc] init];
    return sharedHelper;
}


-(id) init { if (self = [super init]) {[self initArrays];} return self;}

-(void) initArrays {
    _activeTimers = [NSMutableArray arrayWithObjects: nil];
}

-(void) displayLinkUpdate:(customAnimationTimer*)timer {
    if (timer.displayLink.frameInterval != 1) [self animateWithTimer:timer];
    timer.displayLink.frameInterval = (timer.timeInterval/timer.displayLink.duration);
}

-(void) animateWithTimer:(customAnimationTimer*)timer {
    if (!timer.animationState) {

        timer.animationState = true;
        [UIView animateWithDuration:timer.timeInterval animations:^{
           if (timer.animationType == 0) timer.target.alpha = timer.toValue;
        }];

    } else {

        timer.animationState = false;
        [UIView animateWithDuration:timer.timeInterval animations:^{
            if (timer.animationType == 0) timer.target.alpha = timer.fromValue;
        }];

    }
}

-(void) addAnimationToView:(UIView*)view withType:(int)animationType fromValue:(CGFloat)fromValue toValue:(CGFloat)toValue withTime:(CGFloat)time  {
    [_activeTimers addObject: [[customAnimationTimer alloc] initWithTimeInterval:time target:view animationType:animationType fromValue:fromValue toValue:toValue withDelegate:self]];
}

-(void) removeAnimationFromView:(UIView*)view {

    NSInteger index = 900000000;
    for (customAnimationTimer* timer in _activeTimers) {
        if (timer.target == view) {
            index = [_activeTimers indexOfObject:timer];
            [timer.displayLink invalidate];
        }
    }

    if (index != 900000000) [_activeTimers removeObjectAtIndex:index];

}

-(void) removeAnimations {
    for (customAnimationTimer* timer in _activeTimers) [timer.displayLink invalidate];
    [self initArrays];
}


@end

person Community    schedule 02.04.2014    source источник
comment
Из всех объектов, предназначенных для синхронизации рисования и тайминга, нужно было выбрать тот, который не имеет никакого отношения к рисованию. Поддерживайте пул обратных вызовов, а не таймеры. NSTimer недостаточно надежен, чтобы его можно было использовать для чего-то близкого к времени анимации.   -  person CodaFi    schedule 02.04.2014
comment
Боюсь, все еще получаю те же результаты. Кажется, что все вызывается в нужное время, но анимация не происходит в нужное время. Я обновил ОП новым кодом. Любые идеи о том, что не так?   -  person    schedule 02.04.2014
comment
Выполняется ли обратный вызов CADisplayLink в основном потоке? Кроме того, я заметил, что вы анимируете с помощью [UIView animateWithDuration]. В конечном итоге это будет запланировано в runLoop. Я не думаю, что вам гарантировано, что анимация запустится немедленно (по крайней мере, не с точностью до кадра).   -  person blackirishman    schedule 02.04.2014
comment
Да, это происходит в основном потоке. Это также не случай задержки на несколько кадров. Я говорю о полсекунды здесь!   -  person    schedule 02.04.2014


Ответы (2)


Вы, вероятно, тестируете в симуляторе. Не надо. CADisplayLink совершенно ненадежен в симуляторе. Если вы собираетесь использовать CADisplayLink, тестируйте только на устройстве.

Другая проблема с вашим кодом заключается в том, что вы никогда не должны делать предположения о том, как часто будет срабатывать CADisplayLink. Он не срабатывает регулярно. Всегда смотрите на timestamp, чтобы узнать, когда он действительно сработал, и вычисляйте «кадр» анимации на основе этого.

В этом примере из моего собственного кода я называю первый «кадр» анимации 0, а последний «кадр» всей анимации — 1. Таким образом, я могу рассчитать правильный «кадр» анимации, сравнив отметку времени с общей продолжительностью. Я сохраняю состояние в некоторых переменных экземпляра. Вот что происходит, когда срабатывает отображаемая ссылка:

if (self->_timestamp < 0.01) { // pick up and store first timestamp
    self->_timestamp = sender.timestamp;
    self->_frame = 0.0;
} else { // calculate frame
    self->_frame = (sender.timestamp - self->_timestamp) * SCALE;
}
// ... redraw here ...
if (self->_frame > 1.0) {
    [sender invalidate];
    self->_frame = 0.0;
    self->_timestamp = 0.0;
}
person matt    schedule 02.04.2014
comment
Код для загрузки здесь: github.com/ mattneub/Programming-iOS-Book-Examples/blob/master/ - person matt; 02.04.2014

Что ж, я записал его и воспроизвел две записи рядом друг с другом, и мои глаза действительно сыграли со мной злую шутку! Один из них был на синем фоне, а другой на красном фоне, я думаю, это как-то связано! Прошу прощения у людей, которых я запутал!

person Community    schedule 02.04.2014