CocoaPods中管理图片资源
CocoaPods在.podspec文件中描述资源的字段有两个,resources和resouce_bundles。
resources
使用方式如下:
spec.resource = 'Resources/HockeySDK.bundle'
spec.resources = ['Images/*.png', 'Sounds/*']
CocoaPods不推荐使用resources,因为它会产生图片资源冲突。
使用"*.png"描述资源
此时打包之后Pod库图片会出现在安装包里,没有进入主工程的Assets.car中,不同Pod库中相同名字的图片会在安装包里冲突导致被覆盖掉。
使用"*.bundle"描述资源
此时打包之后Pod库的bundle文件会出现在安装包里,不同Pod库的同名bundle文件会融合成一个bundle文件,而bundle文件中相同名字的图片就会冲突然后被覆盖掉。
使用"*.xcassets"描述资源
此时Pod库的.xcassets中的图片会被打包到主工程的Assets.car中,不同Pod库中相同名字的图片会在Assets.car中出现冲突然后被覆盖。
上面详细描述了使用“resources”来描述图片资源时,打包之后图片所在的位置,及造成图片冲突的原因。
resource_bundles
使用方式如下:
spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
spec.resource_bundles = {
'MapBox' => ['MapView/Map/Resources/*.png'],
'MapBoxOtherResources' => ['MapView/Map/OtherResources/*.png']
}
resource_bundles内部通过一对key-value来描述资源,对每一个key生成一个bundle,value则是该bundle下的图片资源;为了减少key冲突,CocoaPods建议key至少包含Pod的名字,同一个Pod库下不同的bundle在Pod的名字之后加上不同的后缀。
CocoaPods推荐使用resource_bundles来管理图片资源,但是有一个非常严重的问题需要注意:https://github.com/CocoaPods/CocoaPods/issues/8431,就是当项目中有一个Pod库采用了resources&xcasset的方式描述资源时,项目所有Pod库中的xcasset都会被拷贝到主工程的Assets.car中;此时如果有一个Pod库使用了resource_bundles&xcasset描述资源时,该Pod的xcasset中的图片也会被拷贝到主工程的Assets.car中,这会与Pod库的自己bundle中生成的Assets.car图片造成重复,产生这个问题的地方在于执行pod install之后生成的Pods-xxxxx-resource.sh脚本里的如下代码中:
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
then
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
while read line; do
if [[ $line != "${PODS_ROOT}*" ]]; then
XCASSET_FILES+=("$line")
fi
done <<<"$OTHER_XCASSETS"
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
else
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
fi
fi
上述issue中提供了可以复现的demo,而且CocoaPods的作者按时安排在1.9版本中解决这个问题。
CocoaPods中图片资源的使用
NSBundle
再来熟悉一下文档中对Bundle的描述,"Apple uses bundles to represent apps, frameworks, plug-ins, and many other specific types of content",也就是说项目打包好的.app和frameworks都是一个Bundle。
查找并且打开一个NSBundle的创建方式大致分如下两种:
- +[NSBundle mainBundle]
"The main bundle represents the bundle directory that contains the current executing code. So for an app, the main bundle object gives you access to the resources that shipped with your app." 可以理解为.app就是一个Bundle。
// Get the app's main bundle
NSBundle *main = [NSBundle mainBundle];
- +[NSBundle bundleForClass:]
"if you link to a framework, you can use the bundleForClass: method to locate the framework bundle based on a class defined in that framework." 当我们想要获取一个Framework的Bundle时,就可以通过这个方法,方法参数是这个framework中定义的一个类。需要注意的是如果当前framework是静态库时,该方法等同于[NSBundle mainBundle]。
// Get the bundle containing the specified private class.
NSBundle *myBundle = [NSBundle bundleForClass:[MyPrivateClass class]];
获取当前Pod的图片资源
按照CocoaPods的建议,使用resource_bundles来描述资源文件,为了避免其他Pod库使用resources&xcasset给我们的Pod库造成麻烦,我们的Pod内部使用*.png来管理图片资源。
s.resource_bundles = {
'XCCategory' => ['XCCategory/Assets/*.png']
}
我们目前Pod库是静态库,所以会在.app下生成一个"XCCategory.bundle"。
当前Pod可能作为动态库或者静态库,为了兼容这两种情况,使用bundleForClass:来获取Pod的bundle,当Pod作为静态库时,该方法返回的是mainBundle,当Pod作为动态库时,该方法返回的就是动态库本身。
下面代码获取到了XCCategory.bundle,然后从XCCategory.bundle中取出对应的图片。
+ (nullable UIImage *)xccategory_imageName:(NSString *)name {
static NSBundle *resourceBundle = nil;
if (resourceBundle == nil) {
resourceBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[XCCategoryViewController class]] pathForResource:@"XCCategory" ofType:@"bundle"]];
}
UIImage *image = [UIImage imageNamed:name inBundle:resourceBundle compatibleWithTraitCollection:nil];
return image;
}