资源文件主要包括:xib、图片以及其他文件资源(json、string等)。这里主要介绍
xib、图片。其他的资源和图片打包使用类似。

   上面两篇介绍静态库.a的手动和自动打包以及使用方法,但是通常在大工程组件化架构中,许多模块都是一个静态库(也可以是动态库的形式,后面会介绍)的形式提供给主栈调用的,那么可定少不了资源文件,那么我们希望将子工程的代码和资源文件一同自动打包在 VideoPlayerLib 文件夹中。其中 VideoPlayerLibBundle.bundle 打包的xib、图片资源。如下图

iOS自定义 pagecontrol_xib打包成Bundle

给静态库添加 Bundle

  1. 给静态库.a添加名为 VideoPlayerLibBundle 的bundle
  2. 修改Bundle 的Build Settings
    修改 Base SDK 为 iOS
    修改 COMBINE_HIDPI_IMAGES 为NO,否则bundle中的图片就是tiff格式
  3. 修改Build Options的Enable Bitcode 为NO
  4. 在Bundle 中添加几张图片如下图 步骤1、2。同时将左边目录中的图片拉到 3 的位置
  5. 创建一个带上xib的测试类:MyViewController,创建完毕后,在xib 中创建两个试图一个Button 一个UIImageView,其中Button直接在xib中设置图片,UIImageView拉到.h 作为属性以便测试代码设置图片。结果如下图:
  6. 把MyViewController.xib拖到VideoPlayerLibBundle项目下,结果如下图
  7. 创建一个类CRRobotBundleManager 获取bundle 的工具类

CRRobotBundleManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface CRRobotBundleManager : NSObject

+ (NSBundle *)getBundle;

+ (NSString *)getFilePathFromBundle: (NSString *) filePathInBundle;

@end

NS_ASSUME_NONNULL_END

CRRobotBundleManager.m 中bundle参数 @“VideoPlayerLibBundle”

#import "CRRobotBundleManager.h"

@implementation CRRobotBundleManager


+ (NSBundle *)getBundle {
    return [NSBundle bundleWithPath: [[NSBundle mainBundle] pathForResource:@"VideoPlayerLibBundle" ofType: @"bundle"]];
}

+ (NSString *)getFilePathFromBundle: (NSString *) filePathInBundle{
    
    NSBundle *myBundle = [CRRobotBundleManager getBundle];
    if (myBundle && filePathInBundle) {
        return [[myBundle resourcePath] stringByAppendingPathComponent: filePathInBundle];
    }
    return nil;
}

@end
  1. MyViewController 类代码

MyViewController.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *testImageView;
    
@end

NS_ASSUME_NONNULL_END

MyViewController.m

#import "MyViewController.h"
#import "CRRobotBundleManager.h"

@interface MyViewController ()

@end

@implementation MyViewController

    
//重写要加载的view的init方法
- (instancetype)init {
    NSBundle *bundle = [NSBundle bundleWithPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"VideoPlayerLibBundle.bundle"]];
    self = [super initWithNibName:@"MyViewController" bundle:bundle];
    if (self) {
        // Custom initialization
    }
    return self;
}
    
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    // 方法一
    self.testImageView.image = [UIImage imageNamed:@"VideoPlayerLibBundle.bundle/cr_add_hidden.png"];
    
    // 方法二:推荐
    // NSString *pic1Path = [CRRobotBundleManager getFilePathFromBundle:@"cr_add_hidden.png"];
    // self.testImageView.image = [UIImage imageWithContentsOfFile:pic1Path];
}

@end
  1. 在静态库中引用MyViewController

VideoPlayerLib.h 增加方法

#import <Foundation/Foundation.h>
#import "MyViewController.h"

@interface VideoPlayerLib : NSObject

/**
 引用MyViewController
*/
+ (MyViewController *)creatMyViewController;

VideoPlayerLib.m 实现

#import "VideoPlayerLib.h"
#import "CRRobotBundleManager.h"

@implementation VideoPlayerLib

+ (MyViewController *)creatMyViewController {
    MyViewController * myVC =[[MyViewController alloc]init];
    return myVC;
}
  1. 此时如果编译Target->VideoPlayerLibBundle.bundle 在Products 下VideoPlayerLibBundle.bundle Show In Folder 以及打出了资源包了!
  2. iOS自定义 pagecontrol_资源文件打包成bundle_02

  3. 将脚本跑一下,然后将VideoPlayerLibBundle.bundle copy到我们脚本存放合成.a和Headers 的目录下,那么就行形成 带有资源文件.bundle 的静态库.a SDK 提供给客户使用了。
  4. 但是既然我们实现.a的脚本化打包。那么可以在原来脚本的基础上稍微改动就能直接形成SDK。需要两步
    第一:给Target->VideoPlayerLib 添加依赖,这样在编译VideoPlayerLib 的时候同时编译VideoPlayerLibBundle。如下图
  5. iOS自定义 pagecontrol_iOS自定义 pagecontrol_03

  6. 第二:修改脚本,将编译出的VideoPlayerLibBundle.bundle copy 到目标文件中。
#图片资源资源
BUNDLE_DIR_INCLUDE=${build_DIR}/${development_mode}-iphoneos/VideoPlayerLibBundle.bundle

