相信基本上所有的iOS开发同学针对于CocoaPods都不陌生。即便没有用过,也是久闻大名如雷贯耳。作为Objective-C和Swift中非常流行的依赖管理工具,它拥有超过10000个公有程序库,通过一份Podfile文件和pod install
命令就能帮助开发者方便的管理工程依赖。
随着组件化越来越火热,大家都开始介入研究。组件化就免不了通过CocoaPods创建自己的私有代码库。而针对于一些业务模块,不光有代码,还有一些其他的资源,如图片、音视频等等。那么,下面就介绍一下如何给Pod库添加资源文件。
在CocoaPods中,官方提供了两汇总资源文件的引用方式——resource
和resource_bundles
。
官方解释:一系列待复制到目标工程中的资源文件。
使用方法:
spec.resource = 'Resources/HockeySDK.bundle'
spec.resources = ['Images/*.png', 'Sounds/*']
(注意一个和多个的区别,多个在属性resource
后面加s
)
官方提示:如果构建静态库Pod时,强烈推荐使用resource_bundles
来指定资源包,因为可以避免名称冲突。
官方解释:允许定义当前Pod库的资源包的名称和文件。使用了hash,其中key表示包的名称,value是对应应包含的文件。key的命名至少应该包含Pod的名称,以最大限度的减少命名冲突的可能性。
使用方法:
spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
spec.resource_bundles = {
'MapBox' => ['MapView/Map/Resources/*.png'],
'OtherResources' => ['MapView/Map/OtherResources/*.png']
}
(注意一个和多个的区别,多个在属性resource_bundle
后面加s
)
接下来我们来测试一下,为什么resource会容易产生命名冲突。
创建一个私有pod,里面分别resource和resource_bundles字段。
Pod::Spec.new do |s|
s.name = "TestResource"
s.version = "0.0.1"
s.summary = "TestResource"
s.homepage = "http://www.sogou.com"
s.author = { "kingsword" => "[email protected]" }
s.platform = :ios, "8.0"
s.ios.deployment_target = "8.0"
s.source = { :svn => "http://[email protected]/svn/browser/seqa/qadev/src/iOS/ComponentLibraries/ComponentProjects/TestResource", :tag => "#{s.version}" }
s.resource = ['resource/*.png']
s.resource_bundle = { 'ResourceBundleA' => ['resource_bundle/*.png'] }
end
而这两个目录结构是这样的
之后,我们在另外一个工程中集成这个pod并构建,构建完成后查看.app
文件目录。
我们发现,通过resources
属性集成的资源文件都直接拷贝到了.app
的根目录,而通过resource_bundle
属性集成的资源文件,都被放到了ResourceBundleA.bundle
目录中,并且这个bundle目录被拷贝到了.app
的根目录。
此时,我们再在主工程中增加两个不同的图片,但图片的命名分别是[email protected]
、[email protected]
。
此时,我们再次构建,再查看目录。
我想结果大家可能也猜到了。因为主工程的同名文件也会被拷贝到.app
目录中,所以会覆盖我们之前集成pod中的同名文件,产生冲突。而针对于ResourceBundleA.bundle
中的资源文件,因为不属于同一目录结构,所以不会被覆盖。但是之前我们讲resource_bundles
时针对于key(bundle的命名)官方也建议我们能包含pod名,来尽可能的避免冲突。
上面说的直接集成图片资源文件,其实在项目中用的比较少了,大部分我们都是通过xcassets来集成图片资源文件,那么这两种方式针对集成xcassets有什么不一样的呢?接下来我们还是看一下例子。
Pod::Spec.new do |s|
s.name = "TestResource"
s.version = "0.0.1"
s.summary = "TestResource"
s.homepage = "http://www.sogou.com"
s.author = { "kingsword" => "[email protected]" }
s.platform = :ios, "8.0"
s.ios.deployment_target = "8.0"
s.source = { :svn => "http://[email protected]/svn/browser/seqa/qadev/src/iOS/ComponentLibraries/ComponentProjects/TestResource", :tag => "#{s.version}" }
s.resource = ['resource.xcassets']
s.resource_bundle = { 'ResourceBundleA' => ['resource_bundle.xcassets'] }
end
我们重复集成到新工程中,构建并查看.app
目录。
然后我们通过Asset Catalog Tinkerer分别打开根目录下的Assets.car和ResourceBundleA.bundle目录下的Assets.car。
我们发现,通过resource
集成的xcassets被放到.app
根目录下的Assets.car包中,而通过resource_bundle
集成的xcassets被放到了ResourceBundleA.bundle目录下的Asset.car包中。
这事我们继续在主工程的Asset.xcassets中新建同名的图片资源,然后继续打包,查看根目录下的Assets.car文件。
答案很明显,再次被覆盖了。所以如果为了尽可能的避免冲突、被覆盖,++那么还是老老实实的使用resource_bundles
属性吧++。
其实我们总使用[UIImage imageNamed:]
方法,但是其还有重载方法,可以指定bundle的[UIImage imageNamed: inBundle: compatibleWithTraitCollection:]
。
已上面的例子,用法如下:
//拼接bundle
NSString * bundleNameWithExtension = @"ResourceBundleA.bundle";
NSString * bundlePath = [[NSBundle bundleForClass:[MyViewController class]].resourcePath
stringByAppendingPathComponent:bundleNameWithExtension];
NSBundle * bundle = [NSBundle bundleWithPath:bundlePath];
UIImage * image = [UIImage imageNamed:@"icon_sina" inBundle:bundle compatibleWithTraitCollection:nil];
这样写代码,就必须要记住bundle的名称,有一些硬编码在,但是为了解决不必要的冲突,也是可以接受的,可以定义一些宏来减少硬编码的问题。
其实无论要实现什么样的功能,两个属性都可以实现,只不过在最终编码上可能有一些差别。
当然在通过resource
集成资源文件时,也可以先将资源文件放入bundle中,然后resource
对应该bundle目录,这样使用效果其实与resource_bundle
就一致了,只不过会有一些额外操作而已。
如果在多人开发环境中要去管理资源文件,还是建议使用resource_bundles
并且建立一套命名规范,来减少资源冲突导致的覆盖,避免不必要的麻烦。