I am creating a Swift project and I want to define a specific protocol that enforces other components to implement a animate
method:
protocol AnimatableBehavior {
@IBAction func animate()
}
The problem is I want this method to be an IBAction
, but I get this error from XCode:
Only instance methods can be declared 'IBAction'
My question is, how would you implement such a thing?
I have considered:
@IBAction
, but then I need to remember adding it in every class that implements. Not very elegant and error prone.Any other ideas?
EDIT: Response to comments below.
The idea of the IBAction
on the protocol is because in the project there will be many different devs implementing small UI components, all of which have the animate method. The components can be added programatically or by Interface Builder and it is very convenient that they are always IBAction
because I plan to compose them from IB files to simplify the View Controllers to the maximum extent (and this is clearly a View only task).
Therefore, the solution proposed below of adding a method in the controller that just calls the animate
of the component is not good because it is redundant code and makes your Controller more dependent on your View.
The idea of letting the dev to remember adding the IBAction
keyword on the method is workable, but as I said it is error prone (and by that I mean that there will be some forgetting about it), and I want to make sure that this is always accessible from IB. It also adds extra cognitive load, because I will need to document this lack of IBAction
on the protocol and request the implementor to add it manually.
I know is not the common way of working in iOS and UIKit
, but that was why I posted the question, maybe someone has an alternative idea.
The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. But there would be a time when you want to restrict protocols to be adopted by a specific class. In Swift 5, you can do just that.
Protocol is a set of methods (either optional or required) that would be implemented by the class which conforms to that protocol. While, delegate is the reference to that class which conforms to that protocol and will adhere to implement methods defined in protocol.
Protocols provide a blueprint for Methods, properties and other requirements functionality. It is just described as a methods or properties skeleton instead of implementation. Methods and properties implementation can further be done by defining classes, functions and enumerations.
Protocols are used to define a “blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.” Swift checks for protocol conformity issues at compile-time, allowing developers to discover some fatal bugs in the code even before running the program.
It doesn't make any sense to have an @IBAction
in a protocol. @IBAction
is nothing more than a keyword for Interface Builder to have a hook when you're control+dragging from Interface Builder to your actual source code.
This is just a simple misunderstanding of what @IBAction
actually is and does.
A method does not have to be marked as @IBAction
in order for it to be the target of a UI element's actions. You programmatically hook up any method to any action using the addTarget
set of methods that UI elements have. The method does not have to be marked as an @IBAction
to do this.
Regardless of whether or not a protocol defines a method as @IBAction
, the class conforming to the protocol can add it (and still be conforming to the protocol.
protocol FooProtocol {
func doSomething()
}
class ViewControllerA: UIViewController, FooProtocol {
@IBAction func doSomething() {
// do something
}
}
class ViewControllerB: UIViewController, FooProtocol {
func doSomething() {
// do something
}
}
Both of these view controller subclasses conform to the protocol, and having @IBAction
there is ONLY necessary if you intend to hook up an action from interface builder!
Ultimately, whatever you're trying to do, if you think an @IBAction
is necessary in your protocol, I think you're taking the wrong approach to something. It's hard to say what the right approach would be without knowing more details about what you're actually doing, but it never makes sense for @IBAction
to belong in a protocol.
To me, it seems like the methods your protocol enforces shouldn't at all be tied to @IBAction
methods. Instead, whatever user interaction should trigger the animation, should in turn call the animate
method. For example, if we weren't talking about the protocol, my recommendation would be this sort of set up:
class ViewController: UIViewController {
@IBAction func buttonThatStartsAnimation {
self.animate()
}
func animate {
// code that does all the animation
}
}
So, with the protocol, we should take the same seperation of duties between the method that's actually initiating the animation code (which in the case of protocols, this is obviously some other outside class), and the animate
method should only ever handle doing the relevant animations.
Importantly, just as a general rule, you shouldn't be directly referring to your @IBAction
methods or your @IBOutlet
variables directly from outside the class which defines them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With