#将图片资源bundle copy到目标文件夹
cp -R "${BUNDLE_DIR_INCLUDE}" "${INSTALL_DIR}"

补充一下修改后的脚本:

#!/bin/sh

#  Script.sh
#  VideoPlayerLib
#
#  Created by lzz on 2019/4/10.
#  Copyright © 2019 lzz. All rights reserved.

#注意:脚本目录和xxxx.xcodeproj要在同一个目录,如果放到其他目录,请自行修改脚本。
#要build的target名
target_Name=VideoPlayerLib
echo "target_Name=${target_Name}"

#工程名
project_name=$target_Name
echo "project_name=${project_name}"

#打包模式 Debug/Release 默认是Release
development_mode=Debug


#当前脚本文件所在的路径 $(pwd)
SCRIPT_DIR=$(pwd)
echo "======脚本路径=${SCRIPT_DIR}======"

#工程路径
#PROJECT_DIR=${SCRIPT_DIR} 和下面写法也样
PROJECT_DIR=$SCRIPT_DIR
echo "======工程路径=${PROJECT_DIR}======"

#build之后的文件夹路径
build_DIR=$SCRIPT_DIR/Build
echo "======Build路径=${build_DIR}======"

#真机build生成的头文件的文件夹路径
DEVICE_DIR_INCLUDE=${build_DIR}/${development_mode}-iphoneos/include/${project_name}



#真机build生成的.a文件路径
DEVICE_DIR_A=${build_DIR}/${development_mode}-iphoneos/lib${project_name}.a
echo "======真机.a路径=${DEVICE_DIR_A}======"

#模拟器build生成的.a文件路径
SIMULATOR_DIR_A=${build_DIR}/${development_mode}-iphonesimulator/lib${project_name}.a
echo "======模拟器.a路径=${SIMULATOR_DIR_A}======"


#目标文件夹路径(也就SDK的文件:.a文件 和 头文件)
INSTALL_DIR=${build_DIR}/Products/${project_name}
echo "======SDK的文件夹路径=${INSTALL_DIR}======"

#目标头文件的文件夹路径
INSTALL_DIR_Headers=${build_DIR}/Products/${project_name}/Headers
echo "======头文件的文件夹路径=${INSTALL_DIR}======"

#目标.a路径
INSTALL_DIR_A=${build_DIR}/Products/${project_name}/lib${project_name}.a
echo "======目标.a路径=${INSTALL_DIR}======"


#图片资源资源
BUNDLE_DIR_INCLUDE=${build_DIR}/${development_mode}-iphoneos/VideoPlayerLibBundle.bundle


#判断build文件夹是否存在,存在则删除
#rm -rf 命令的功能:删除一个目录中的一个或多个文件或目录
if [ -d "${build_DIR}" ]
then
rm -rf "${build_DIR}"
fi


#判断目标文件夹是否存在,存在则删除该文件夹
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi


#创建目标文件夹
mkdir -p "${INSTALL_DIR}"



echo "======盒子已经准备好了,开始生产.a 并合成装到盒子里吧======"

#build之前clean一下
xcodebuild -target ${target_Name} -configuration ${development_mode} -sdk iphonesimulator clean

xcodebuild -target ${target_Name} -configuration ${development_mode} -sdk iphoneos clean

#模拟器build
xcodebuild -target ${target_Name} -configuration ${development_mode} -sdk iphonesimulator

#真机build
xcodebuild -target ${target_Name} -configuration ${development_mode} -sdk iphoneos


#复制头文件到目标文件夹
#使用-R参数可实现递归功能,即所有子目录中的文件与目录均拷贝
cp -R "${DEVICE_DIR_INCLUDE}" "${INSTALL_DIR_Headers}"



#将图片资源copy到目标文件夹
cp -R "${BUNDLE_DIR_INCLUDE}" "${INSTALL_DIR}"





#合成模拟器和真机.a包
lipo -create "${DEVICE_DIR_A}" "${SIMULATOR_DIR_A}" -output "${INSTALL_DIR_A}"

echo "======合成结束======"



# -f 判断文件是否存在
if [ -f "${INSTALL_DIR_A}" ]
then
echo "======验证合成包是否成功======"
lipo -info "${INSTALL_DIR_A}"
fi


#打开目标文件夹
open "${INSTALL_DIR}"
  1. 执行完脚本
  2. iOS自定义 pagecontrol_iOS自定义 pagecontrol_04

  3. 最后,我们就可以把上面路径下的 VideoPlayerLib 文件夹丢给客户了,然后尼?然后就可以喝喝茶,刷刷手机了!岂不乐哉!
  4. 使用验证
    新建demo 导入***VideoPlayerLib*** 文件夹 并使用
#import "VideoPlayerLib.h"
- (IBAction)pushLoadBundle:(id)sender {
    [self.navigationController pushViewController:[VideoPlayerLib creatMyViewController] animated:YES];
}

运行效果

iOS自定义 pagecontrol_xib打包成Bundle_05


写在最后:静态库.a 以及 资源bundle 手动、脚本自动打包以及使用到此结束了。以后如有其他的用法,会继续补充文章。

接下来会学习制作 静态库.Framework 的以及 资源bundle 手动、脚本自动打包