【问题标题】:What would be better strategy for IBActions in protocols in Swift?Swift 协议中的 IBActions 有什么更好的策略?
【发布时间】:2015-04-11 09:45:22
【问题描述】:

我正在创建一个 Swift 项目,我想定义一个特定的协议来强制其他组件实现 animate 方法:

protocol AnimatableBehavior {
    @IBAction func animate()
}

问题是我希望这个方法是一个IBAction,但是我从 XCode 得到这个错误:

只有实例方法可以声明为“IBAction”

我的问题是,你将如何实现这样的事情?

我考虑过:

  1. 删除@IBAction,但我需要记住在每个实现的类中添加它。不是很优雅且容易出错。
  2. 创建一个基类而不是协议,但随后我强制所有组件对我的基类进行子类化,而不是它们自己选择的子类,因此它不是一个有效的选项。

还有其他想法吗?


编辑:对以下 cmets 的回应。

协议上IBAction的想法是因为在项目中会有很多不同的开发者实现小的UI组件,它们都有animate方法。 可以通过编程方式或通过界面生成器添加组件,它们始终为IBAction 非常方便,因为我计划从 IB 文件中组合它们以简化视图控制器最大限度地发挥作用(这显然是一项仅限查看的任务)。

因此,下面提出的在控制器中添加一个仅调用组件的animate 的方法的解决方案并不好,因为它是冗余代码,并且会使您的控制器更加依赖您的视图。

让开发人员记住在方法上添加 IBAction 关键字的想法是可行的,但正如我所说的那样容易出错(我的意思是会有一些人忘记它),我想要确保始终可以从 IB 访问它。它还增加了额外的认知负担,因为我需要记录协议中缺少IBAction 并请求实现者手动添加它。

我知道这不是在 iOS 和 UIKit 中工作的常见方式,但这就是我发布问题的原因,也许有人有其他想法。

【问题讨论】:

  • 我认为没有其他解决方案。也许看看大局,你想用这个实现什么?
  • 我不明白如何让实际的实现者确定animate() 是否是@IBAction 是否容易出错。请阅读我的回答,如果您有任何问题,请告诉我。我认为这可能是一个 XY 问题,但是如果您真正了解 @IBAction 是什么以及做什么,那么在协议中创建一个方法 @IBAction 根本就没有任何意义。
  • 我已经阅读了您的评论,并且我了解 IBAction 的作用,但它仍然有意义。查看我的编辑:)
  • 有什么解决办法吗?我理解这个问题,遇到了同样的事情。 Storyboard IBOutlets 需要在协议中“可检测”。你不能那样做真的很糟糕。希望它只看方法签名。

标签: ios swift protocols ibaction swift-protocols


【解决方案1】:

在协议中使用@IBAction 没有任何意义@IBAction 只不过是一个关键字,当您从 Interface Builder 控制 + 拖动到实际源代码时,Interface Builder 有一个钩子。

