2

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

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

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

在 longTask 时间内,我再次单击该按钮,它真的没有任何反应。但是,当 longTask 完成后,它会自动响应点击事件并再次执行 longTask!当按钮的启用值为“否”时,我单击了多少次,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'?

感谢任何帮助!

4

3 回答 3

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

} 

请注意,当您这样做时,您的所有 UI 将不再被阻止,只会禁用所需的按钮。

正如@Erik Godard 提到的,您确实应该考虑在执行此类任务时使用某种 UI 反馈。您可以在将按钮的启用属性设置为的同一区域启动一些进程指示器,NO并在将属性设置为时停止它YES

另一种在没有 GCD 的情况下实现此目的的方法是通过NSRunLoop's 方法更改睡眠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;
} 

两种方法都经过测试,似乎正在奏效。

于 2013-08-20T01:59:05.887 回答
1

这里有几个问题。

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

    BOOL shouldCall;
    

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

正如@LucasEduardo 指出的那样,除非您摆脱睡眠,否则三个解决方法将不起作用,因此请忽略这一点。

于 2013-08-20T01:48:35.113 回答
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!
于 2013-08-20T13:13:15.330 回答