Access Asset Catalog pathForResource
Asked Answered
S

6

70

It appears that:

[[NSBundle mainBundle] pathForResource:@"name" ofType:@"png"];

Does not return anything for assets that are inside the Images.xcassets asset catalog. I also tried:

[[NSBundle mainBundle] pathForResource:@"name" ofType:@"png" inDirectory:@"Images"];

[[NSBundle mainBundle] pathForResource:@"name" ofType:@"png" inDirectory:@"Images.xcassets"];

But neither of those worked either.

Has anyone had success retrieving paths to assets in the catalog?

Supple answered 23/9, 2013 at 20:35 Comment(2)
I also need the actual paths to the images because I don't want to use imageNamed (broken caching) and it turns out that Asset Catalogs are also broken with no programatic access.Labarum
@Keab42 No, and I just ran into another case where i would like it! :)Supple
G
27

Same problem, but I don't think we could access it via pathForResource:, except below:

UIImage* image = [UIImage imageNamed:@"name-in-asset-catalog"];

It has been clearly documented:

Each set in an asset catalog has a name. You can use that name to programmatically load any individual image contained in the set. To load an image, call the UIImage:imageNamed: method, passing the name of the set that contains the image.

I found that in the package of complied app, a file named "Assets.car" came out, and I think it's the entire images sets in my project, and should have been zipped or something.

Refer to Apple's documentation:

Xcode 5 provides different functionality for asset catalogs depending on the deployment target for your project:

  • For all projects, individual images can be loaded using set names.
  • For projects with a deployment target of iOS 7, Xcode compiles your asset catalogs into a runtime binary file format that reduces the download time for your app.

So that's it.

I'm also looking for a way that doesn't use imageNamed:, I don't want runtime to cache my images.

Gast answered 25/9, 2013 at 9:34 Comment(4)
I have the same problem about caching images here #19919613. It appears like imageNamed: caching does not work as expected when using Asset Catalogs, because the memory is never freed up by the system, not even on memory warnings.Bennett
I can verify this. Memory is not freed up. This part of iOS is pretty broken.Labarum
What if there is a folder in assets and i need only photos in this folder? any help please ?Spumescent
Same issue here. iOS 11.. XCode 9.1Bab
P
13

I wanted to access some vector assets for creating UNNotificationAttachment with local resources so I came up with this helper class. It basically just gets image from assets, saves its data to disk and return file URL. I hope that helps someone.

import UIKit

class AssetExtractor {

    static func createLocalUrl(forImageNamed name: String) -> URL? {

        let fileManager = FileManager.default
        let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
        let url = cacheDirectory.appendingPathComponent("\(name).png")

        guard fileManager.fileExists(atPath: url.path) else {
            guard
                let image = UIImage(named: name),
                let data = UIImagePNGRepresentation(image)
            else { return nil }

            fileManager.createFile(atPath: url.path, contents: data, attributes: nil)
            return url
        }

        return url
    }

}
Performative answered 28/9, 2016 at 13:28 Comment(2)
I had the same issue and this worked great. Thanks for sharing.Lingerie
Nice! I made it a static function on the UIImage class.Enswathe
T
11
[UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@.png", [[NSBundle mainBundle] resourcePath], imgName]];

imgName: the name of Image set, not caring the true name of the image in Image set.

Tutu answered 24/9, 2014 at 3:27 Comment(2)
Doesnt work with image in any other bundle apart from main bundle.Cystine
Thanks, mate. The way you did it is a good substitution for + (UIImage *)imageNamed:(NSString *)name inBundle:(NSBundle *)bundle compatibleWithTraitCollection:(UITraitCollection *)traitCollection. It was introduced with iOS 8.0 and I need to cover 7.x.x as well.Citrin
A
2

Try using the image name as determined in the "Compile asset catalog" step. You'll find this in the build output. Be sure to expand the transcript - you should see something like this:

/* com.apple.actool.compilation-results */
/path/to/Debug-iphonesimulator/Your.app/[email protected]
/path/to/Debug-iphonesimulator/Your.app/[email protected]
/path/to/Debug-iphonesimulator/Your.app/[email protected]
/path/to/Debug-iphonesimulator/Your.app/[email protected]
/path/to/Debug-iphonesimulator/Your.app/LaunchImage-700-Landscape~ipad.png
/path/to/Debug-iphonesimulator/Your.app/LaunchImage-700-Landscape@2x~ipad.png

Tested using +[UIImage imageNamed:] with Xcode 6.1.1.

Albuminate answered 7/12, 2014 at 17:21 Comment(0)
O
0

For launch images and application icons in particular, the image paths are available via the UILaunchImages and CFBundleIcons Info.plist keys respectively. It appears those images are generated individually and the Info.plist values updated during the app build when using asset catalogs (and if not, those keys are written to directly by the Xcode UI). You might have to write some code to figure out the correct sub-dictionary to use, but the images are available separately and you can avoid using imageNamed in that case.

Opinionated answered 1/4, 2015 at 16:41 Comment(0)
B
-3

Although there is a Contents.json file (see below) associated with each item in xcassets that contains the filenames of that item's images, it does not appear to be accessible at the filesystem level. All images are placed in a single filesystem folder upon compilation. The json file is auto-generated and should not be edited, but it does provide a template for a workaround.

Name each file with a consistent suffix to match the corresponding idiom, scale and subtype appearing in the json file. This requires manual work by selecting the 'Show in Finder' option for each asset and renaming each file, but once completed, you can then use pathForResource in combination with a function to add the appropriate suffix to the base asset name to retrieve the appropriately-sized image.

Sample code to retrieve path:

NSString *imageName = @"Add to Cart";
NSString *imageSuffix = [self someMethodThatDeterminesSuffix]; // Example: "-iphone-2x-retina4"
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@%@", imageName, imageSuffix] ofType:@"png"];

Example Contents.json file for the "Add to Cart" image asset:

{
  "images" : [
    {
      "idiom" : "iphone",
      "scale" : "1x",
      "filename" : "Add to Cart-iphone-1x.png"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "filename" : "Add to Cart-iphone-2x.png"
    },
    {
      "idiom" : "iphone",
      "filename" : "Add to Cart-iphone-2x-retina4.png",
      "subtype" : "retina4",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}
Bobinette answered 27/3, 2014 at 16:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.