I have a cocoapod library which contains assets in 2 formats:
- a .storyboard
- XCode asset catalog .xcassets (with images)
my podspec
file contains the definition for the resource bundle:
s.resource_bundle = {'SparkSetup' => ['Resources/**/*.{xcassets,storyboard}']}
and I have a separate target in the pod project to create a resource bundle by using those files + a plist file for that bundle.
thing is that when I use the pod in an app project - I can see the storyboard/xcassets files being in the pod target and I can access and run the storyboard easily but the images referenced in the storyboard (to the .xcassets file) are not found in run-time (but displayed correctly in IB).
Error I get is:
Could not load the "spinner" image referenced from a nib in the bundle with identifier "(null)"
I do see a bundle file in the products directory. To instanciate VCs in the storyboard I use:
+(NSBundle *)getResourcesBundle
{
NSBundle *bundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"SparkSetup" withExtension:@"bundle"]];
return bundle;
}
+(UIStoryboard *)getSetupStoryboard
{
UIStoryboard *setupStoryboard = [UIStoryboard storyboardWithName:@"setup" bundle:[SparkSetupMainController getResourcesBundle]];
return setupStoryboard;
}
which seems to work well for finding the storyboard, but not for finding images in the .xcassets in the same bundle.
What am I doing wrong? how can I reference images from this storyboard/from code and be able to integrate this UI pod into any app?
Thanks!
At least as of version 1.0.1 of Cocoapods, image asset catalogs are supported.
In my Swift code, I used:
public static var GoogleIcon:UIImage {
let bundle = NSBundle(forClass: self)
Log.msg("bundle: \(bundle)")
return UIImage(named: "GoogleIcon", inBundle: bundle,compatibleWithTraitCollection: nil)!
}
In my pod spec, I used:
s.resources = "SMCoreLib/Assets/*.xcassets"
When I build using the simulator, the .car file does show up in the Framework:
cd /Users/<snip>/SMCoreLib.framework
ls
Assets.car Info.plist SMCoreLib
Well, image asset catalogs are not supported via pods - just include a resource bundle that contains your image files in your podspec like so:
s.subspec 'Resources' do |resources|
resources.resource_bundle = {'MyBundle' => ['Resources/**/*.{png}']}
end
and access the images safely from the pod like that:
+(NSBundle *)getResourcesBundle
{
NSBundle *bundle = [NSBundle bundleWithURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"MyBundle" withExtension:@"bundle"]];
return bundle;
}
+(UIImage *)loadImageFromResourceBundle:(NSString *)imageName
{
NSBundle *bundle = [MyClass getResourcesBundle];
NSString *imageFileName = [NSString stringWithFormat:@"%@.png",imageName];
UIImage *image = [UIImage imageNamed:imageFileName inBundle:bundle compatibleWithTraitCollection:nil];
return image;
}
That solves all issues of handling images/resources within a cocoapod.
Swift 3 version
private func getImageFromBundle(name: String) -> UIImage {
let podBundle = Bundle(for: YourClass.self)
if let url = podBundle.url(forResource: "YourClass", withExtension: "bundle") {
let bundle = Bundle(url: url)
return UIImage(named: name, in: bundle, compatibleWith: nil)!
}
return UIImage()
}
if you are using swift.
class func loadImage(name: String) -> UIImage? {
let podBundle = NSBundle(forClass: MyClass.self)
if let url = podBundle.URLForResource("MyBundleName", withExtension: "bundle") {
let bundle = NSBundle(URL: url)
return UIImage(named: name, inBundle: bundle, compatibleWithTraitCollection: nil)
}
return nil
}
In you podspec do like this
s.resources = 'RootFolder/**/*.{lproj,storyboard,xcdatamodeld,xib,xcassets,json}'
Only this works for me (Swift 3 & Swift 4)
public struct ImagesHelper {
private static var podsBundle: Bundle {
let bundle = Bundle(for: YourClass.self)
return Bundle(url: bundle.url(forResource: "YourClass",
withExtension: "bundle")!)!
}
private static func imageFor(name imageName: String) -> UIImage {
return UIImage.init(named: imageName, in: podsBundle, compatibleWith: nil)!
}
public static var myImage: UIImage {
return imageFor(name: "imageName")
}
}
Then use it as below:
ImagesHelper.myImage
As in the @Chris Prince's answer, don't forget to update your podspec file like:
s.resource_bundles = {
'YourClass' => ['YourPodName/*/Assets.xcassets']
}
I can't still get it to work, with none of the solutions above. I have an Assets.xcassets file in my Pod.
And inside my Pod classes i would like to access images from the Assets
Specified like this:
s.resource_bundles = {
'SWDownloadPlayButton' => ['SWDownloadPlayButton/Assets/Assets.xcassets']
}
With this helper:
public struct ImagesHelper {
private static var podsBundle: Bundle {
let bundle = Bundle(for: SWDownloadPlayButton.self)
return Bundle(url: bundle.url(forResource: "SWDownloadPlayButton",
withExtension: "bundle")!)!
}
private static func imageFor(name imageName: String) -> UIImage {
return UIImage.init(named: imageName, in: podsBundle, compatibleWith: nil)!
}
public static var download_icon: UIImage {
return imageFor(name: "download_icon")
}
}
But whatever i do inside my Pod classes (not in the example project)...like this
var centerImage: UIImage = ImagesHelper.download_icon.withRenderingMode(.alwaysTemplate) {
didSet {
updateImage()
}
}
I still get a nil on the centerImage
Add your .xcassets
file within the resources in your pod spec, and use the following UIImage init:
extension UIImage {
convenience init?(podAssetName: String) {
let podBundle = Bundle(for: ConfettiView.self)
/// A given class within your Pod framework
guard let url = podBundle.url(forResource: "CryptoContribute",
withExtension: "bundle") else {
return nil
}
self.init(named: podAssetName,
in: Bundle(url: url),
compatibleWith: nil)
}
}
My own solution to the issue with CocoaPods. Instead of mucking with UIImage, I added a method to Bundle instead. The method attempts to locate an inner bundle of a given name, but falls back to the original bundle. This allows the code to work in both the application code using pods, as well as the pod code itself.
extension Bundle {
/**
Locate an inner Bundle generated from CocoaPod packaging.
- parameter name: the name of the inner resource bundle. This should match the "s.resource_bundle" key or
one of the "s.resoruce_bundles" keys from the podspec file that defines the CocoPod.
- returns: the resource Bundle or `self` if resource bundle was not found
*/
func podResource(name: String) -> Bundle {
guard let bundleUrl = self.url(forResource: name, withExtension: "bundle") else { return self }
return Bundle(url: bundleUrl) ?? self
}
}
Then in the viewDidLoad
method or wherever else you have your image setting code, put something like this, where "ClassFromPod" refers to a Swift class from a CocoaPod, and "ResourceName" is the name of the inner bundle within the pod (you should be able to see the bundles using Xcode project navigator in "Pods/Projects" -- embedded bundles have a LEGO icon and have a "bundle" suffix.)
super.viewDidLoad()
let bundle = Bundle(for: ClassFromPod.self).podResource(name: "ResourceName")
img1 = UIImage(named: "FancyBase", in: bundle, compatibleWith: nil)
img2 = UIImage(named: "FancyHandle", in: bundle, compatibleWith: nil)
As you can see, the UIImage API remains the same, only the bundle being used is different depending on what it finds at runtime.
来源:https://stackoverflow.com/questions/32577227/how-to-use-images-asset-catalog-in-cocoapod-library-for-ios