如何在iOS的Cocoapod库中使用图像资源目录?

31

我有一个cocoapod库,其中包含2种格式的资源:

  • .storyboard文件
  • Xcode的asset catalog .xcassets(带图像)

我的podspec文件包含资源包的定义:

s.resource_bundle = {'SparkSetup' => ['Resources/**/*.{xcassets,storyboard}']}

我在pod项目中有一个单独的目标,通过使用这些文件和一个plist文件创建资源包。

问题是,当我在应用程序项目中使用pod时,我可以看到storyboard /xcassets 文件位于pod目标中,并且可以轻松访问和运行storyboard,但是在运行时找不到storyboard中引用的图像(到.xcassets文件),但在Interface Builder中显示正确。

我得到的错误是:

Could not load the "spinner" image referenced from a nib in the bundle with identifier "(null)"

我在产品目录中看到一个捆绑文件。 在Storyboard中实例化VCs时,我使用以下内容:

+(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;
}

似乎可以很好地找到故事板,但无法在同一捆绑包中的.xcassets中找到图像。

我哪里做错了? 我该如何从故事板/代码引用图像并能够将此UI pod集成到任何应用程序中?

谢谢!


遇到完全相同的问题。我注意到,如果我使用没有资源目录的图像,它能够正常工作。 - bencallis
1
@bencallis 显然这是唯一的解决方案。 XCode 似乎无法引用未加载到主应用程序捆绑包中的资源目录.. 唯一的解决方案是更改 podspec 中的 resource_bundle 行以包括 .png 而不是 .xcasset,并逐个引用每个图像。虽然不完美,但这是唯一的可行方式.. - mindbomb
1
我也遇到了这个问题。甚至无法从pod中的xcasset目录使用imageNamed加载图像。:( - Jure
12个回答

41

至少在Cocoapods的版本1.0.1中,支持图片资源目录。

在我的Swift 4.2代码中,我使用了:

public static var GoogleIcon: UIImage {
    let bundle = Bundle(for: self)
    log.msg("bundle: \(bundle)")
    return UIImage(named: "GoogleIcon", in: bundle, compatibleWith: nil)!
}

在我的 Pod 规范中,我使用了:

s.resources = "SMCoreLib/Assets/*.xcassets"

当我使用模拟器构建时,.car文件 确实 出现在框架中:

cd /Users/<snip>/SMCoreLib.framework 
ls
Assets.car  Info.plist  SMCoreLib

1
s.resources = "SMCoreLib/Assets/*.xcassets" 这个代码让我省了不少时间,谢谢 :) - Fadi Abuzant
是的,但这包括您应用程序目标内部的资源,而不是作为 Pod 框架的一部分。 - MichK
@MichK 我不太明白你的意思。据我所知,图像资源确实会保存在 Pod 的 bundle 中。因此需要像上面那样使用 Pod 的 NSBundle。 - Chris Prince
@ChrisPrince 我的意思是,通过使用s.resources,您可以将资源文件添加到主应用程序包中,但是当您使用s.resource_bundle时,您将资源文件添加到单独的包中,然后将其添加到主应用程序包中。在我看来,第二个选项提供了更好的分离,但是podspec的用户可以使用两种方法,这取决于项目规范和偏好。 - MichK
@MichK 这仅适用于静态集成Pods的情况。当使用use_frameworks!选项时,s.resources会执行正确的操作并添加标准框架资源。 - Rafael Nobre
显示剩余2条评论

11

好的,图像资源目录不支持通过 pods - 只需在您的 podspec 中包含包含图像文件的资源束:

s.subspec 'Resources' do |resources|
    resources.resource_bundle = {'MyBundle' => ['Resources/**/*.{png}']}

end

并且可以像这样安全地从 pod 访问图片:

+(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;
}

这解决了在 Cocoapod 中处理图像/资源的所有问题。


3
这种方法能否获取storyboard中的图片? - swalkner
在Storyboard中,您必须将MyBundleName.bundle/<imagename>.png添加到UIImageView图像ID中,它只能在运行时使用。无法预览。 - ale_stro

6

如果您正在使用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
}

MyClass 是指什么? - Vaisakh KP

5

Swift 3 版本

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()
}

4

只有这个对我有效(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")
  }
}

然后按照下面的方式使用它:
ImagesHelper.myImage

@Chris Prince的回答所述,别忘了更新你的podspec文件,例如:

s.resource_bundles = {
  'YourClass' => ['YourPodName/*/Assets.xcassets']
}

设置 s.resources 和 resource_bundles 应该完美运行。 - Zaporozhchenko Oleksandr

4
在你的 podspec 中要像这样做:
s.resources = 'RootFolder/**/*.{lproj,storyboard,xcdatamodeld,xib,xcassets,json}'

1

您可以通过使用其bundle id访问pods中的图像

enter image description here

 NSBundle * bundle = [NSBundle bundleWithIdentifier:@"bundle id of your pod"];
UIImage * image = [UIImage imageNamed:@"imageName" inBundle:bundle compatibleWithTraitCollection:nil];

1
我仍然无法让它工作,使用以上任何一种解决方案都不行。我的Pod中有一个Assets.xcassets文件。在我的Pod类中,我想从Assets中访问图像。指定如下:
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")
    }
}

但是,无论我在我的 Pod 类中做什么(不是在示例项目中)...就像这样。

 var centerImage: UIImage = ImagesHelper.download_icon.withRenderingMode(.alwaysTemplate) {
        didSet {
            updateImage()
        }
    }

我仍然在centerImage上得到一个nil


找到了!!在属性中不起作用,但在类的初始化方法中可以正常使用!所有的方法 :) - jr00n

0

在你的pod spec中将.xcassets文件添加到资源中,并使用以下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)
    }
 }

0

综合所有答案并使其易于复制粘贴到您的项目中:

class RandomClass: Any { } // Necessary for UIImage extension below
extension UIImage {
    convenience init?(fromPodAssetName name: String) {
        let bundle = Bundle(for: RandomClass.self)
        self.init(named: name, in: bundle, compatibleWith: nil)
    }
}

不要忘记将你的资源目录添加到你的 .podspec 文件中:

s.resource_bundles = {
    'Resources' => ['YourPodName/*.xcassets']
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接