1

Hope you can help or explain what's going on here, as I'm not great with UIGraphics contexts.

I have defined a class that automatically scrolls a label back and forth in it's frame when the label text is to long for the given space on the view controllers primary view.

It consists of a scrollview that fits the allocated space in the parent view, and contains a UILabel (subclassed) that is sized to fit it's text. (The UILabel is a subview in the parent scrollview).

The scrollview commits an animation that scrolls the UILabel left -> right, then when the animation is finished, I fire the delegate '

(void)animationDidStop:animationID(NSString*)finished:(NSNumber *)finished context:(void *)context' 

which resets some numbers and restarted the animation scrolling it in the reverse direction right -> left

This works really nicely.

However, if i add another instance of this 'scrolling label' somewhere else in the view controllers master view, as one of the animations stop and the AnimationDidStop delegate gets fired, it relaunches the animations from scratch for the other 'scrolling' label object.

I have tried the following to isolate the firing of the animation with no positive results. Passing the views context and an identifier to beginAnimations and then trapping them in the (void)animationDidStop method... Didn't make any difference. This makes me think the method doesn't seem to take

Below is the code that does the animation, and the code that relaunches it in the other direction.

As I say, it works really well on it's own, but when there is more than 1 instance of this class resident on the screen at the same time, the beginAnimations seems to fire for both instances.

Hope you can explain why. Thanks.

- (void)beginAnimationWithOrgigin:(CGPoint)origin Terminus:(CGPoint)terminus {


NSLog (@"Message %@ in context %@",((UILabel*)_textLabel).text,_ctx);

CGFloat text_width = ((UILabel*)_textLabel).frame.size.width;
CGFloat display_width = self.frame.size.width;

if ( text_width > display_width ) {
    float duration = (text_width - display_width)/40;
    [self setContentOffset:origin];

    [UIView beginAnimations:((UILabel*)_textLabel).text context:_ctx];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDelay:1.0];
    [UIView setAnimationDuration:duration];
    [UIView setAnimationRepeatCount:1];
    [self setContentOffset:terminus];

    [UIView commitAnimations];

}
}


- (void)animationDidStop:(NSString *)animationID
            finished:(NSNumber *)finished
             context:(void *)context
{

NSLog(@"Animation Did stop context = %@",context);

if ([animationID isEqualToString:((UILabel*)_textLabel).text]){

    static BOOL forward = NO;
    if ([finished boolValue]) {

        CGPoint origin;
        CGPoint terminal_origin;

        if (forward){
            origin = CGPointMake(0, 0);
            terminal_origin = CGPointMake(((UILabel*)_textLabel).frame.size.width - self.frame.size.width, ((UILabel*)_textLabel).frame.origin.y);
        }else{

            terminal_origin = CGPointMake(0, 0);
            origin = CGPointMake(((UILabel*)_textLabel).frame.size.width - self.frame.size.width, ((UILabel*)_textLabel).frame.origin.y);
        }

        forward = !forward;
        [self beginAnimationWithOrgigin:origin Terminus:terminal_origin];
    }
}
}

EDIT: Added 28/5/14 @ 13:14 After A-Lives advise regarding the use of statics... have modified the code to use it's label as a flag to prompt the change of direction in scrolling. THIS HAS FIXED THE PROBLEM

- (void)beginAnimationWithOrgigin:(CGPoint)origin Terminus:(CGPoint)terminus Direction:(NSString*)direction{

if (!direction)direction = @"FORWARD";

NSLog (@"Message %@ in context %@",((UILabel*)_textLabel).text,_ctx);

CGFloat text_width = ((UILabel*)_textLabel).frame.size.width;
CGFloat display_width = self.frame.size.width;

if ( text_width > display_width ) {
    float duration = (text_width - display_width)/40;
    [self setContentOffset:origin];

    [UIView beginAnimations:direction context:_ctx];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationDelay:1.0];
    [UIView setAnimationDuration:duration];
    [UIView setAnimationRepeatCount:1];
    [self setContentOffset:terminus];

    [UIView commitAnimations];

}
}



