6

I was on my way to override UIImage class method init(named:). My goal is to be able to retrieve the file name of an image.

The code would look like this:

class UIImageWithFileName: UIImage {

    let fileName: String

    override init(named:String){
        super.init(named)
    fileName = named
    }
}

This code seems to be impossible for following reasons.

I created a subclass of UIImage, started typing init... and to my surprise there was no init(named:) method to override. Here is a complete list of init methods:

enter image description here

enter image description here

How does one override UIImage init(named:)

potato
  • 4,479
  • 7
  • 42
  • 99

3 Answers3

3

Turns out that "init(named:" is explicitly not inheritable.

If you look at the UIImage definition, you'll see this:

public class UIImage : NSObject, NSSecureCoding, NSCoding {

    public /*not inherited*/ init?(named name: String) // load from main bundle

    @available(iOS 8.0, *)
    public /*not inherited*/ init?(named name: String, inBundle bundle: NSBundle?, compatibleWithTraitCollection traitCollection: UITraitCollection?)

Which means you need to resolve or open the file yourself and pass in the raw data into the super class.

Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • 1
    And how do I use Assets Catalog when opening images from file? – potato Aug 29 '15 at 10:24
  • 1
    There's a bit more information [in this related question](http://stackoverflow.com/questions/17144701/objective-c-class-inheritance-with-factory-methods). The "`named`" methods are factory methods in Objective-C and so I'm guessing these corresponding init methods in swift are meant for convenience. – Michael Dautermann Aug 29 '15 at 10:25
  • 1
    To get at the appropriate path in the Assets Catalog, you might be able to use one of the answers [in this related question](http://stackoverflow.com/questions/18968352/access-asset-catalog-pathforresource) – Michael Dautermann Aug 29 '15 at 10:30
  • 1
    Came up with a solution. Take a peak and let me know your thoughts – potato Aug 29 '15 at 11:04
1

This is a way around:

class UIImageWithName: UIImage {

var fileName: String!

func ofFile(named:String) -> UIImageWithName?{
    let cgImage = UIImage(named: named)?.CGImage
    if let validName = cgImage{
        let image = UIImageWithName(CGImage: validName)
        image.fileName = named
        return image
    }
    return nil
}

}

Not sure if it is efficient or in any way disputable. Feedback please

potato
  • 4,479
  • 7
  • 42
  • 99
0

I've found this to work nicely.

class MyImage: UIImage {

    convenience init?(named name: String) {
        guard let image = UIImage(named: name),
            let cgImage = image.cgImage else {
                return nil
        }
        self.init(cgImage: cgImage)
    }

}
MightyKip
  • 9
  • 1
  • 1
    This is probably far from safe. Does not take even image orientation into account. – Sulthan Nov 07 '19 at 15:36
  • This doesn't work, it doesn't take into account scale, orientation and whatever else it normally does. – Koen. Jul 17 '20 at 12:11