从 IBAction 以编程方式设置 UISwitch isOn 再次调用 IBAction

Posted

技术标签:

【中文标题】从 IBAction 以编程方式设置 UISwitch isOn 再次调用 IBAction【英文标题】:Setting UISwitch isOn programmatically from IBAction calls IBAction again 【发布时间】:2017-03-03 22:17:46 【问题描述】:

我刚刚注意到在其 IBAction 中设置 UISwitch 的 isOn 会导致再次调用 IBAction。所以下面的代码:

class ViewController: UIViewController 
    var count = 0
    @IBOutlet weak var mySwitch: UISwitch!

    override func viewDidAppear(_ animated: Bool) 
        super.viewDidAppear(animated)
        mySwitch.isOn = false
    

    @IBAction func buttonTapped(_ sender: UIButton) 
        mySwitch.isOn = !mySwitch.isOn
    

    @IBAction func switchChanged(_ sender: UISwitch) 
        print("\(count) pre: \(mySwitch.isOn)")
        mySwitch.isOn = !mySwitch.isOn
        print("\(count) post: \(mySwitch.isOn)")
        count += 1
    

当开关打开一次时打印以下内容:

0 pre: true
0 post: false
1 pre: false
1 post: true
    开关在viewDidLoad 中关闭 开关被用户打开 当switchChanged (IBAction) 被调用时,开关现在打开 0 pre: true 已打印 开关在switchChanged 中以编程方式关闭 0 post: false 已打印 系统再次调用switchChanged 现在switchChanged 中的开关已关闭,并且调用了1 pre: false 以编程方式打开开关 1 post: true 已打印

为什么系统会第二次调用 IBAction?例如,当想要基于某种内部状态否定用户的操作时,如何解决这个问题?我觉得我遗漏了一些非常明显的东西,但我很确定类似的代码曾经可以工作。这是ios错误吗?它在 iOS 10.2 iPhone 5s 模拟器 Xcode 版本 8.2.1 (8C1002) 上运行

有趣的是,当点击绑定到buttonTapped 的按钮时(调用相同的方法),不会调用开关的 IBAction。

【问题讨论】:

您可能已将 switch 事件处理程序连接到两个不同的事件。 我对其进行了测试,并看到了与您相同的行为。仅当您通过点击它来更改开关时,通过拖动它来更改它只会导致该操作被调用一次。 switchChanged 中切换开关没有意义。如果您希望开关关闭,请将其显式设置为关闭,然后再次调用该方法无关紧要,因为第二次它将关闭并且您将其设置为关闭并且不会被调用再次。 gnasher729,我确实考虑过不止一个事件可以连接到交换机,但事实并非如此。丹,我确认了您在拖动开关时看到的内容。即,通过拖动来更改开关,IBAction 被调用一次,“撤消”就像我最初预期的那样工作。 Paulw,如果我只是想让它关闭,也许这会奏效,但如果我真的想将它切换回初始状态怎么办,问题仍然在于为什么该方法被调用两次。此外,如果以编程方式设置 isOn 导致该方法被调用一次,为什么它不继续被调用? 【参考方案1】:

您的 IBAction 可能与 valueChanged 挂钩,这并不表示特定的触摸事件,只是它所说的值已更改。

我建议设置一个类似于var didOverrideSwitchValue = false 的变量,在设置新开关值之前将其设置为true,然后在调用该函数时检查该变量。如果设置为true,则设置为false并返回。

或者,如果您希望仅在新设置打开时取消它,那么您可以执行 if (switch.isOn),如果需要,您可以通过关闭它来响应它。

【讨论】:

Andrew,没错,它已连接到 valueChanged。我不确定我是否完全理解您的第一个建议,假设我总是想切换布尔值。我仍在试图理解为什么 set-isOn/IBAction-invocation 循环不会无限期地继续,为什么拖动开关的行为与点击不同,以及为什么当 isOn 在 @ 中更改时开关的 IBAction 没有被调用987654329@.【参考方案2】:

我一直在与同样的问题作斗争并找到了解决方法...

在您的开关处理程序中检查发送方的“已选择”属性。我发现第一次是真的,第二次是假的,所以你可以判断你是否真的被用户操作调用了。

我猜想第二次触发事件的原因不是开关本身,或者可能在处理第一个事件后该属性被清除。也许 UIKit 大师可以插话。

-setOn:animated 的 UISwitch 文档:说

将开关设置到任一位置都不会导致发送操作消息。

看起来很清楚。感觉像是一个操作系统错误。

无论如何,这似乎可行,但它让我感到不安,因为我不完全理解为什么首先存在问题,也不确切为什么会修复它,我担心这两者都可能在未来的操作系统更新中发生变化。

更新

这在我的小测试应用程序中运行良好,但在我的实际应用程序中却不行,它具有更复杂的 UI 层次结构,带有导航栏、选项卡等。这只会加剧我对这个解决方案的不安。

【讨论】:

以上是关于从 IBAction 以编程方式设置 UISwitch isOn 再次调用 IBAction的主要内容,如果未能解决你的问题,请参考以下文章

UIButton click(IBAction)没有以编程方式触发

在 TableViewHeader 中以编程方式调用 IBAction

如何在 Swift 4 中以编程方式将 IBAction 添加到按钮?

以编程方式将 IBAction 添加到 UIButton

以编程方式设置在视图控制器上添加的对象的标记

以编程方式从 Xib 执行 Segue