我正在创建一个Swift项目,我想定义一个特定的协议,强制其他组件实现animate
方法:
protocol AnimatableBehavior {
@IBAction func animate()
}
问题是我希望这个方法是IBAction
,但我从XCode:中得到了这个错误
只有实例方法可以声明为"IBAction">
我的问题是,您将如何实现这样的东西
我考虑过:
- 删除
@IBAction
,但我需要记住在实现的每个类中添加它。不太优雅,容易出错 - 创建一个基类而不是协议,但我强制所有组件将我的基类子类化,而不是他们自己选择的基类,所以这不是一个有效的选项
还有其他想法吗?
编辑:对以下评论的回应
IBAction
在协议上的想法是因为在项目中会有许多不同的开发人员实现小型UI组件,所有这些组件都有animate方法。组件可以通过程序添加,也可以通过接口生成器添加,它们总是IBAction
非常方便,因为我计划从IB文件中组合它们,以最大限度地简化视图控制器(这显然是一项仅限视图的任务)。
因此,下面提出的在控制器中添加一个只调用组件的animate
的方法的解决方案是不好的,因为它是冗余代码,会使控制器更加依赖于视图。
让开发人员记住在方法上添加IBAction
关键字的想法是可行的,但正如我所说,这很容易出错(我的意思是,会有一些人忘记它),我想确保这总是可以从IB访问的。它还增加了额外的认知负荷,因为我需要记录协议上IBAction
的缺失,并请求实现者手动添加它。
我知道这不是iOS和UIKit
中常见的工作方式,但这就是我发布这个问题的原因,也许有人有其他想法。
在协议中使用@IBAction
没有任何意义。@IBAction
只不过是一个关键字,当您从Interface Builder控件+拖动到实际源代码时,Interface Builder会有一个钩子。
这只是对@IBAction
的实际含义和作用的一个简单误解。
-
方法不必标记为
@IBAction
,就可以成为UI元素操作的目标。您可以使用UI元素所具有的addTarget
方法集以编程方式将任何方法连接到任何操作。不必将该方法标记为@IBAction
即可执行此操作。 -
无论协议是否将方法定义为
@IBAction
,符合协议的类都可以添加它(并且仍然符合协议。protocol FooProtocol { func doSomething() } class ViewControllerA: UIViewController, FooProtocol { @IBAction func doSomething() { // do something } } class ViewControllerB: UIViewController, FooProtocol { func doSomething() { // do something } }
这两个视图控制器子类都符合协议,并且具有
@IBAction
,如果您打算从接口生成器挂接操作,则只有ONLY是必要的
最终,无论你想做什么,如果你认为@IBAction
在你的协议中是必要的,我认为你采取了错误的方法。如果不知道更多关于你实际在做什么的细节,很难说什么是正确的方法,但@IBAction
属于一个协议是没有意义的。
对我来说,您的协议强制执行的方法似乎根本不应该与@IBAction
方法绑定。相反,无论用户交互应该触发动画,都应该调用animate
方法。例如,如果我们不讨论协议,我的建议是这样设置:
class ViewController: UIViewController {
@IBAction func buttonThatStartsAnimation {
self.animate()
}
func animate {
// code that does all the animation
}
}
因此,对于协议,我们应该在实际启动动画代码的方法(在协议的情况下,这显然是其他外部类)和animate
方法之间采取相同的职责分离,应该只处理相关的动画。
重要的是,正如一般规则一样,您不应该直接从定义它们的类外部引用@IBAction
方法或@IBOutlet
变量。
我完全同意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
。