前言
上一节我们通过把代码简单的拆分上传至github
,通过cocoapods
的远程私有库,把拆分的代码当作第三方库供我们使用,但是这种方式存在很多问题,实际项目使用这种简单粗暴的拆分也不可取,所以今天讲上一篇博客提到的第二种私有库创建方式,后面统一把私有库称作组件。
最后大概结构
本文要讲的是改变组件创建的方式,解决:
- 组件使用
.framework
或者.a
这两种静态库的方式创建 - 组件单独依赖第三方,即有自己的
podfile
文件配置使用 - 组件有自己单独的资源文件,在自己组件中使用资源文件
- 组件工程调试及配置使用
吐槽部分可跳过
关于创建静态库的方式:
- 使用pod命令也就是
pod lib
命令创建 - 自己通过
xcode
手动创建
两种方式也无所谓好坏,都能够达到创建组件的目的。我想要说的是手动创建好处:
- 手动创建可以知道创建过程都做了什么,
pod
命令如果不去深究不能很明白原理。 -
pod
命令创建的组件只能放在Development Pods
文件夹目录下
,对于手动创建使用target
依赖的方式的方式来说显得不是很友好。 -
pod
命令创建的组件要添加资源文件时配置麻烦,手动创建简单。
当然手动创建也有缺点:
-
pod
命令创建方便快捷,手动创建相对步骤多 - 创建和编译过程需要踩坑。
总结:
- 如果是单纯抽取一些小东西用命令就可以没必要折腾
- 如果项目要大规模使用组件化建议手动创建方式
正文
上面说过创建组件静态库有两种,一种是.framwework另外一种是.a,两种方式创建大同小异。这里先以创建.a静态库的方式创建,相关节点配置不同点我会说明。
创建
打开xcode创建进入选择创建菜单
上图中Cocoa Touch Framework
就是创建.framework
静态库的方式,我们先创建.a形式的静态库 选中Cocoa Touch Static Library
点击 next
,填写你组件静态库的名称完成。
创建好之后像这样:
初始文件:
- 帮我们创建和静态库名称一样的文件夹和类(头文件)。
- 把创建的头文件加入到
Copy Files
里面,这里要关心的一点是,如果你修改了头文件这里也要添加相应你要暴露的头文件,这里的头文件可以是多个也就是你想暴露多少个就添加多少个。
添加podfile:
使用pod init
命令在根目录添加 podfile
,这里简单添加Masonry
platform :ios, '8.0'
target 'StaticKit' do
pod 'Masonry'
end
进行pod install
命令之后我们可以看到pod
给我们黄色的警告
主要是前面的错误:
[!] The Podfile contains framework or static library targets (StaticKit), for which the Podfile does not contain host targets (targets which embed the framework).
If this project is for doing framework development, you can ignore this message. Otherwise, add a target to the Podfile that embeds these frameworks to make this message go away (e.g. a test target).
这大概的意思是说当前的target
为静态库,而且pod
依赖的是当前这个静态库不是依赖host targets
,host targets
我的理解是可执行的target
,当时我没注意这个警告以为是普通的警告,之后添加到壳工程的时候会提示该静态库的pod
找不到。
当然最后如果我们认真看提示的警告已经告诉了我们解决的办法了,最后一行的括号内(e.g. a test target)
。这就是让我们创建一个测试的target
然pod
依赖它就可以让这个警告消失。这个很多网上文章都是一笔带过没说清楚的,不然就是不提静态库添加第三方依赖的问题,当然我的解释也不一定对。
静态库需要添加一个测试target
让第三方依赖
添加测试target
:
点击+
按钮进入选择target
菜单
选中iOS Unit Testing Bundle
取名为 StaticKitTests
创建好之后是这样
修改podfile
重新更新
platform :ios, '8.0'
project 'StaticKit'
#修改为测试的target
target 'StaticKitTests' do
pod 'Masonry'
end
更新后的 pod
信息,发现已经正常了
到这里如果你把Masonry 导入项目的类的时候会发现提示无法找到头文件,这时候我们要设置Heaser Search Paths 设置路径 添加 "$(SRCROOT)/Pods/Headers/Public"
添加资源文件:
添加资源文件我一般可以简单创建一个bundle放到工程目录下,使用时添加工程下就行了。这种方式很简单也是可以用的,但是这样的方式不容易管理我们的资源文件。在这里我推荐把资源文件也当作一个target
添加到静态库组件中,过程和上面创建测试target
差不多只是需要一些配置。
点击+
好到创建target
界面,tab页选中macOS
下面的 Bundle
,下一步 我取名为StaticKit_res
按自己需要可以改名字
创建好之后是这样的
创建的时候你可以看到我们创建的是macOS
的bundle
,至于为什么要用mac
不用iOS
很简单因为iOS
没有这个选项,所以为了适配iOS
我们需要做下面两个配置
- 配置
Base SDK
为iOS
配置路径看下图
- 一般我们图片有
@2x
@3x
这样放在bundle
会导致读取图片失败,所以我们要改下配置,把COMBINE_HIDPI_IMAGES
设置为NO
读取资源文件:
我们的组件的资源文件基础配置已经好了,接下来要添加一个工具类来读取资源了,在这里我们以读取图片资源为例,其他的资源大同小异。
- 我们添加几张测试图片放到
staticKit_res
文件夹下,记得target
的选择
- 创建我们的工具类名称为
StaticKitHelper
代码如下
@interface StaticKitHelper : NSObject
+ (NSBundle *)resBundle:(Class)classtype;
+ (UIImage*)imageName:(NSString*)name;
@end
#import "StaticKitHelper.h"
@implementation StaticKitHelper
+ (NSBundle *)resBundle:(Class)classtype {
static NSBundle *framworkBundle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
framworkBundle = [NSBundle bundleForClass:classtype];
if (framworkBundle) {
NSString *resourceBundlePath = [framworkBundle pathForResource:@"StaticKit_res" ofType:@"bundle"];
if (resourceBundlePath && [[NSFileManager defaultManager] fileExistsAtPath:resourceBundlePath]) {
framworkBundle = [NSBundle bundleWithPath:resourceBundlePath];
}
}
});
return framworkBundle;
}
+ (UIImage*)imageName:(NSString*)name {
if (name.length == 0) {
return nil;
}
NSBundle *bundle = [StaticKitHelper resBundle:[self class]];
NSString *path = [bundle pathForResource:name ofType:@"png"];
return [UIImage imageWithContentsOfFile:path];
}
@end
//调用
UIImage *image = [StaticKitHelper imageName:@"PlayButtonOverlayLarge"];
添加测试代码:
通过上面的设置我们的.a静态库组件基础配置已经好了,接下来我们要添加测试代码,我这里把默认的头文件普通的类改成一个控制器类,设置背景为蓝色,并添加了一张图片:
#import "StaticKit.h"
#import "StaticKitHelper.h"
#import <Masonry/Masonry.h>
@implementation StaticKit
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"StaticKit控制器";
UIImage *image = [StaticKitHelper imageName:@"PlayButtonOverlayLarge"];
UIImageView *imageView = [UIImageView new];
imageView.image = image;
[self.view addSubview:imageView];
[imageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(100);
make.top.equalTo(self.view).offset(100);
make.size.mas_equalTo(CGSizeMake(40, 40));
}];
self.view.backgroundColor = [UIColor blueColor];
}
@end
到这里StaticKit组件已经是一个合格的静态库组件了,它可以参与模块的开发了,最后总结下大概的步骤:
- 打开
xcode
创建Cocoa Touch Static Library
设置组件名字 - 添加测试
target
,创建podfile
设置依赖的target
为测试StaticKitTests
- 添加资源文件
target
StaticKit_res 设置两个编译参数,导入测试图片 - 添加读取资源工具类,添加测试代码
- 编写的代码在自己的静态库组件内编译通过
添加壳工程调试:
- 准备工作
所谓的壳工程就是我平常创建可以直接运行的工程,你可以随便创建一个带导航栏的控制器,用于push
到我们的静态库组件供调试,这个步骤很简单就不废话了默认你已经创建好了壳工程。
- 添加到壳工程
- 打开壳工程将
StaticKit.xcodeproj
拖到壳工程下
- 点击壳工程选中
Build Phases
在Target Dependencies
下点+
号 添加StaticKit
和StaticKit_res
- 在同一个地方的
Link Binary With Libraries
添加StaticKit
- 在静态库中的
Products
文件夹下的StaticKit_res
拖到Copy Bundle Resources
下
- 编译下如果显示成功就算大功告成了,如果有报错你可以回头看上面那个步骤做错了或者漏了。
这里解释下
第一步没什么好解释的。
第二步是我们把两个target
当作子target
依赖壳工程。
第三步表示代码执行我们链接的是StacKit
这个target
而不包含StaticKit_res
。
第四步是把我们静态库的资源文件添加到mainBundle
里面这样我们才可以通过代码找得到它。
添加壳工程测试代码:
//第三方方式访问,这个头文件在我们创建的时候有说要设置才能访问
#import <StaticKit/StaticKit.h>
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"点击跳转到库";
}[图片上传中...(show.gif-f0b9fe-1530268184126-0)]
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
StaticKit *sk = [[StaticKit alloc]init];
[self.navigationController pushViewController:sk animated:YES];
}
@end
运行截图
到这里自己手动创建.a静态库的全过程就完了,接下来会说.a静态库和.framework静态库配置,framework的创建和.a基本流程是一致的,某些配置有细微差别。
.framework静态库的差异
- framework的静态库创建的时候默认都是动态的,但是上架是不允许动态的所以我要在
Build Settings
里面的Mach-O Type
设置为Static Library
2.framework设置的头文件不一样,framework创建的时候头文件都会默认显示在Project
下我们需要把它拖到Public
下
3.最后一个不同点就是加人壳工程是frameworkKit.framework
不需要加入到Target Dependencies
下
本文到这里就交代完手动创建静态库到调试的全部内容了,如果有写错的地方或者有什么问题你可以在本文下方评论,也希望大家都交流学习。下篇带大家编译静态库到远程私有库,敬请期待!
参考资料
https://www.raywenderlich.com/65964/create-a-framework-for-ios
https://github.com/jverkoey/iOS-Framework