- (void)animationDidStop:(NSString *)animationID
            finished:(NSNumber *)finished
             context:(void *)context
{

NSLog(@"Animation Did stop context = %@",context);
NSString *direction;
CGPoint origin;
CGPoint terminal_origin;

    if (![animationID isEqualToString:@"FORWARD"]){
        origin = CGPointMake(0, 0);
        terminal_origin = CGPointMake(((UILabel*)_textLabel).frame.size.width - self.frame.size.width, ((UILabel*)_textLabel).frame.origin.y);
        direction = @"FORWARD";
    }else{

        terminal_origin = CGPointMake(0, 0);
        origin = CGPointMake(((UILabel*)_textLabel).frame.size.width - self.frame.size.width, ((UILabel*)_textLabel).frame.origin.y);
        direction = @"BACKWARD";
        }

[self beginAnimationWithOrgigin:origin Terminus:terminal_origin Direction:direction];
}
Amisl
  • 11
  • 1
  • 3
  • Why is there a static var inside `animationDidStop:finished:context:` ? – A-Live Apr 28 '14 at 09:37
  • Hi the static is just to remember which direction the animation got launched last time and toggle it in the other direction for the next call – Amisl Apr 28 '14 at 09:42
  • You reset its value each time the method is called. – A-Live Apr 28 '14 at 10:28
  • Hi A-Live, that is correct. The problem i have is... if I have 2 instances of this scrolling control (separate instances with different content) operational in the ViewControllers.view the beginAnimations method in one instance also restarts the animation in the other instance, which is what the problem is really. – Amisl Apr 28 '14 at 10:57
  • From one appearance of incorrect static variables usage I expect something similar to cause this issue. – A-Live Apr 28 '14 at 11:17
  • Hi A-Live, thanks for your input. Could you expand a little on why you think the use of the static variable is incorrect? That will help me understand your further conclusion about why I am seeing the results I am. – Amisl Apr 28 '14 at 11:30
  • In general you don't want to use static vars to work with instance values. – A-Live Apr 28 '14 at 11:58
  • Thank you A-Live... My edit of the code has worked... The statics get shared in memory. I will remember that. Why not post as answer for a vote. – Amisl Apr 28 '14 at 12:31
  • I've just given you a direction, please write an answer yourself. – A-Live Apr 28 '14 at 14:48

1 Answers1

0

The problem was in forgetting that statics really are just ordinary C static variables which are globally accessible across the app. (i.e, not encapsulated in the scope that defined them.)

Multiple instances of the same class are all reading and writing values to a the same variable causing unpredictable behaviour.

Solution: Pass instance level messages to control toggling activities where you can't guarantee your class is a singleton.

Modified Working Code:

- (void)beginAnimationWithOrgigin:(CGPoint)origin Terminus:(CGPoint)terminus Direction:(NSString*)direction{

    if (!direction)direction = @"FORWARD";

    NSLog (@"Message %@ in context %@",((UILabel*)_textLabel).text,_ctx);

    CGFloat text_width = ((UILabel*)_textLabel).frame.size.width;
    CGFloat display_width = self.frame.size.width;

    if ( text_width > display_width ) {
        float duration = (text_width - display_width)/40;
        [self setContentOffset:origin];

        [UIView beginAnimations:direction context:_ctx];
        [UIView setAnimationDelegate:self];
        [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
        [UIView setAnimationDelay:1.0];
        [UIView setAnimationDuration:duration];
        [UIView setAnimationRepeatCount:1];
        [self setContentOffset:terminus];

        [UIView commitAnimations];

    }
}



- (void)animationDidStop:(NSString *)animationID
        finished:(NSNumber *)finished
         context:(void *)context
{

    NSLog(@"Animation Did stop context = %@",context);
    NSString *direction;
    CGPoint origin;
    CGPoint terminal_origin;

    if (![animationID isEqualToString:@"FORWARD"]){
        origin = CGPointMake(0, 0);
        terminal_origin = CGPointMake(((UILabel*)_textLabel).frame.size.width - self.frame.size.width, ((UILabel*)_textLabel).frame.origin.y);
        direction = @"FORWARD";
    }else{

        terminal_origin = CGPointMake(0, 0);
        origin = CGPointMake(((UILabel*)_textLabel).frame.size.width - self.frame.size.width, ((UILabel*)_textLabel).frame.origin.y);
        direction = @"BACKWARD";
        }

    [self beginAnimationWithOrgigin:origin Terminus:terminal_origin Direction:direction];
}
Amisl
  • 11
  • 1
  • 3