简体   繁体   English

在长时间的任务中忽略UIButton的touch事件

[英]Ignore UIButton's touch event in a long task time

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. 在longTask时间内,我再次单击该按钮,实际上什么也没有发生。 BUT, when the longTask is finished, it automatically respond to the click events and execute the longTask again! 但是,当longTask完成时,它将自动响应单击事件并再次执行longTask! How many times I clicked when the button's enabled value is 'NO', the longTask will perform how many times. 当按钮的启用值为“否”时,我单击了多少次,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!

I tried to set userInteractionEnabled=NO but got the same result. 我试图将userInteractionEnabled设置为NO,但结果相同。

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'? 换句话说,仅在启用按钮值为“是”时单击按钮时才执行longTask吗?

Thanks any help! 谢谢你的帮助!

The sleep is simply freezing the main thread, who is responsible for all UI interations. sleep只是冻结了负责所有UI交互的主线程。

You should perform all long tasks in background, which can be easlily achieved with GCD . 您应该在后台执行所有冗长的任务,而使用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. 请注意,当您以这种方式进行操作时,将不再阻止所有UI,仅将禁用所需的按钮。

As @Erik Godard mentioned, you really should consider to use some kind of UI feedback when performing this kind of tasks. 正如@Erik Godard提到的那样,您确实应该在执行此类任务时考虑使用某种UI反馈。 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 您可以在将按钮的enabled属性设置为NO的同一区域中启动某个过程指示器,并在将该属性设置为YES时停止它

Another way to accomplish this, without GCD, is changing the sleep by NSRunLoop 's method runUntilDate . 没有GCD的另一种方法是通过NSRunLoop的方法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. 两种方法都经过测试,似乎可行。

There are several issues here. 这里有几个问题。

  1. First of all, if you are performing a task and you don't want the user to be able to enter input while it's occurring, you should provide some sort of visual indicator to tell the user that the app has not frozen up. 首先,如果您正在执行任务,并且您不希望用户在输入任务时就能够输入,则应提供某种视觉指示器以告知用户该应用程序尚未冻结。 Simply disabling a button without giving some indication of what's going on is bad for the user experience. 仅禁用按钮而不给出任何提示说明这对用户体验不利。 For example, you could use a progress bar or a progress indicator to alert the user that a task is being completed. 例如,您可以使用进度条或进度指示器来警告用户任务已完成。
  2. Secondly, if you're doing some task that will take a significant amount of time like the one that you're proposing, you should consider using multithreading. 其次,如果您要执行的任务要花费大量时间(例如所建议的任务),则应考虑使用多线程。 See here for an introduction to this. 有关的介绍,请参见此处 Generally, you don't want to do computationally intensive tasks on the main thread like this. 通常,您不想像这样在主线程上执行计算密集型任务。
  3. 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. 最后,如果您仍然想继续使用此方法,则可以声明一个标志,该标志定义是否应调用longTask。 You could make a private instance variable 您可以将一个私有实例变量

     BOOL shouldCall; 

When you call longTask for the first time, set it to NO. 首次调用longTask时,请将其设置为NO。 Once the longTask completes, change it to yes again. 一旦longTask完成,将其再次更改为yes。 You can use this in conjunction with .hidden to control the UI of your button. 您可以将其与.hidden结合使用以控制按钮的UI。

As @LucasEduardo pointed out, the workaround in three won't work unless you get rid of the sleep, so disregard that. 正如@LucasEduardo所指出的那样,除非您摆脱睡眠,否则三分之二的替代方法都不会起作用,因此,请不要理会。

As @Lucas Eduardo mention, perform a long task in background thread is the best implementation. 正如@Lucas Eduardo提到的那样,在后台线程中执行长任务是最好的实现。 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. 但是在某些情况下,该任务必须在主线程中执行,例如发送PDF或Web视图或移动大图像。 And you still need to ignore the touch events in the runtime. 而且您仍然需要在运行时忽略触摸事件。

Thanks @Erik Godard's points. 感谢@Erik Godard的观点。 When I have to perform a long task in main thread, I have to set a BOOL value to response to the touch events. 当我必须在主线程中执行较长的任务时,必须设置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;
}

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. 我在0.5秒的延迟后更改BOOL值,因为如果在button.enabled = YES之后立即设置isRunning = NO,则将再次执行longTask。 Just leave 0.5 second delay to let the touch events go through. 只需保留0.5秒的延迟,即可使触摸事件继续进行。 At that period, isRunning's value still equal YES, so touch events will be ignored. 在此期间,isRunning的值仍等于YES,因此触摸事件将被忽略。 0.5 second later, isRunning=NO, the button will trigger new longTask if which be clicked again. 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!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM