Pregunta

I am trying to create an Animations Manager class, which will handle recursive animations for me. It is working, but is being unreliable. e.g Sometimes, when using it the animations happen "back to back" as it were, with no delay between them. Other times when using it, delays are left between animations (This is happening on other elements located on other view controllers)

Here is my code:

@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
¿Fue útil?

Solución 2

Well, having recorded it and played back the two recordings side-to-side and it was indeed my eyes playing tricks on me! One of them was on a blue background and other was on a red background, I guess this had something to do with it! My apologies to people I have confused!

Otros consejos

You are probably testing in the simulator. Don't. CADisplayLink is completely unreliable in the simulator. If you are doing to use CADisplayLink, test only on a device.

Another problem with your code is that you should never make assumptions about how often the CADisplayLink will fire. It does not fire regularly. Always look at the timestamp to learn when it actually did fire, and calculate your animation "frame" based on that.

In this example from my own code, I call the first "frame" of the animation 0 and the last "frame" of the whole animation 1. Thus I can calculate the correct "frame" of the animation by comparing the timestamp with the total duration. I maintain state in some instance variables. Here's what happens when the display link fires:

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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top