Reputation: 1091
I want to ignore all touch events of a button while a long task is running.
- (void)buttonAction{
NSLog(@"click!");
button.enabled = NO;
[self longTask];
}
- (void)longTask{
NSLog(@"task begin!");
sleep(5);
NSLog(@"task finished!");
button.enabled = YES;
}
During the longTask time, I click the button again, it really nothing happens. BUT, when the longTask is finished, it automatically respond to the click events and execute the longTask again! How many times I clicked when the button's enabled value is 'NO', the longTask will perform how many times.
2013-08-20 09:24:49.478 AppName[2518:c07] click!
2013-08-20 09:24:49.479 AppName[2518:c07] task begin!
2013-08-20 09:24:54.481 AppName[2518:c07] task finished!
2013-08-20 09:24:54.482 AppName[2518:c07] click!
2013-08-20 09:24:54.482 AppName[2518:c07] task begin!
2013-08-20 09:24:59.484 AppName[2518:c07] task finished!
I tried to set userInteractionEnabled=NO but got the same result.
How can make it ignores all touch events when a long task is running and never performs the task? In other words, only executes the longTask when the button is clicked at it's enabled value is 'YES'?
Thanks any help!
Upvotes: 2
Views: 1072
Reputation: 6030
There are several issues here.
Finally, if you still want to continue with this approach, you can declare a flag that defines whether or not your longTask should be called. You could make a private instance variable
BOOL shouldCall;
When you call longTask for the first time, set it to NO. Once the longTask completes, change it to yes again. You can use this in conjunction with .hidden to control the UI of your button.
As @LucasEduardo pointed out, the workaround in three won't work unless you get rid of the sleep, so disregard that.
Upvotes: 1
Reputation: 1091
As @Lucas Eduardo mention, perform a long task in background thread is the best implementation. But in some situations, the task must be performed at main thread, such as rending a PDF or a web view or moving a big image. And you still need to ignore the touch events in the runtime.
Thanks @Erik Godard's points. When I have to perform a long task in main thread, I have to set a BOOL value to response to the touch events.
- (void)buttonAction{
NSLog(@"click!");
[self longTask];
}
- (void)longTask{
if (!isRunning) {
button.enabled = NO;
isRunning = YES;
NSLog(@"task begin!");
[NSThread sleepForTimeInterval:5];
NSLog(@"task finished!");
button.enabled = YES;
[self performSelector:@selector(switchStatus) withObject:nil afterDelay:.5f];
}
}
- (void)switchStatus{
isRunning = NO;
}
- (void)viewDidLoad
{
[super viewDidLoad];
isRunning = NO;
}
I change the BOOL value after 0.5 second delay, because if isRunning = NO is immediately set after button.enabled = YES, the longTask will be performed again. Just leave 0.5 second delay to let the touch events go through. At that period, isRunning's value still equal YES, so touch events will be ignored. 0.5 second later, isRunning=NO, the button will trigger new longTask if which be clicked again.
2013-08-20 20:44:19.727 AppName[4005:c07] click!
2013-08-20 20:44:19.728 AppName[4005:c07] task begin!
2013-08-20 20:44:24.730 AppName[4005:c07] task finished!
2013-08-20 20:44:24.731 AppName[4005:c07] click!
2013-08-20 20:44:24.732 AppName[4005:c07] click!
Upvotes: 0
Reputation: 11675
The sleep
is simply freezing the main thread, who is responsible for all UI interations.
You should perform all long tasks in background, which can be easlily achieved with GCD. Just do like below and you should be able to achieve what you want:
- (void)buttonAction{
NSLog(@"click!");
button.enabled = NO;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
[self longTask];
dispatch_async(dispatch_get_main_queue(), ^(void){
button.enabled = YES;
});
});
}
- (void)longTask{
NSLog(@"task begin!");
[NSThread sleepForTimeInterval:5];
NSLog(@"task finished!");
}
Notice that when you do this way, all your UI won't be blocked anymore, just the desired button will be disabled.
As @Erik Godard mentioned, you really should consider to use some kind of UI feedback when performing this kind of tasks. You can start some process indicator in the same area where you set the button's enabled property to NO
and stop it when setting the property to YES
Another way to accomplish this, without GCD, is changing the sleep by NSRunLoop
's method runUntilDate
. In this way, your main thread won't be blocked too, and you would be able to achieve what you are wanting.
- (void)buttonAction{
NSLog(@"click!");
self.addCartButton.enabled = NO;
[self longTask];
}
- (void)longTask{
NSLog(@"task begin!");
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
NSLog(@"task finished!");
self.addCartButton.enabled = YES;
}
Both approach tested and seems to be working.
Upvotes: 3