Daniel
Daniel

Reputation: 9040

Load image from iOS 8 framework

I'm trying to load an image from an iOS 8 framework that I'm writing (in Swift). I'm using Xcode 6 Beta 6

This code does not work (i.e. load image) if the image is stored in my framework's Images.xcassets:

let image = UIImage(named: "Background.png")

If the image is stored in an Images.xcassets of a host application (that uses the framework), then the image is loaded properly (from code inside the framework).

I can see that the framework's Images.xcassets is included in the Copy Bundle Resources phase.

I'm also using a storyboard file as a resource in the framework; and this loads properly.

I've tried renaming the Images.xcassets of the framework to avoid some kind of naming collision with the host application, but this doesn't work either.

Upvotes: 33

Views: 28003

Answers (5)

Daniel
Daniel

Reputation: 9040

While @Renatus answer is perfectly valid and addresses the core issue (bundle for framework needs to be specified), I wanted to post the solution I went with since it's slightly more direct:

Swift 3.0/4.0/5.0

let image = UIImage(named: "YourImage", in: Bundle(for: YOURFRAMEWORKCLASS.self), compatibleWith: nil)

Alternatively, you can use this pattern for non-class, aka non-"static", functions:

let image = UIImage(named: "YourImage", in: Bundle(for: type(of: self)), compatibleWith: nil)

or this pattern for class functions:

let image = UIImage(named: "YourImage", in: Bundle(for: self), compatibleWith: nil)

These alternatives are better for cutting and pasting.

Upvotes: 72

Brian
Brian

Reputation: 31282

Another option is assigning the bundle identifier, which makes more sense than assigning class when it comes to readability.

In Swift 3:

UIImage(named: "MyImage", in: Bundle(identifier: "bundleIdentifier"), compatibleWith: nil)

Upvotes: 3

NatashaTheRobot
NatashaTheRobot

Reputation: 6949

In Swift 3.0:

let currentBundle = Bundle(for: YOURCLASS.self)
guard let path = currentBundle.path(forResource: imageName, ofType: "jpg") else {  return defaultImage }
return UIImage(contentsOfFile: path) ?? defaultImage

Upvotes: 3

Sakiboy
Sakiboy

Reputation: 7459

The accepted answer didn't work for me. Here's a fool proof way for loading an image embedded in a dynamic framework:

    var bundle = NSBundle(forClass: self.classForCoder)

    if let bundlePath = NSBundle(forClass: self.classForCoder).resourcePath?.stringByAppendingString("/MYLIB.bundle"), resourceBundle = NSBundle(path: bundlePath) {
        bundle = resourceBundle
    }

    let image = UIImage(named: "my-embedded-image", inBundle: bundle, compatibleWithTraitCollection: nil)

Upvotes: 0

Renatus
Renatus

Reputation: 1133

UIImage(named: "Background.png") calls NSBundle.mainBundle() in the internals. So, your code is trying to find resource in your host app's bundle, not in the frameworks bundle. To load UIImage from your framework's bundle use this snippet:

let frameworkBundle = NSBundle(forClass: YOURFRAMEWORKCLASS.self)
let imagePath = frameworkBundle.pathForResource("yourImage.png", ofType: "")
if imagePath != nil {
  result = UIImage(contentsOfFile: imagePath!)
}

Edited: added explanation (thx to milz)

Upvotes: 13

Related Questions