Mutaeb Alqahtani
Mutaeb Alqahtani

Reputation: 354

How to extend more than one class using Swift 4/5

I am trying to extend more than one class which are UIButton and UITextField. They both have the same function where they can wiggle when I call the function. I'm trying not to repeat my code more than once. I have been attempting to use protocal that I can extend the and write the function that I want and then extend them in my classes, but the problem is in my function I have to call self, but I will get an error since self can be only called on UITextField and UIButton.

Here is my code

import UIKit

extension UIButton {

    func wiggle() {
        let position = "position"
        let wiggleAnimation = CABasicAnimation(keyPath: position)
        wiggleAnimation.duration = 0.05
        wiggleAnimation.repeatCount = 5
        wiggleAnimation.autoreverses = true
        wiggleAnimation.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
        wiggleAnimation.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
        layer.add(wiggleAnimation, forKey: position)
    }

}

extension UITextField {

    func wiggle() {
        let position = "position"
        let wiggleAnimation = CABasicAnimation(keyPath: position)
        wiggleAnimation.duration = 0.05
        wiggleAnimation.repeatCount = 5
        wiggleAnimation.autoreverses = true
        wiggleAnimation.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
        wiggleAnimation.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
        layer.add(wiggleAnimation, forKey: position)
    }

}

Here is what I have tried to attempt but I get an error because I am calling self.

protocol Animations {
    func wiggle()
}

extension Animations {
    func wiggle() {
        let position = "position"
        let wiggleAnimation = CABasicAnimation(keyPath: position)
        wiggleAnimation.duration = 0.05
        wiggleAnimation.repeatCount = 5
        wiggleAnimation.autoreverses = true
        wiggleAnimation.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
        wiggleAnimation.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
        layer.add(wiggleAnimation, forKey: position)
    }
}

extension UIButton: Animations {}

extension UITextField: Animations {}

The errors that I receive are Value of type 'Self' has no member 'center' Value of type 'Self' has no member 'center' Use of unresolved identifier 'layer'

Upvotes: 1

Views: 182

Answers (2)

Teetz
Teetz

Reputation: 3785

Only UIView has a center property. Your protocol declaration should look like this for Swift 5+:

protocol Animations: UIView {
    func wiggle()
}

Care that only UIViews can conform to this protocol.

For Swift 4.x you have to use it like this:

protocol Animations {
    func wiggle()
}

extension Animations where Self: UIView {
   func wiggle() {
        let position = "position"
        let wiggleAnimation = CABasicAnimation(keyPath: position)
        wiggleAnimation.duration = 0.05
        wiggleAnimation.repeatCount = 5
        wiggleAnimation.autoreverses = true
        wiggleAnimation.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
        wiggleAnimation.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
        layer.add(wiggleAnimation, forKey: position)
    }
}

Upvotes: 6

Jon Jones
Jon Jones

Reputation: 141

The previous answer given does not actually compile using Swift 4.x, as protocols could not directly inherit from classes before Swift 5.

The correct way to write this in Swift 4.x involves providing a default implementation for your wiggle() function only when the conforming type inherits from UIView. The correct way to write this is:

protocol Animations {
    func wiggle()
}
extension Animations where Self: UIView {
   func wiggle() {
        let position = "position"
        let wiggleAnimation = CABasicAnimation(keyPath: position)
        wiggleAnimation.duration = 0.05
        wiggleAnimation.repeatCount = 5
        wiggleAnimation.autoreverses = true
        wiggleAnimation.fromValue = CGPoint(x: self.center.x - 4.0, y: self.center.y)
        wiggleAnimation.toValue = CGPoint(x: self.center.x + 4.0, y: self.center.y)
        layer.add(wiggleAnimation, forKey: position)
    }
}

Upvotes: 4

Related Questions