Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSTimer not fired when uiscrollview event occurs

I have a UIImageView placed in UIScrollView, Basicly this UIImageView holds very big map, and created animation on a predefined path with "arrows" pointed navigation direction.

But, whenever uiscrollevents occurs, I think MainLoop freezes and NSTimer being not fired, and animation stopped.

Are there any existing property, which solves this problem, on UIScrollView, CAKeyFrameAnimation or NSTimer?

//viewDidLoad
   self.myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(drawLines:) userInfo:nil repeats:YES];

- (void)drawLines:(NSTimer *)timer {

   CALayer *arrow = [CALayer layer];
   arrow.bounds = CGRectMake(0, 0, 5, 5);
   arrow.position = CGPointMake(line.x1, line.y1);
   arrow.contents = (id)([UIImage imageNamed:@"arrow.png"].CGImage);

   [self.contentView.layer addSublayer:arrow];

   CAKeyframeAnimation* animation = [CAKeyframeAnimation animation];
   animation.path = path;
   animation.duration = 1.0;
   animation.rotationMode = kCAAnimationRotateAuto; // object auto rotates to follow the path
   animation.repeatCount = 1;
   animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
   animation.fillMode = kCAFillModeForwards;

   [arrow addAnimation:animation forKey:@"position"];
}
like image 558
ubaltaci Avatar asked Feb 01 '12 04:02

ubaltaci


2 Answers

iOS Applications run on an NSRunLoop. Each NSRunLoop has different modes of execution for different tasks. For example, the default nstimer is scheduled to run under the NSDefaultRunMode on the NSRunLoop. What this means however is that certain UIEvents, scrollviewing being one, will interrupt the timer, and place it on a queue to be run as soon as the event stops updating. In your case, in order to get the timer to not be interrupted, you need to schedule it for a different mode, namely NSRunLoopCommonModes, like so:

  self.myTimer =  [NSTimer scheduledTimerWithTimeInterval:280
                                                                 target:self
                                                               selector:@selector(doStuff)
                                                               userInfo:nil
                                                                repeats:NO];
  [[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSRunLoopCommonModes]; 

This mode will allow your timer to not be interrupted by scrolling. You can find more about this info here: https://developer.apple.com/documentation/foundation/nsrunloop At the bottom you will see the definitions of the modes you can choose from. Also, legend has it, you can write your own custom modes, but few have ever lived to tell the tale im afraid.

like image 106
Greg Price Avatar answered Sep 19 '22 23:09

Greg Price


One more thing (c)

  1. use timerWithTimeInterval method in order to avoid adding timer to runloop in DefaultMode
  2. use mainRunLoop

So:

self.myTimer = [NSTimer timerWithTimeInterval:280 target:self selector:@selector(doStuff) userInfo:nil repeats:NO]; [[NSRunLoop mainRunLoop] addTimer:self.myTimer forMode:NSRunLoopCommonModes];

like image 24
Andrey Seredkin Avatar answered Sep 19 '22 23:09

Andrey Seredkin