如何以编程方式将触摸事件伪造为 UIButton?

Posted

技术标签:

【中文标题】如何以编程方式将触摸事件伪造为 UIButton?【英文标题】:how to programmatically fake a touch event to a UIButton? 【发布时间】:2011-05-01 00:49:11 【问题描述】:

我正在编写一些单元测试,并且由于这个特定应用程序的性质,重要的是我尽可能在UI 链中处于较高位置。所以,我想做的是以编程方式触发按钮按下,就好像用户按下了GUI 中的按钮。

(是的,是的——我可以只调用IBAction 选择器,但同样,这个特定应用程序的性质使得我假装实际按下按钮很重要,这样@ 987654324@ 被按钮本身调用。)

这样做的首选方法是什么?

【问题讨论】:

您能解释一下为什么伪造按钮触摸而不是直接调用操作方法很重要吗?确实没有实际区别。 @Jasarien:嗯,因为有区别! ;) 在我的应用程序中,几个按钮使用相同的操作方法,该方法根据按钮的标签解析行为方式。因此,单元测试除了通过方法测试一个特定的代码路径之外,(a) 需要对它们全部进行测试,并且 (b) 还需要验证按钮是否正确连接。此外,它很好地抽象了我的单元测试套件,因为我可以将单元测试定义为文本文件中的行,如下所示:“1 + 2 = 3”其中“3”是预期的结束显示,以及之前的所有内容是按下哪个按钮。 那么您的操作方法没有 (id)sender 参数吗?你不能只调用方法并将按钮作为发送者传递吗? 由于我的测试工具的抽象性质,我不知道需要为特定按钮调用的方法。我想我可以深入研究按钮并获取它,但是执行 sendActionsForControlEvents: 为我完成了所有这些操作,出错的机会更少,并保持测试抽象并符合设计。 【参考方案1】:

原来是这样

[buttonObj sendActionsForControlEvents:UIControlEventTouchUpInside];

在这种情况下,我得到了我需要的东西。

编辑:不要忘记在主线程中执行此操作,以获得类似于用户按下的结果。


对于 Swift 3:

buttonObj.sendActions(for: .touchUpInside)

【讨论】:

这和打电话给[myController myActionMethod:myButton1];没有什么不同 你的评论回复非常克制;) 不幸的是,这些都不会总是有效 - 它们依赖于作为 UIControl 的接收器,而不是例如具有点击手势。 @ChenLiYong,一个原因是myActionMethod: 不是公开的,所以你不能直接调用它。 Xamarin: buttonObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);【参考方案2】:

对 Swift 这个答案的更新

buttonObj.sendActionsForControlEvents(.TouchUpInside)

编辑:针对 Swift 3 更新

buttonObj.sendActions(for: .touchUpInside)

【讨论】:

我会说:buttonObj.sendActionsForControlEvents(.TouchUpInside) 风格更加 Swift ;) 如果按钮是在没有运行循环支持的 XCTestCase 中创建的,我认为这不会起作用。【参考方案3】:

斯威夫特 3:

self.btn.sendActions(for: .touchUpInside)

【讨论】:

这只是重复得分较高的答案。【参考方案4】:

如果你想做这种测试,你会喜欢 ios 4 中的 UI 自动化支持。你可以编写 javascript 来模拟按钮按下等相当容易,虽然文档(尤其是入门部分)有点稀疏。

【讨论】:

是的,这很酷,但我试图在应用程序内完成,而不是通过 Instruments(如果你的意思是这样的话。) Instruments 与您的应用程序一起运行。该应用程序仍在运行,仪器只是模拟触摸。 我了解 Instruments,Jeff -- 我的意思是:我希望我的单元测试能够在我口袋里的设备中运行,而不需要笔记本电脑或 XCode 或 Instruments。或者你知道在设备上运行仪器的秘密吗? ;)【参考方案5】:

在这种情况下,UIButton 派生自 UIControl。这适用于从UIControl 派生的对象。

我想在特定用例上重复使用“UIBarButtonItem”操作。在这里,UIBarButtonItem 不提供方法sendActionsForControlEvents:

但幸运的是,UIBarButtonItem 具有目标和操作属性。

 if(notHappy)        
         SEL exit = self.navigationItem.rightBarButtonItem.action;
         id  world = self.navigationItem.rightBarButtonItem.target;
         [world performSelector:exit];
 

这里,rightBarButtonItem 的类型是 UIBarButtonItem

【讨论】:

如果你使用UIBarButtonItem,你可以[self doAction:self.navigationItem.rightBarButtonItem]; //-doAction: 是 self.navigationItem.rightBarButtonItem 的切换动作名称。【参考方案6】:

对于 Xamarin iOS

btnObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);

Reference

【讨论】:

【参考方案7】:

斯威夫特 5:

class ViewController: UIViewController 
    @IBOutlet weak var theTextfield: UITextField!
    @IBOutlet weak var someButton: UIButton!
    override func viewDidLoad() 
        super.viewDidLoad()
        theTextfield.text = "Pwd"
        someButton.sendActions(for: .touchUpInside)
    

    @IBAction func someButtonTap(_ sender: UIButton) 
        print("button tapped")
    

【讨论】:

【参考方案8】:

对于编写单元测试而不使用 UI 测试的人来说很方便 ;-)

UIBarButtonItem 的 Swift 5 解决方法,它没有 UIButton 等 sendAction 方法。

extension UIBarButtonItem 
    func sendAction() 
        guard let myTarget = target else  return 
        guard let myAction = action else  return 
        let control: UIControl = UIControl()
        control.sendAction(myAction, to: myTarget, for: nil)
    

现在您可以简单地:

let action = UIBarButtonItem(title: "title", style: .done, target: self, action: #selector(doSomething))
action.sendAction()

【讨论】:

【参考方案9】:

斯威夫特 4:

self .yourButton(self)

【讨论】:

以上是关于如何以编程方式将触摸事件伪造为 UIButton?的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式为处于触摸状态的 UIButton 创建灰色叠加层?

如何以编程方式突出显示 UIButton?

为啥 UIView 的这些自动布局约束会阻止 UIButton 子视图接收触摸事件?

无法触摸 UIScrollView 中的 UIButton

如何禁用标准 UIButton 触摸事件

在 UIButton 上实现触摸取消