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

Ios 如何以编程方式将触摸事件伪造为UIButton?,ios,iphone,uibutton,touch-event,Ios,Iphone,Uibutton,Touch Event,我正在编写一些单元测试,由于这个特定应用程序的性质,我必须尽可能地在UI链中占据较高的位置。因此,我想做的是通过编程触发按钮按下,就像用户在GUI中按下按钮一样 (是的,是的——我可以直接调用iAction选择器,但同样,这个特定应用程序的性质使得我必须假装实际按下按钮,以便从按钮本身调用iAction。) 做这件事的首选方法是什么?如果你想做这种测试,你会喜欢iOS 4中的UI自动化支持。您可以编写JavaScript来模拟按钮按下等操作,尽管文档(尤其是入门部分)有点稀疏。事实证明 [按钮发

我正在编写一些单元测试,由于这个特定应用程序的性质,我必须尽可能地在
UI
链中占据较高的位置。因此,我想做的是通过编程触发按钮按下,就像用户在
GUI
中按下按钮一样

(是的,是的——我可以直接调用
iAction
选择器,但同样,这个特定应用程序的性质使得我必须假装实际按下按钮,以便从按钮本身调用
iAction
。)


做这件事的首选方法是什么?

如果你想做这种测试,你会喜欢iOS 4中的UI自动化支持。您可以编写JavaScript来模拟按钮按下等操作,尽管文档(尤其是入门部分)有点稀疏。

事实证明

[按钮发送控制事件的操作:UIControlEventTouchUpInside];
在这种情况下,我得到了我所需要的

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


对于Swift 3:

buttonObj.sendActions(用于:.touchUpInside)

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

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

但幸运的是,
uibarbuttoneim
具有target和action属性

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

这里,
rightBarButtonItem
属于
UIBarButtonItem

对Swift回答的更新

buttonObj.sendActionsForControlEvents(.TouchUpInside)
编辑:为Swift 3更新

buttonObj.sendActions(for: .touchUpInside)
对于Xamarin iOS

btnObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);
Swift 3:

self.btn.sendActions(for: .touchUpInside)
Swift 4:


self.yourButton(self)

对于编写没有UI测试的单元测试的人来说非常方便;-)

Swift为
UIBarButtonItem
提供了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()
Swift 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")
    }
}

你能解释一下为什么假装触摸按钮比直接调用动作方法更重要吗?实际上并没有什么区别。@Jasarien:因为有区别在我的应用程序中,几个按钮使用相同的动作方法,该方法根据按钮的标记解析行为。因此,单元测试除了通过该方法测试一个特定的代码路径外,(a)还需要测试所有的代码路径,(b)还验证按钮是否正确连接。此外,它很好地抽象了我的单元测试套件,因为我可以在文本文件中将单元测试定义为这样的行:“1+2=3”,其中“3”是预期的结束显示,而之前的所有内容都是要按下哪个按钮。那么,您的操作方法没有(id)sender参数吗?你就不能调用这个方法并将按钮作为发送者传递吗?因为我的测试工具的抽象性质,我不知道需要为特定的按钮调用什么方法。我想我可以挖掘按钮并获得它,但执行sendActionsForControlEvents:这一切都是为了我,错误的机会更少,并保持测试抽象和符合设计。是的,这是很酷的东西,但我尝试从应用程序内部执行,而不是通过仪器(如果你指的是这种方法的话)仪器与你的应用程序一起运行。应用程序还在运行,仪器只是模拟触摸。我理解仪器,杰夫——我想说的是:我希望我的单元测试在口袋里的设备中运行,而不需要笔记本电脑、XCode或仪器。或者你知道在设备上运行仪器的秘密吗?;)这与调用
[myController myActionMethod:myButton1]没有什么不同您在评论回复中有很大的限制;)不幸的是,这两种方法都不总是有效的-它们依赖于接收者是一个UIControl,而不是一个点击手势。@ChenLiYong,一个原因是如果
myActionMethod:
不是公共的,所以你不能直接调用它。Xamarin:
buttonObj.SendActionForControlEvents(UIControlEvent.TouchUpInside)如果您使用uiBarButtonim,您可以只使用[self-doAction:self.navigationItem.RightBarButtonim]//-doAction:是self.navigationItem.RightBarButtonim的切换操作名称。我想说的是:buttonObj.sendActionsForControlEvents(.TouchUpInside)更为快速;)如果按钮是在没有运行循环支持的XCTestCase中创建的,我认为这是行不通的。这只是重复得分较高的答案。