Pod私有库素材管理
首先说说场景,由于以前项目是基于小组件化功能开发最后再拼合起来。某一些模块内需要用到一些UI素材,做法就是在 .podspec
中的 resource_bundle
中添加素材的路径,执行 pod install
后添加到pod里面
1 | s.resource_bundles = { |
如上,pod install
时候就会在路径文件夹下,导入所有.png
素材到工程里面.
这是网上普遍做法,且 cocoapod
的 .podspec
文件中也是默认这样导进去素材的.这样导进素材有什么问题呢?
install
完,素材只是简单的copy到了项目bundle里面(PS:素材文件夹命名也不正规)。会有以下问题:
- 管理起来麻烦
- 如果图片资源放到
.xcasset
里面Xcode
会帮我们自动优化、可以使用 Slicing 等,但是放在bundle
里面并不会压缩素材等,等项目大了,包越来越大
所以,调研了网上做法,踩了坑之后决定写一篇文章记录下做法,这也是 包瘦身 一个方法.由于从0记录了整个过程,我会从创建私有库开始写起,如果你已经有了私有库,直接可以跳去第二步看起
1. 创建私有库
使用pod lib create 私有库名字
创建完之后会自动打开工程
2.资源文件引用的方式
CocoaPods
两种资源文件引用的方式——resource_bundles
& resources
2.1 resources
在 .podspec
利用 resources
属性可以指定 pod 要使用的资源文件。这些资源文件在 build 时会被直接拷贝到 client target 的 mainBundle
里。这样就实现了把图片、音频、NIB等资源打包进最终应用程序的目的。
但是,这就带来了一个问题,那就是 client target 的资源和各种 pod 所带来的资源都在同一 bundle 的同一层目录下,很容易产生命名冲突。
例如,我的 app 里有张按钮图片叫 “button.png”,而你的 pod 里也有张图片叫 “button.png”,拷贝资源时,我很担心 pod 里的文件会不会把我 app 里的同名文件给覆盖掉?即使没覆盖掉,程序运行时到底用哪张?很显然,我们不希望上述事情发生。
2.2 resource_bundles
resource_bundles
允许定义当前 Pod 库的资源包的名称和文件。用 hash 的形式来声明,key 是 bundle 的名称,value 是需要包括的文件的通配 patterns。
为了解决上述问题,CocoaPods 在 0.23.0 加入了一个新属性 resource_bundles
。示例用法如下:
1 | spec.resource_bundles = { |
可见, resources
和 resource_bundles
的差别是在于后者用字典替换了数组。相较之前所有资源都平铺开来的做法,新属性显式地做了 bundle 层面的分组。有组织、有纪律!CocoaPods 官方显然更推荐 resource_bundles
。原因有二:
- 如前所述,用
resources
属性容易引起资源的命名冲突。诚然,resource_bundles
也有极小的可能在 bundle 名上起冲突,可那也比前者好处理。 - 用
resources
属性指定的资源直接被拷贝到 client target(事实上 CocoaPods 会先运行脚本对 NIB,Asset Catalog,Core Data Model 等进行编译)
2.3 Assets.xcassets
Assets.xcassets是用来存放图像资源文件的。常常利用Assets.xcassets
来管理项目中用到的icon
和各种图片素材, 但其除了提供为不同设备和Size Classes
添加不同的图片这种基础使用方式,这个也不做过多解释,我们的目标就是将Pod
里面的素材打包生成这样东西
3.Pod私有库素材管理
调查得到上面结果,我们目标与技术选择也很明白了,选择resource_bundles
+ Assets.xcassets
方式管理 Pod
里面的素材就ok了
做这个之前,我们还是测试下使用 resources
+ Assets.xcassets
会有什么坑吧
3.1 resources + Assets.xcassets 方式
首先在刚创建的私有库 .podspec
中配置一下
素材路径这里不用 cocoapod
默认的 resource_bundles
,改为resource
形式读取素材,素材路径为 pod
私有库下Assets
文件夹里面所有的 *.xcassets
文件。此时我们在 Assets
中创建2个 xcassets
测试下读取情况
分别再每个module
中创建各自的 xcassets
,名字与文件夹名字一样.
创建完之后再 Example
文件夹中运行pod install
更新下文件结构
完成后我们手动add file
导入刚刚创建的2个 *.xcassets
并向里面添加图片测试(图片名字先不要一样测试)
我们在 submodule1.xcassets
中添加了两个素材 loading@2x
与 loading@3x
; submodule2.xcassets
中添加2个素材 arrow@2x
、arrow@3x
, install 一下
可见,素材 .xcassets
已经导入成功且里面也有相应素材.然后读取相应素材
这种使用 resource
方法处理 pod
文件需要用以下方法读取图片
1 | //当然这个图片也可以用 submodule2 里面的图片名字,仅做测试 |
为什么需要这种方法读取呢
由于 iOS 8 Dynamic Frameworks 特性的引入,CocoaPods 能帮你打包 framework 了。0.36 版的 release note很详细地说明了加入 framework 特性所带来的变化。一个显著区别就是当你的 pod 库以 framework 形式被使用时,你的资源不是被拷贝到 mainBundle 下,而是被放到 pod 的最终产物—— framework
里。此时,你必须保证自己在访问这个 framework 的 bundle,而不是 client target 的。
1 | [NSBundle bundleForClass:<#ClassFromPodspec#>] |
到此,我们成功的使用 resource
方法继承了 Pod
中的素材,现在我们来测试下如果 *.xcassets
中如果有重复名字图片会怎么样
3.1.1 resource中 *.xcassets 中存在相同素材名字
首先我们将重复名字的 loading
素材放进去 submodule2.xcassets
中,因为 submodules2.xcassets
中的素材名字叫 arrow
,如下图
install 一下,即会出现以下错误,重复 image sets
,但是 Demo 中显示的图片还是原来的(submodule1.xcassets
的loading素材) loading
素材,
此时我们会想是否编译顺序会影响素材读取,同理,我们重新添加一份素材名为arrow
的素材放到 submodule1.xassets
中,且在Demo中读取 arrow
,不添加之前读取arrow
是显示一个箭头素材的,这次看看添加一个同名的素材到submodule1.xassets
中会出现什么情况
install 一下,同理提示错误
此时显示的素材为 submodule1.xcassets
中的 arrow
素材了.
3.1.2 resource中 *.xcassets 中与项目本身的 *.xcasset 存在相同素材名字
然后我们试验下第二个问题,如果 Pod
中的 *.xcasset
与项目自己的 *.xcasset
中素材重复名字会怎么样
此时我们先删除 submodule1.xcasset
中 arrow
素材,将其添加到 项目本身的 *.xcasset
中
经过测试,并不会有影响,因为在 Pod
中读取的是 Pod Bundle
中的素材,与App自身的 Asset
是不会有影响的
由此我们得出使用 resource
的结论,如果 Pod Bundle
中存在相同名字的素材,根据编译顺序不同,读取的图片不同。
3.2 resource_bundles
准备工作如之前步骤一样,.podspec
中可以写成如下:
此时改成了使用 s.resource_budnles
,install 完项目结构如下:
同理在不同的 *.xcassets
中放入不同素材,读取
此处读取图片需要硬编码方式读取, 加上了 bundle
的路径,这样也是能正常加载出来的
此时我们也来测试下重复素材会怎么样,测试方法也是如上.
此时也会出现重复素材问题,为什么呢?
因为这个这个方法:
1 | s.resource_bundles = { |
将Assets里面的所有素材最后都打包到 bundle
里面,遇到重复的名字的素材也会有上面问题,所以我们稍微改下上面的写法,改成:
1 | s.resource_bundles = { |
install完出现2个bundle
那么我们的做法其实就在读取图片时候,把硬编码改成相应的bundle即可
这个就能避免重复名字素材名字问题,当然这个硬编码也是其中一个不方便的缺点了
4 利用特性对项目的扩展
上面研究了这么多,功能只局限于当前 pod
的素材管理,那么我们回到项目改动上做一些扩展.
项目来说需要将这个pod,嵌入到不同app里面,不同app素材用一套,是不是听起来做法有点熟悉,
是的我们可以根据不同项目,将这个 s.resource_bundles
重写,例如:
1 | //Bundle这个Key名字写死,方便在硬编码时候用相同的 |
如图,我们可以利用 cocoapod
中的 Ruby
脚本命令重写这个私有库对于不同 App
使用的不同素材.、
在硬编码中读取 Bundle
的路径即可
总结
在这次调研中研究了 私有库 cocoapod一些使用与素材管理,包优化素材策略
resource_bundles 优点:
- 可以使用
.xcassets
指定资源文件 - 可以避免每个库和主工程之间的同名资源冲突
resource_bundles 缺点:
- 获取图片时可能需要使用硬编码的形式来获取:
[[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/SubModule_Use_Bundle.bundle"]
resources 优点:
- 可以使用
.xcassets
指定资源文件
resources 缺点:
- 会导致每个库和主工程之间的同名资源冲突
- 不需要用硬编码方式获取图片:
[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];
So,一般来说使用 resource_bundles
会更好,不过关于硬编码,还可以再找找别的方式去避免。
最后为了不同App素材可以根据 Ruby
脚本写下 Bundle
路径读取不同素材
最后上 Demo
Reference
Pod私有库素材管理