UISlider addTarget 和 sendActions 在 UT 中不起作用
Posted
技术标签:
【中文标题】UISlider addTarget 和 sendActions 在 UT 中不起作用【英文标题】:UISlider addTarget and sendActions not working in UTs 【发布时间】:2017-05-19 15:17:38 【问题描述】:当我在标准 MVC 项目中运行此代码时,一切正常:我可以以编程方式将操作发送到我的 UISlider。
override func viewDidLoad()
super.viewDidLoad()
slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
slider.minimumValue = 1
slider.maximumValue = 100
slider.addTarget(self, action: #selector(sliderTouch), for: .touchUpInside)
slider.sendActions(for: .touchUpInside)
func sliderTouch(sender: UISlider)
print("value: \(sender.value)")
现在由于某种原因,当我想在我的 UT 中模拟 Slider 行为时,它不起作用。这是代码:
func testSlider()
let slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
slider.minimumValue = 1
slider.maximumValue = 100
slider.addTarget(self, action: #selector(sliderTouch), for: .touchUpInside)
slider.sendActions(for: .touchUpInside)
func sliderTouch(sender: UISlider)
print("value: \(sender.value)")
sliderTouch()
在 UT 案例中从未被调用,据我了解,sendActions 不是asnyc
,应该直接调用操作方法。
那么为什么我会出现这种行为,我该如何解决呢?
编辑:我还需要它用于其他 UIKit 控件,例如 UISwitch
、UISegmentedControl
、UIDatePicker
等...
【问题讨论】:
你想抓住机会吗? 以什么方式测试您的组件与 UIKit 提供的动作调度功能?如果你想测试你的触摸处理,你可以直接调用action方法,比如sliderTouch(sender: slider)
。 (这无助于澄清这一点,您的 testSlider
测试函数没有实际的测试断言,因此总是通过。)
@JeremyW.Sherman 我的用例不是上面的 sn-p。我刚刚写了 sn-p 来说明主要问题是什么。实际上,我在库中有一个自定义 Slider,它以某种方式对动作调度做出反应,我想测试该行为是否正确。我没有在上面的 sn-p 中写断言,所以我尽量不混淆你并保持问题清晰:)
【参考方案1】:
在测试框架目标中包含的类时,我遇到了与您类似的问题。
这是Apple's documentation的摘录:
当发生特定于控件的事件时,控件会立即调用任何关联的操作方法。 Action 方法通过当前的 UIApplication 对象进行调度,该对象会找到一个合适的对象来处理消息,如果需要,跟随响应者链。
在框架中测试类时没有UIApplication
对象,因此无法调度事件。
您可以在测试目标设置中添加主机应用程序。如果您没有宿主应用程序,您可以创建一个空的 ios 应用程序并使用它。
另见this post。
更新:
以下是设置主机应用程序的方法:
如果下拉列表中没有应用程序,只需创建一个新目标并使用 应用程序 > 单视图应用程序模板。
你已经完成了,你的测试现在应该可以工作了。没有额外的步骤。
【讨论】:
这是正确的!我会考虑添加这个主机应用程序。如果有效,我会标记为正确 Vasili Muravev 在他的答案的 cmets 中指出了一种方法,并且似乎被 ReactiveCocoa 采用。它对我有用。你认为你的方法更好吗?让我知道,因为我找不到简单的方法来做到这一点。但无论如何,你的回答正确地给了我一个“为什么它不起作用”的答案。再次感谢您。 当然,与任何主题一样,每种方法都有优点和缺点。话虽如此,我认为我的解决方案应该是首选解决方案,因为它不依赖任何额外的代码。方法调配是一件非常强大的事情,但如果您不完全了解发生了什么,它会带来很多陷阱。此外,对于使用相同代码的其他团队成员来说,如果发现自己处于 UIKit 方法实际上并没有像通常那样做的情况下,这可能会非常令人惊讶,因为您交换了它的实现。 至于“简单的方法”,我将在我的帖子中添加一些屏幕截图,说明您需要做什么。 我更新了我的帖子,提供了有关如何设置 Host 应用程序的更多详细信息。如果您还有其他问题,请告诉我。【参考方案2】:对于 Swift 3: https://github.com/ReactiveCocoa/ReactiveCocoa/blob/ae6ebb7725b3d5d33db039d456797c220720cb99/ReactiveCocoaTests/UIKit/UIControl%2BEnableSendActionsForControlEvents.swift
对于 Swift 2 https://github.com/RACCommunity/Rex/blob/master/Tests/Helpers/UIControl%2BEnableSendActionsForControlEvents.swift
或者尝试子类化:
class TouchSlider: UISlider
var touchUpInsideHandler: ((TouchSlider) -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
touchUpInsideHandler?(self)
guard let touch = touches.first,
let imageWidth = currentThumbImage?.size.width else
super.touchesBegan(touches, with: event)
return
let location = touch.location(in: self)
let newValue: Float = minimumValue + (maximumValue - minimumValue) * Float((location.x - imageWidth / 2.0) / (bounds.width - imageWidth))
setValue(newValue, animated: true)
super.touchesBegan(touches, with: event)
使用touchUpInsideHandler
管理外部触摸。
【讨论】:
感谢您的解决方案。我担心的是,我不仅需要模拟UISlider
,还需要模拟UIDatePicker
、UISwitch
和许多其他的。对于一个简单的问题,这将是很多子类化和样板文件,你不觉得吗?
@GuyDaher 你应该把你的问题具体化;)So why am I getting this behavior, and how can I solve this?
你是对的,我的错。我认为有一个可以应用于所有 UIKit 控件的解决方案。我将编辑问题,谢谢您指出并再次抱歉
@GuyDaher 嗯,它对我有用:Test Case '-[BaseAppV2UnitTests.testTest testSleder]' started. value: 1.0 Test Case '-[BaseAppV2UnitTests.testTest testSleder]' passed (0.004 seconds).
@GuyDaher 看看github.com/RACCommunity/Rex/blob/master/Tests/Helpers/…以上是关于UISlider addTarget 和 sendActions 在 UT 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
UITapGestureRecognizer 和 addTarget 的区别
协议扩展和 addTarget 因 NSInvalidArgumentException 而崩溃
Swift:UIScrollView 和 UIControl - addTarget | addGestureRecognizer - 没有按预期工作