42

I have an extension:

extension UILabel {
    func animateHidden(flag: Bool) {
        self.hidden = flag
    }
}

I need to make the same one for UIImageView but I don't want to copy that whole code. Is it possible to make an extension for multiple classes?

Thanks.

Danny
  • 3,975
  • 4
  • 22
  • 35
  • 4
    Can you just use `extension UIView`? It will depend on the implementation of `animateHidden` – Kevin Jul 19 '16 at 16:43
  • @Kevin all code of `animateHidden` will be useful for both classes `UILabel`, `UIImageView` – Danny Jul 19 '16 at 16:45

2 Answers2

87

You could make a protocol and extend it.

Something like:

protocol Animations {
    func animateHidden(flag: Bool)
}

extension Animations {
    func animateHidden(flag: Bool) {
        // some code
    }
}

extension UILabel: Animations {}

extension UIImageView: Animations {}

Your method will be available for the extended classes:

let l = UILabel()
l.animateHidden(false)

let i = UIImageView()
i.animateHidden(false)

In a comment, you've asked: "in this case how to call self for UILabel and UIImageView in animateHidden function?". You do that by constraining the extension.

Example with a where clause:

extension Animations where Self: UIView {
    func animateHidden(flag: Bool) {
        self.hidden = flag
    }
}

Thanks to @Knight0fDragon for his excellent comment about the where clause.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • In this case how to call `self` for `UILabel` and `UIImageView` in `animateHidden` function? – Danny Jul 19 '16 at 16:49
  • 4
    @Danny, add a where clause. `extension Animations where Self: UIView` – Knight0fDragon Jul 19 '16 at 16:57
  • 1
    But then won't you have to do `extension Animations where Self: UIImageView` and reproduce the same implementation for `animateHidden(flag:)` anyway? That sort of destroys the purpose of reusing the implementation code. – HuaTham Nov 23 '16 at 03:28
  • @HuaTham No you don't have to do this, because UIImageView is extended with the Animations extension, so by composition it includes animateHidden, that's the whole point indeed. :) – Eric Aya Nov 23 '16 at 14:53
  • the example with the where clause is nice. I was going to do it with a conditional cast, but pushing this back to compile-time is nicer/better. @Knight0fDragon big ups! – Dan Rosenstark Apr 15 '23 at 20:39
10

Best Method to Extend UILabel & UIImageView Together

Swift 4.1 / Xcode 9.4

A much better way to do this in your case would be to just extend UIView. This works because both UILabel and UIImageView both inherit from UIView.


Extension

extension UIView {
    func animateHidden(flag: Bool) {
        self.hidden = flag
    }
}

Usage of animateHidden(flag: Bool) Extension

Declaration of label and imageView:

label = UILabel()
imageView = UIImageView()

Actual usage of extension

label.animateHidden(flag: true)
imageView.animateHidden(flag: false)

Bonus - Other Classes that many UI Components Conform to

If you would like to have your extension be able to conform with many different types of UI components, there are 4 types that a very large amount of UI components conform to:

  1. CVarArg
  2. Equatable
  3. Hashable
  4. NSCoding

