iOS: Perform action after time while user is interacting / scrolling
Asked Answered
C

3

13

I'm trying to get my application to perform an action after a delay, but it will have to be done WHILE the user is interacting with/scrolling on a UIScrollView.

I'm not sure why neither performSelector:withObject:afterDelay or scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: will fire. Is it because they're on a background thread?

Any suggestions or help?

Congruity answered 28/6, 2012 at 20:52 Comment(7)
What is not working with performSelector: afterDelay:? What is it you are trying to do? Are you SURE they don't fire - i.e you put an NSLog statement in to check?Grubstake
I have put in a log statement. It fires after the user stops interacting with the screen. I am trying to update a label.Congruity
I've found this: #2866260, but there isn't any way to incorporate a delay with that.Congruity
Ok, good. Have you tried performSelectorOnMainThread in your timer callback?Grubstake
Theres no delay in that, though.Congruity
Have your timer call a method - which THEN calls performSelectorOnMainThread. The timer is doing the delay.Grubstake
The timer doesn't work either.Congruity
A
24

Both NSTimer and performSelector:withObject:afterDelay: by default only fire in the normal run loop mode. When scrolling, the run loop is in event tracking mode.

You have to schedule your timed action in all common modes:

NSTimer *timer = [NSTimer timerWithTimeInterval:0.016 target:self selector:@selector(fire:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

or

[self performSelector:@selector(fire:) withObject:nil afterDelay:1.0 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];

There's also the dedicated NSEventTrackingRunLoopMode.

Agenesis answered 28/6, 2012 at 21:7 Comment(6)
So, its just like what I have in the comment?Congruity
@Congruity I'm not sure I understand what you mean. Which comment?Agenesis
Sorry. I'm being very vague! The comment where I mention a post I was looking at before. The post is: #2866260Congruity
Exactly. The answer matches my answer, except for the timer based approach.Agenesis
Would you be willing to answer another issue I'm having with this? I'm about to edit my post.Congruity
You should post that as a new question.Agenesis
N
4

Be sure the delayed trigger happens on the NSRunLoopCommonModes. The default is NSDefaultRunLoopMode which won't get messages while e.g. scrolling.

[self performSelector:@selector(fire:) withObject:nil afterDelay:2.0 inModes:@[NSRunLoopCommonModes]];

Alternatively you can use GCD which also behaves the same way (not sure which runloop mode it uses)

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    <#code to be executed on the main queue after delay#>
});
Nason answered 15/5, 2013 at 8:6 Comment(0)
R
0

For Swift:

performSelector(#selector(fire:), withObject: sender, afterDelay: 1.0, inModes: [NSRunLoopCommonModes])
Rockett answered 22/7, 2016 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.