英文:
How to programmatically invoke the action of a `UISwitch`?
问题
我有一个UIViewController,位于一个SPM包中,其中包含一个带有一些简单单元格的UITableView - 这些单元格要么是详细披露,要么有一个UISwitch。
我正在尝试编写单元测试,以确认当特定行中的开关被切换时,会调用所需的操作。该操作是由其他代码提供的,因此单元格配置如下:
let toggle = UISwitch(frame: .zero, primaryAction: UIAction(handler: { action in
guard let toggle = action.sender as? UISwitch else { return }
toggleAction(toggle.isOn)
}))
cell.accessoryView = toggle
(其中toggleAction是传递给VC的init的众多操作之一)
我的测试可以找到感兴趣的行以及其中的开关。它可以通过.isOn修改值,但这不会触发操作。我知道需要一个UIApplication才能使.sendActions(for:)起作用,因此我使用了这里的解决方案,并确认它对UIButton有效。我尝试发送各种事件给UISwitch(.touchUpInside,.valueChanged,.primaryActionTriggered),但它们都不会导致注册的操作被调用。这是否可能?
英文:
I have a UIViewController, in an SPM package, with a UITableView with some simple cells - they're either detail disclosures or have a UISwitch.
I'm trying to write unit tests to confirm that when the switch in a specific row is toggled, the desired action is invoked. That action was provided by other code, so the cell configuration looks like:
let toggle = UISwitch(frame: .zero, primaryAction: UIAction(handler: { action in
guard let toggle = action.sender as? UISwitch else { return }
toggleAction(toggle.isOn)
}))
cell.accessoryView = toggle
(where toggleAction was one of many passed to the VC's init)
My test can find the row of interest, and the switch on it. It can modify the value via .isOn, but that doesn't trigger the action. I am aware of the need for a UIApplication in order for .sendActions(for:) to work, so am using the solution here, and confirm that it works for UIButtons. I have tried sending various events to the UISwitch (.touchUpInside, .valueChanged, .primaryActionTriggered), but none of them cause the registered action to be invoked. Is it possible?
答案1
得分: 1
UISwitch 是一个 UIControl。由于你使用了一个 UIAction 来设置开关的主要动作,你可以在开关上调用 sendActions:
let switch = ... // 从表格单元格获取 UISwitch 的引用
switch.isOn = true // 根据需要设置为 true 或 false
switch.sendActions(for: .primaryActionTriggered)
这将导致调用开关的动作,进而调用你的 toggleAction 方法。
这不需要引用 UIApplication。
英文:
UISwitch is a UIControl. Since you setup the switch with a UIAction for the primary action, you can call sendActions on the switch:
let switch = ... // a reference to the UISwitch from the table cell
switch.isOn = true // or false as needed
switch.sendActions(for: .primaryActionTriggered)
This will result in the switch's action being called which in turn calls your toggleAction method.
No reference to the UIApplication is needed for this.
Here is some sample code that demonstrates that using sendActions works for both UISwitch and UIButton and that it works whether the control is setup with a primary action or with a target/selector.
The following can be run in an iOS Swift Playground.
class Controls {
func toggleAction(_ isOn: Bool) {
print("toggleAction: \(isOn)")
}
@objc func toggleHandler(_ sender: UIControl) {
print("toggleHandler: \(sender)")
}
lazy var toggle = UISwitch(frame: .zero, primaryAction: UIAction(handler: { action in
guard let toggle = action.sender as? UISwitch else { return }
self.toggleAction(toggle.isOn)
}))
lazy var button = UIButton(primaryAction: UIAction(handler: { action in
self.toggleAction(false)
}))
lazy var other = UIButton(type: .custom)
func doStuff() {
other.addTarget(self, action: #selector(toggleHandler), for: .touchUpInside)
toggle.isOn.toggle()
toggle.sendActions(for: .primaryActionTriggered)
other.sendActions(for: .touchUpInside)
button.sendActions(for: .primaryActionTriggered)
}
}
let x = Controls()
x.doStuff()
The output will be something like:
>toggleAction: true
toggleHandler: <UIButton: 0x7fb77f406320; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x6000037e2fc0>>
toggleAction: false
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论