Some of the many UI components include:

  • UILabel: CVarArg, Equatable, Hashable, NSCoding

  • UITextField: CVarArg, Equatable, Hashable, NSCoding

  • UITableViewCell: CVarArg, Equatable, Hashable, NSCoding

  • UITextView: CVarArg, Equatable, Hashable, NSCoding

  • UITableView: CVarArg, Equatable, Hashable, NSCoding

  • UIImage: CVarArg, Equatable, Hashable, NSCoding

  • UIPickerView: CVarArg, Equatable, Hashable, NSCoding

  • UIView: CVarArg, Equatable, Hashable, NSCoding

  • UIImageView: CVarArg, Equatable, Hashable, NSCoding

  • UINavigationBar: CVarArg, Equatable, Hashable, NSCoding

  • UIButton: CVarArg, Equatable, Hashable, NSCoding

  • UIBarButtonItem: CVarArg, Equatable, Hashable, NSCoding

  • UIStackView: CVarArg, Equatable, Hashable, NSCoding

  • UIToolbar: CVarArg, Equatable, Hashable, NSCoding

  • UITabBar: CVarArg, Equatable, Hashable, NSCoding

  • UITabBarItem: CVarArg, Equatable, Hashable, NSCoding

  • UIScrollView: CVarArg, Equatable, Hashable, NSCoding

  • UISplitViewController: CVarArg, Equatable, Hashable, NSCoding

  • UIViewController: CVarArg, Equatable, Hashable, NSCoding

  • UIScreen: CVarArg

  • UISwitch: CVarArg, Equatable, Hashable, NSCoding

  • UISlider: CVarArg, Equatable, Hashable, NSCoding

  • UIAlertAction: CVarArg

  • UIAlertController: CVarArg, Equatable, Hashable, NSCoding

  • UIImageAsset: CVarArg, Equatable, Hashable, NSCoding

  • UIDatePicker: CVarArg, Equatable, Hashable, NSCoding

  • UINib: CVarArg

  • UIResponder: CVarArg

  • UIWindow: CVarArg, Equatable, Hashable, NSCoding

  • UIRegion: CVarArg, Equatable, Hashable, NSCoding

  • UIControl: CVarArg, Equatable, Hashable, NSCoding

  • UIBezierPath: CVarArg, Equatable, Hashable, NSCoding

  • UIVisualEffect: CVarArg, Equatable, Hashable, NSCoding

  • UISearchBar: CVarArg, Equatable, Hashable, NSCoding

  • UIMenuItem: CVarArg

  • UIMenuController: CVarArg

  • UIStoryboard: CVarArg

  • And many more...


This means that by extending either CVarArg, Equatable, Hashable, or NSCoding, you can extend most (if every not every) UI component.



Well anyways, I hope this all helps you resolve your issue and if you have absolutely any questions, suggestions, etc., feel free to ask!



Noah Wilder
  • 1,656
  • 20
  • 38
  • 1
    Although your approach works well in this particular case I would not consider this programming style being a best practise for well structured code. Reasons for this are: 1) You don't want to have all UIViews being extended by the functions that you only need in a UILabel or UIImageView 2) With the way the accepted answer is implemented you are able to functionally separate your code into a swift file that is easier to find within your codebase since your extension will have an identifiable name. – Dennis Stücken Aug 31 '19 at 18:48
  • Hii, i have a quick question. i want to do like this extension UITableViewCell, UICollectionViewCell{ // Methods }. How can achieved this. Please help Thanks !! – Yogesh Patel Feb 27 '20 at 07:09
  • @YogeshPatel You can try to find a common subclass of both `UITableViewCell` and `UICollectionViewCell` that has all the functionality you need in your extension and just extend that . For example, extending `UIView` will do so for all of its subclasses like `UILabel`, `UITextField`, etc., but the extension method will be limited to using only methods defined on `UIView` and any of its superclasses, thus limiting what you can actually do. Ideally you'll be able to use the first common superclass between the two types. Note that if you do this it extends *all* subclasses of the extended class. – Noah Wilder Feb 27 '20 at 15:30
  • @YogeshPatel, if the common superclass does not incorporate the functionality you wish to use in your extension or if you would like it to extend `UITableViewCell` and `UICollectionViewCell` in exclusion of any unnecessary subclasses of their common superclass, then you should use a protocol. In the protocol, define the methods you need to access in your joint extension. Then conform `UITableViewCell` and `UICollectionViewCell` to the protocol individually. After they are conformed to it, you can extend the protocol. This also allows you to write functions that are generic over the protocol. – Noah Wilder Feb 27 '20 at 15:36
  • Hii thanks for your response. i did this thing using Protocol. Now it's working! – Yogesh Patel Feb 28 '20 at 13:41