Pod私有库素材管理

​ 首先说说场景,由于以前项目是基于小组件化功能开发最后再拼合起来。某一些模块内需要用到一些UI素材,做法就是在 .podspec中的 resource_bundle中添加素材的路径,执行 pod install后添加到pod里面

1
2
3
4
5
s.resource_bundles = {
#素材路径
#采用的是keyValue形式
'Bundle名字' => ['路径/Resource/Asset/**/**.png']
}

如上,pod install时候就会在路径文件夹下,导入所有.png素材到工程里面.

这是网上普遍做法,且 cocoapod.podspec 文件中也是默认这样导进去素材的.这样导进素材有什么问题呢?

install完,素材只是简单的copy到了项目bundle里面(PS:素材文件夹命名也不正规)。会有以下问题:

  1. 管理起来麻烦
  2. 如果图片资源放到 .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
2
3
4
spec.resource_bundles = {
'MyLibrary' => ['Resources/*.png'],
'OtherResources' => ['OtherResources/*.png']
}

可见, resourcesresource_bundles 的差别是在于后者用字典替换了数组。相较之前所有资源都平铺开来的做法,新属性显式地做了 bundle 层面的分组。有组织、有纪律!CocoaPods 官方显然更推荐 resource_bundles。原因有二:

  1. 如前所述,用 resources 属性容易引起资源的命名冲突。诚然, resource_bundles 也有极小的可能在 bundle 名上起冲突,可那也比前者好处理。
  2. 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@2xloading@3x ; submodule2.xcassets 中添加2个素材 arrow@2xarrow@3x, install 一下

可见,素材 .xcassets 已经导入成功且里面也有相应素材.然后读取相应素材

这种使用 resource方法处理 pod 文件需要用以下方法读取图片

1
2
3
4
//当然这个图片也可以用 submodule2 里面的图片名字,仅做测试 
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
UIImage *image = [UIImage imageNamed:@"loading" inBundle:bundle compatibleWithTraitCollection:nil];
self.imageView.image = image;

为什么需要这种方法读取呢

由于 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.xcassetarrow 素材,将其添加到 项目本身的 *.xcasset

经过测试,并不会有影响,因为在 Pod 中读取的是 Pod Bundle 中的素材,与App自身的 Asset是不会有影响的

由此我们得出使用 resource 的结论,如果 Pod Bundle中存在相同名字的素材,根据编译顺序不同,读取的图片不同。

3.2 resource_bundles

准备工作如之前步骤一样,.podspec中可以写成如下:

此时改成了使用 s.resource_budnles,install 完项目结构如下:

同理在不同的 *.xcassets中放入不同素材,读取

此处读取图片需要硬编码方式读取, 加上了 bundle的路径,这样也是能正常加载出来的

此时我们也来测试下重复素材会怎么样,测试方法也是如上.

此时也会出现重复素材问题,为什么呢?

因为这个这个方法:

1
2
3
s.resource_bundles = {
'Pod-Resource' => ['Pod-Resource/Assets/**/*.xcassets']
}

将Assets里面的所有素材最后都打包到 bundle里面,遇到重复的名字的素材也会有上面问题,所以我们稍微改下上面的写法,改成:

1
2
3
4
5
s.resource_bundles = {
#不同文件夹不同bundle打包
'Pod-Resource1' => ['Pod-Resource/Assets/submodule1/*.xcassets'],
'Pod-Resource2' => ['Pod-Resource/Assets/submodule2/*.xcassets'],
}

install完出现2个bundle

那么我们的做法其实就在读取图片时候,把硬编码改成相应的bundle即可

这个就能避免重复名字素材名字问题,当然这个硬编码也是其中一个不方便的缺点了

4 利用特性对项目的扩展

上面研究了这么多,功能只局限于当前 pod 的素材管理,那么我们回到项目改动上做一些扩展.

项目来说需要将这个pod,嵌入到不同app里面,不同app素材用一套,是不是听起来做法有点熟悉,

是的我们可以根据不同项目,将这个 s.resource_bundles 重写,例如:

1
2
3
4
5
6
7
8
9
10
11
12
//Bundle这个Key名字写死,方便在硬编码时候用相同的
s.subspec 'aApp' do |aApp|
aApp.resource_bundles = {
'Bundle' => ['aApp素材路径']
}
end

s.subspec 'bApp' do |bApp|
bApp.resource_bundles = {
'Bundle' => ['bApp素材路径']
}
end

如图,我们可以利用 cocoapod中的 Ruby脚本命令重写这个私有库对于不同 App 使用的不同素材.、

在硬编码中读取 Bundle 的路径即可

总结

在这次调研中研究了 私有库 cocoapod一些使用与素材管理,包优化素材策略

resource_bundles 优点:

  1. 可以使用 .xcassets 指定资源文件
  2. 可以避免每个库和主工程之间的同名资源冲突

resource_bundles 缺点:

  1. 获取图片时可能需要使用硬编码的形式来获取:[[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/SubModule_Use_Bundle.bundle"]

resources 优点:

  1. 可以使用 .xcassets 指定资源文件

resources 缺点:

  1. 会导致每个库和主工程之间的同名资源冲突
  2. 不需要用硬编码方式获取图片:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil];

So,一般来说使用 resource_bundles 会更好,不过关于硬编码,还可以再找找别的方式去避免。

最后为了不同App素材可以根据 Ruby 脚本写下 Bundle路径读取不同素材

最后上 Demo

Reference

1.关于 Pod 库的资源引用 resource_bundles or resources

2.给 Pod 添加资源文件

3.Pod 中资源引入方式对比

Author

Sylar

Posted on

2019-04-28

Updated on

2021-11-14

Licensed under

Comments