这只是对@IBAction 的实际含义和作用的简单误解。

  1. 方法不一定要标记为@IBAction 才能成为 UI 元素操作的目标。您可以使用 UI 元素拥有的 addTarget 方法集以编程方式将任何方法连接到任何操作。该方法不必标记为 @IBAction 即可执行此操作。

  2. 无论协议是否将方法定义为@IBAction,符合协议的类都可以添加它(并且仍然符合协议。

    protocol FooProtocol {
        func doSomething()
    }
    
    class ViewControllerA: UIViewController, FooProtocol {
        @IBAction func doSomething() {
            // do something
        }
    } 
    
    class ViewControllerB: UIViewController, FooProtocol {
        func doSomething() {
            // do something
        }
    }
    

    这两个视图控制器子类都符合该协议,并且如果您打算从界面构建器中连接一个动作,如果您打算使用@IBAction,则是必需的!


最终,无论您尝试做什么,如果您认为在您的协议中需要 @IBAction,我认为您采取了错误的方法。如果不了解您实际在做什么的更多细节,很难说出正确的方法是什么,但 @IBAction 属于协议是毫无意义的。

对我来说,您的协议强制执行的方法似乎根本不应该与@IBAction 方法相关联。相反,无论用户交互应该触发动画,都应该依次调用animate 方法。例如,如果我们不讨论协议,我的建议是这样的设置:

class ViewController: UIViewController {
    @IBAction func buttonThatStartsAnimation {
        self.animate()
    }

    func animate {
        // code that does all the animation
    }
}

因此,对于协议,我们应该在实际启动动画代码的方法(在协议的情况下,这显然是其他一些外部类)和 animate 方法之间采取相同的职责分离应该只处理相关动画。

重要的是,作为一般规则,您不应直接从定义它们的类外部直接引用您的 @IBAction 方法或 @IBOutlet 变量。

【讨论】:

  • 感谢您花时间回答。但是,我必须不同意您的某些 cmets。我完全知道IBAction 是什么意思,但这并没有改变问题。检查我的编辑。
  • 顺便说一句,你说“任何用户交互应该触发动画,应该反过来调用 animate 方法。”。这正是我想要的,但是通过在 VC 中“粘合”它,您实际上是从您的视图层到您的控制器层并返回到视图层,当您可以将它全部保存在 IB 的视图层中时(它应该在哪里,因为它只是一个 UI 更改)。 View Controller 总是在 Cocoa 中被滥用,这是 IMO 的一个明显示例。
  • 恕我直言,我完全不同意。在协议中声明某些功能集非常有意义。例如,在您的应用程序中,一半的ViewControllers 可能具有在多个不同位置显示用户配置文件控制器的功能,将@IBAction func actionPresentProfile(sender:) 放入协议中可能非常有用。
  • 我同意@superarts.org。我遇到了一些视图控制器符合协议的情况,我希望它们都可以使用某些操作来连接到各自的故事板中。我正在使用 where 子句在协议的扩展中定义方法的默认版本。能够将其公开给 IB 将非常有帮助,因为这是所有视图控制器都应该能够执行的操作。
【解决方案2】:

我完全同意 OP,尽管在 Swift 3.1 之前,您不能在协议中真正声明任何内容为 @IBOutlet@IBAction@objc 等。作为一种解决方法,我选择基于pod 'ActionKit' 构建一些东西并写了类似的东西:

protocol RequiresAnimation {
    var animateButton: UIButton! { get }
    func enableAnimateButton()
    func actionAnimate()
}

extension RequiresAnimation where Self: UIViewController {
    func enableAnimateButton() {
        animateButton.addControlEvent(.touchUpInside) {
            self.actionAnimate()
        }   
    }
    func actionAnimate() {
        //  animate here
    }
}

并制作您的视图控制器:

class MyViewController: UIViewController, RequiresAnimation {
    @IBOutlet var animateButton: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        enableAnimateButton()
    }
}

我希望有更简单的方法,但到目前为止,您需要手动执行以下两件事:将您的按钮声明为 @IBOutlet 并调用设置函数。我们之所以需要import ActionKit,是因为我们不能在协议扩展中addTarget

【讨论】:

  • 这实际上是不是更喜欢简单地不为按钮制作插座,而是将按钮挂钩到视图控制器中的操作,并在该操作中简单地从协议中调用actionAnimate 方法?我看不出这种方法有什么可取之处?
  • 没有什么总是更好的,这取决于您的用例。可以肯定的是,您的方法更灵活,但在我的情况下,我想指定我希望 MyViewController “拥有一个除了执行特定动画之外什么都不做的按钮”。您的方法引入了隐藏的依赖关系,例如,如果您放置诸如呈现另一个视图控制器之类的东西,您将不知道它是否会在调用 animate 之前或之后破坏任何东西。但另一方面,如果您确实想在animate 之前或之后做某事,我的方法将很难扩展。
猜你喜欢
  • 1970-01-01
  • 2019-02-20
  • 1970-01-01
  • 2014-11-24
  • 1970-01-01
  • 2020-06-04
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多