NSTimer не запускается, когда происходит событие uiscrollview

У меня есть UIImageView, помещенный в UIScrollView. По сути, этот UIImageView содержит очень большую карту и создал анимацию по заранее определенному пути со стрелками, указывающими направление навигации.

Но всякий раз, когда происходит uiscrollevents, я думаю, что MainLoop зависает, а NSTimer не запускается, а анимация останавливается.

Есть ли какое-либо существующее свойство, которое решает эту проблему, в UIScrollView, CAKeyFrameAnimation или 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"];
}

person ubaltaci    schedule 01.02.2012    source источник
comment
возможный дубликат UIScrollView приостанавливает NSTimer при прокрутке   -  person rob mayoff    schedule 01.02.2012


Ответы (2)


Приложения iOS работают в NSRunLoop. Каждый NSRunLoop имеет разные режимы выполнения для разных задач. Например, ntimer по умолчанию планируется запускать в режиме NSDefaultRunMode в NSRunLoop. Однако это означает, что определенные UIEvents, одним из которых является scrollviewing, прерывают таймер и помещают его в очередь для запуска, как только событие перестанет обновляться. В вашем случае, чтобы таймер не прерывался, вам нужно запланировать его для другого режима, а именно NSRunLoopCommonModes, например:

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

Этот режим позволит вашему таймеру не прерываться прокруткой. Вы можете найти больше информации здесь: https://developer.apple.com/documentation/foundation/nsrunloop Внизу вы увидите определения режимов, которые вы можете выбрать. Кроме того, легенда гласит, что вы можете писать свои собственные пользовательские режимы, но мало кто когда-либо жил, чтобы рассказать об этом, я боюсь.

person Greg Price    schedule 01.02.2012
comment
Привет, @GregPrice. Спасибо за объяснение, даже спустя 3 года. Однако в документе говорится, что scheduledTimerWithTimeInterval:... создает таймер и назначает его в текущем цикле выполнения в режиме по умолчанию. Так почему же вы добавляете его дважды в RunLoop по умолчанию, а не только один раз в режиме NSRunLoopCommonModes? - person Martin; 20.05.2015
comment
UIScrollView запускает цикл выполнения в UITrackingRunLoopMode, который отличается от NSDefaultRunLoopMode. Недостаточно запланировать таймер для режима по умолчанию, если вы также хотите, чтобы он работал в других режимах. - person rob mayoff; 10.03.2016
comment
Для Swift 3: RunLoop.current.add(self.myTimer, forMode: .commonModes) - person Steve Cotner; 21.12.2016

Еще одно (с)

  1. используйте метод timerWithTimeInterval, чтобы избежать добавления таймера в цикл выполнения в DefaultMode
  2. использовать mainRunLoop

So:

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

person Andrey Seredkin    schedule 08.07.2019