Swift协议中IBActions的更好策略是什么



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

protocol AnimatableBehavior {
@IBAction func animate()
}

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

只有实例方法可以声明为"IBAction">

我的问题是,您将如何实现这样的东西

我考虑过:

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

还有其他想法吗?


编辑:对以下评论的回应

IBAction在协议上的想法是因为在项目中会有许多不同的开发人员实现小型UI组件,所有这些组件都有animate方法。组件可以通过程序添加,也可以通过接口生成器添加,它们总是IBAction非常方便,因为我计划从IB文件中组合它们,以最大限度地简化视图控制器(这显然是一项仅限视图的任务)。

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

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

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

在协议中使用@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,如果您打算从接口生成器挂接操作,则只有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

最新更新