0

Not sure if my thinking here is correct but I have similar animations I use throughout my iOS project and I would like to condense it to 1 file and reuse wherever I want.

A brief example. In my animations file I have a scale animation

Animations.swift

class Animations {

    class func scaleSmall(_ view: UIView) {
        let scaleAnim = POPBasicAnimation(propertyNamed: kPOPLayerScaleXY)
        scaleAnim?.toValue = NSValue(cgSize: CGSize(width: 0.9, height: 0.9))
        view.layer.pop_add(scaleAnim, forKey: "scaleSmallAnim")
    }

}

Here I have one of my many swift files in my View folder and I would like to add that animation to the button

Button.swift

class Button: UIButton {

    override func awakeFromNib() {
        super.awakeFromNib()

        self.addTarget(self, action: #selector(Animations.scaleSmall(_:)), for: .touchDown)
    }

}

I thought I would be able to reference the animation from an additional file however everytime I do it this way I get the same error

Argument of '#selector' refers to instance method 'scaleSmall' that is not exposed to Objective-C

Am I referencing this function wrong?

user934902
  • 1,184
  • 3
  • 15
  • 33
  • You are doing it right. Its just that Swift classes are not entirely compatible with Obj-C, and `UIKit` (i.e. `UIButton`) is in Obj-C world. You can try either inheriting from `NSObject` by your `Animations` class or marking your method as `@objc`. – Losiowaty Jan 11 '17 at 12:27
  • The confusing thing for me is if I have the animation inside my Button class I can access it with #selector(Button.scaleSmall) and it works correctly. Soon as I bring it out of the file I cant get it correctly – user934902 Jan 11 '17 at 12:30

2 Answers2

1

try changing class func scaleSmall(_ view: UIView) {

to

 @objc class func scaleSmall(view: UIView) {
  • Trying to keep a clean swift file and not mix up objc if I dont have to – user934902 Jan 11 '17 at 12:31
  • it is not really mixing objc with swift code, @objc before the function is just a flag for the compiler. – Kostas Tsoleridis Jan 11 '17 at 12:39
  • So it gets rid of my error but the next issue I have is the App failing 'unrecognized selector sent to instance'. Doing some investigating into it now – user934902 Jan 11 '17 at 12:43
  • That may be due to the fact, that you are passing `self` as a target. This means that the button is actually trying to call the method on `self`. I've actually never tried to use static methods as selectors for buttons. Maybe doing it via a singleton of the `Animation` class would work. – Losiowaty Jan 11 '17 at 12:45
1

I've confirmed my comment, so I'm posting an answer. Methods for UIButton need to be bridged to Obj-C. That's what @Kostas Tsoleridis suggests with his answer as well - it is not mixing two languages in one file, you are just marking the method for the compiler. Other solution would be to inherit from NSObject by your Animations class.

Now, as your confusion mentioned in a comment - it worked, because your Button class inherits from UIButton which is both from Obj-C world, and also inherits from NSObject down the chain.

To also address the issue mentioned in a comment under @Kostas Tsoleridis answer (and to be honest I should have thought about it before) - you can't pass self as a target and use a method from another class (even a static one). To solve this, you can use a singleton instance of your Animations class, something like this :

class Animations {
    static let sharedInstance = Animations()
    @objc class func scaleSmall(_ view: UIView) {
        // your code
    }
}

let button = UIButton()
button.addTarget(Animations.sharedInstance, action: #selector(Animations.scaleSmall(_:)), for: .touchDown)
Losiowaty
  • 7,911
  • 2
  • 32
  • 47