UIButton带有多个动作:如何防止其他动作触发

3
我希望找到一种消耗按钮按下事件的方法,不是从响应链中消耗,而是从绑定到该按钮的其他操作方法中消耗。我已经搜索了很多地方,但都没有找到解决方法。
例如,假设我设置了一个具有事件选择器的按钮:
[button addTarget:self action:@selector(handler1:) forControlEvents:UIControlEventTouchUpInside];

后续代码中,根据特定应用程序情况,我希望为相同的控件事件向同一个按钮添加另一个事件处理程序:

[button addTarget:self action:@selector(handler2:) forControlEvents:UIControlEventTouchUpInside];

这个方法可以正常工作,两个事件都确实被调用了。但我的问题是,在不删除按钮上的handler1的情况下,我如何使handler2被调用时,该事件被"消耗",从而不调用handler1?

我之所以有这样的情况,是因为我想让我的应用进入教程模式,在该模式下动态绑定新的事件到按钮上。教程将指导用户点击某个按钮,但我希望屏幕上其他按钮的点击事件被忽略,基本上强制用户点击请求的按钮以继续进行教程。因此,当用户进入教程时,每个按钮都会添加一个新的TouchUpInside handler。我希望首先调用这个新的handler并阻止原始handler执行。

我已经能够通过获取所有原始事件并对所有现有事件调用[button removeTarget...]来使其首先被调用。然后我添加了我的动态事件,然后重新添加了集合中的所有原始事件。在调试器中,这可以显示我的动态事件确实被首先调用。

  • 例如:
  • handler1在按下时会执行某些操作(按钮的默认处理程序)
  • handler2是动态添加的,将与教程控制器通信,"消耗"触摸事件(防止handler1执行)。

当不处于教程模式时,我仍希望handler1执行其预期的操作,但如果存在handler2,则希望该方法运行,然后防止调用handler1。我不能从按钮中删除handler1,因为当教程结束时,我希望应用程序按预期工作。此外,我可能有某些情况仍希望调用handler1。

所以,有没有可能消耗一个事件并阻止其他相关事件的触发?

我已经尝试在handler2中使用[button resignFirstResponder],但似乎没有起作用。它仍然调用原始按钮事件处理程序。


1
当按钮的教程功能完成时,为什么不能重置第一个处理程序? - jscs
该应用程序有数十个按钮。也许在教程中,屏幕上只有一个或两个按钮会被涉及。如果用户按下任何未涉及的按钮,我不希望它们按默认方式执行操作。但是,如果我只是删除处理程序,那么我必须在某个地方跟踪所有这些按钮,然后在教程结束时将它们添加回来。我可以这样做,但是我正在寻找更优雅的解决方案。如果无法实现,那么我将不得不选择蛮力技术。:) 感谢您的回复。 - zunkination
另一个选择是使用一个覆盖常规按钮的不可见按钮来实现教程功能。 - jscs
很抱歉,这不是一个重复的问题。我不想从按钮中删除目标动作。我的问题是,我想给一个按钮添加一个新的目标,并且只有这个目标会被调用,直到我手动删除该目标动作。谢谢。 - zunkination
是的,但随后当教程结束时,我必须将所有这些先前的操作添加回来。 对于参与教程的按钮(例如“嘿用户,点击此按钮”),我实际上想保留它们的默认行为。 我希望在不同的视图控制器中处理组成教程的按钮的逻辑,并按照必须按下它们的顺序进行排列。 因此,当正确的按钮被按下时,动作发生。 如果按下错误的按钮,则不会发生任何事情。 教程结束时,“教程操作”将被删除,并且应用程序正常工作。 - zunkination
显示剩余2条评论
3个回答

3

根据@danielquokka关于重写方法sendActionsForControlEvents:的想法,我创建了一个UIButton子类并添加了以下代码。它可以正常工作。在触发事件后,它会阻止UI事件0.5秒。

- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    [super sendAction:action to:target forEvent:event];
    self.userInteractionEnabled = NO;

    ///! After handle UIEvent, block this button UI events for a while.
    [self performSelector:@selector(delayEnable) withObject:nil afterDelay:0.5];
}

- (void)delayEnable
{
    self.userInteractionEnabled = YES;
}

更新

另一种忽略UI事件的方法是使用 [[UIApplication sharedApplication] beginIgnoringInteractionEvents][[UIApplication sharedApplication] endIgnoringInteractionEvents]

beginIgnoringInteractionEventsendIgnoringInteractionEvents 会阻止应用程序的所有触摸事件,直到调用 endIgnoringInteractionEvents


如此简单,却又如此有效。 - NYC Tech Engineer

1
如果您知道应用程序的当前状态,例如教程等,则最好只使用一个handler1。并将以下内容添加到方法handler1的主体中:if(mode == tutorial){教程} else {默认}。

1
我认为这不是一个好的解决方案;这个很少使用的代码不需要出现在主处理程序中。它只会使事情变得更难以阅读和维护。 - jscs
我可以这样做,但是这个特定的应用程序是一个复杂的规则计算器(用于竞赛评判)。它有多个屏幕,每个屏幕上都有几十个按钮(这就是为什么我们需要一个教程的原因)。这将需要我进行许多修改,而这些修改可以通过更优雅的高级解决方案避免。我感谢您的建议,如果使用iOS框架无法实现此请求,我最终会采取类似的方法,但我想先在这里询问是否有更好的方法来完成这项任务。谢谢。 - zunkination
好的。然后你可以添加GestureRecognizer并使用方法...shouldReceiveTouch。在这个方法中检查它是否是你的按钮,然后返回NO(取消handler1),并在那个位置调用handler2。我没有测试过它,但它可能作为教程的独立解决方案。 - kabarga
谢谢kabarga,这是一个有趣的想法,可能正好符合我的需求。我会回报... - zunkination

1
你可以添加一个类别到UIButton(或其子类),重写sendActionsForControlEvents:方法,仅为一个目标调用sendAction:to:forEvent:(有关这些方法的描述,请参阅UIControl文档)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接