在很长的任务时间内忽略UIButton的触摸事件

时间:2013-08-20 01:28:40

标签: ios objective-c uibutton

我希望在长任务运行时忽略按钮的所有触摸事件。

- (void)buttonAction{
    NSLog(@"click!");
    button.enabled = NO;
    [self longTask];
}

- (void)longTask{
    NSLog(@"task begin!");
    sleep(5);
    NSLog(@"task finished!");
    button.enabled = YES;
}

在longTask期间,我再次点击按钮,它确实没有任何反应。但是,当longTask完成时,它会自动响应click事件并再次执行longTask! 当按钮的启用值为“NO”时,我点击了多少次,longTask将执行多少次。

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!

我尝试设置userInteractionEnabled = NO,但结果相同。

如何在长任务运行时忽略所有触摸事件并且从不执​​行任务?换句话说,只有在单击按钮时才执行longTask,启用的值是'YES'?

感谢任何帮助!

3 个答案:

答案 0 :(得分:3)

sleep只是冻结主线程,负责所有UI交互。

您应该在后台执行所有长任务,使用GCD可以轻松完成。就像下面这样,你应该能够达到你想要的效果:

- (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!");

} 

请注意,当您这样做时,您的所有用户界面都不会被阻止,只会禁用所需的按钮。

正如@Erik Godard所说,在执行此类任务时,您应该考虑使用某种UI反馈。您可以在将按钮的已启用属性设置为NO的同一区域中启动某个流程指示器,并在将属性设置为YES时将其停止

在没有GCD的情况下实现这一目标的另一种方法是通过NSRunLoop方法runUntilDate改变睡眠状态。通过这种方式,您的主线程也不会被阻止,您将能够实现您想要的。

- (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;
} 

两种方法都经过测试,似乎正在发挥作用。

答案 1 :(得分:1)

这里有几个问题。

  1. 首先,如果您正在执行任务并且您不希望用户在输入时输入输入,您应该提供某种可视指示符来告诉用户该应用程序尚未冻结。简单地禁用按钮而不提供正在发生的事情的指示对用户体验是不利的。例如,您可以使用进度条或进度指示器来警告用户任务正在完成。
  2. 其次,如果你正在做一些需要花费大量时间的任务,比如你提议的那个,你应该考虑使用多线程。有关此内容的介绍,请参阅here。通常,您不希望像这样在主线程上执行计算密集型任务。
  3. 最后,如果您仍想继续使用此方法,则可以声明一个标志,用于定义是否应调用longTask。您可以创建一个私有实例变量

    BOOL shouldCall;
    
  4. 第一次调用longTask时,将其设置为NO。 longTask完成后,再次将其更改为yes。您可以将此与.hidden结合使用来控制按钮的UI。

    正如@LucasEduardo指出的那样,除非你摆脱睡眠,否则三种解决方法将无效,所以无视这一点。

答案 2 :(得分:0)

正如@Lucas Eduardo所说,在后台线程中执行一项长任务是最好的实现。但在某些情况下,任务必须在主线程上执行,例如撕毁PDF或Web视图或移动大图像。而且您仍然需要忽略运行时中的触摸事件。

感谢@Erik Godard的观点。当我必须在主线程中执行长任务时,我必须设置一个BOOL值来响应触摸事件。

- (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;
}

我在0.5秒延迟后更改BOOL值,因为如果在button.enabled = YES之后立即设置isRunning = NO,则将再次执行longTask。只需延迟0.5秒即可让触摸事件通过。在那个时期,isRunning的值仍然等于YES,因此触摸事件将被忽略。 0.5秒后,isRunning = NO,按钮将触发新的longTask,如果再次点击它。

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!