3篇文章帶你學(xué)會(huì)組件化??????
1.iOS 創(chuàng)建遠(yuǎn)程cocoapods 私有庫
2.iOS Pod 私有庫創(chuàng)建(自定義的組件)
3.iOS CTMediator組件化實(shí)踐
首先你要懂得組件化的整體思想杈湾,如下圖,就是通過一個(gè)中間者傳遞信息攘须,用來降低模塊間的耦合度, 從而達(dá)到高內(nèi)聚低耦合的目的.
提到的中間層就是CTMediator, GitHub上面介紹的非常詳細(xì), 還有使用文章推薦.
廢話不多說, 開始實(shí)踐:
-
目的: 項(xiàng)目的ViewController 的touchesBegan方法push 新界面(AViewController); 分解成通過CTMediator 和組件通信獲取aVc , 來達(dá)到相同功能.
步驟詳解
1. 創(chuàng)建組件A_Section, 可參考前一篇文章自定義組件
- GitHub 上創(chuàng)建A_Section組件遠(yuǎn)程倉庫
- 創(chuàng)建本地項(xiàng)目 (pod lib create A_Section)
- 修改.podspec 文件中的homepage 和source 和version
#發(fā)版版本號(hào)漆撞,每更新一次代碼就改變一次版本號(hào)
s.version = "0.0.1"
#你的 git 倉庫首頁的網(wǎng)頁 url,注意并不是 https/ssh這種代碼倉庫地址
s.homepage = "https://xxxxxxxxx/A_Section"
#這里就是你 git 倉庫的 https/ssh 地址了
s.source = { :git => "git@gitxxxxxxxx/A_Section.git", :tag => "#{s.version}" }
- 創(chuàng)建AViewController 和 Target_A 文件, 在選擇文件夾的時(shí)候記得選classes
注
: 一個(gè)組件對(duì)應(yīng)一個(gè) Target_A, 這里要特殊記憶下Target_A
的A
和Action_viewController
的viewController
;
// Target_A.h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Target_A : NSObject
/// 獲取該組件的AViewController 的實(shí)例化對(duì)象
- (UIViewController *)Action_viewController:(NSDictionary *)params;
@end
// Target_A.m 文件
#import "Target_A.h"
#import "AViewController.h"
@implementation Target_A
- (UIViewController *)Action_viewController:(NSDictionary *)params
{
AViewController *viewController = [[AViewController alloc] init];
return viewController;
}
@end
- 這時(shí)的A_Section工程目錄應(yīng)該是這樣的:
- 提交組件項(xiàng)目更新內(nèi)容, 組件發(fā)版打tag, 將repo源添加到自己的私有repo索引庫中.可參考自定義組件5~8步驟
2. 創(chuàng)建中間件A_Category(CTMediator 的Category)
- 同一業(yè)務(wù)模塊使用同一中間件就可以, 這里只是文件夾名稱, 可隨意, 這里使用A_Category
- 創(chuàng)建遠(yuǎn)程倉庫A_Category
- 修改.podspec 文件, 依賴CTMediator 第三方庫
- cd /A_Section/Example 目錄下, 更新(pod update --no-repo-update)
- 創(chuàng)建CTMediator 的分類CTMediator+A
- CTMediator (A)內(nèi)部實(shí)現(xiàn)如下:
// CTMediator+A.h 內(nèi)容
#import <CTMediator/CTMediator.h>
@interface CTMediator (A)
- (UIViewController *)getAViewController;
@end
// CTMediator+A.m 內(nèi)容
#import "CTMediator+A.h"
@implementation CTMediator (A)
- (UIViewController *)getAViewController
{
/*
AViewController *viewController = [[AViewController alloc] init];
*/
// 這個(gè)方法就牛逼了, CTMediator 會(huì)自動(dòng)去找Target_A 的類, 并且去尋找是否存在viewController 方法, 存在就去實(shí)現(xiàn)
return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];
}
@end
注
: 還記得步驟一第四小步需要特殊記憶的Target_A
的A
和Action_viewController
的viewController
嗎? 其實(shí)對(duì)應(yīng)的就是performTarget:@"A"
中的A
和action:@"viewController"
中的viewController
, 名字都可以隨便起, 但是必須是在對(duì)應(yīng)的前提下;
- 配置之后的文件結(jié)構(gòu):
- 提交組件項(xiàng)目更新內(nèi)容, 組件發(fā)版打tag, 將repo源添加到自己的私有repo索引庫中.可參考自定義組件5~8步驟
3. 本地創(chuàng)建主工程MainProject
- 就是創(chuàng)建個(gè)名字為MainProject(名稱可隨意) 的Xcode項(xiàng)目, pod init -> pod install 操作完成即可.
- 盡量保證A_Section 和A_Category 和MainProject 在同一文件夾下或同級(jí)目錄下, 方便后續(xù)操作.
4. 本地組件測試
- cd至MainProject目錄下, 修改profile 文件:
target 'ManiProject' do
use_frameworks!
#組件化依賴
pod 'CTMediator'
#本地測試
pod 'A_Category', :path => '../A_Category'
pod 'A_Section', :path => '../A_Section'
end
注
: pod 'A_Category', :path => '../A_Category'
代表在當(dāng)前的podfile目錄下, (../)返回上一級(jí)目錄, 找到A_Category文件, 拉取其中A_Category.podspec文件.
- 更新庫文件( pod install)
- 測試組件是否能正常使用, MainProject中的ViewController 類中測試push 功能
// ViewController.m文件內(nèi)容
#import "ViewController.h"
// 引入中間件頭文件
#import <A_Category/CTMediator+A.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// aVc 其實(shí)就是AViewController
UIViewController *aVc = [[CTMediator sharedInstance] getAViewController];
[self.navigationController pushViewController:aVc animated:YES];
}
@end
注
: 引入A_Category
的頭文件, 通過[[CTMediator sharedInstance] getAViewController]
獲取組件提供的東西.
這個(gè)時(shí)候能正常實(shí)現(xiàn)push 功能, 就算本地的組價(jià)可以正常使用了.
4. 遠(yuǎn)程倉庫的組件的使用測試
- 修改podfile 文件, 添加私有源repo索引庫.不會(huì)的話可參考創(chuàng)建遠(yuǎn)程cocoapods 私有庫
- 添加需要使用組件的依賴
podfile 文件大致為:
#這是GitHub的源索引庫
source 'https://github.com/CocoaPods/Specs.git'
#這是我自己的組件的源索引庫
source 'git@github.com:LiHe0308/PrivateSpecs.git'
target 'ManiProject' do
use_frameworks!
#組件化依賴
pod 'CTMediator'
#組件
pod 'A_Category'
pod 'A_Section'
end
- 主工程pod install 更新庫文件
- 測試push 功能, 代碼一點(diǎn)不需要變
這時(shí)候功能正常, 就代表遠(yuǎn)程組件可以正常使用了.
總結(jié)注意點(diǎn):
-
主工程、組件浮驳、之間的關(guān)系
1. 組件A_Section: 將相當(dāng)于正常開發(fā)的一個(gè)文件夾, 但是多了一個(gè)Target_A類, Target_A中對(duì)外(CTMediator 的分類)開放整個(gè)組件的功能,所以整個(gè)組件的功能都寫在Target_xName中.例:
- (UIViewController *)Action_viewController:(NSDictionary *)params;
2. A_Category: 主要是使用CTMediator 的分類,CTMediator (A), 重點(diǎn)就是命名, 一定要和組建中Target后面的名稱相同, 也就是A.
通過方法return [self performTarget:@"A" action:@"viewController" params:nil shouldCacheTarget:NO];實(shí)現(xiàn)和組件通信, 獲取自動(dòng)尋找Target_A 文件中的viewController 來執(zhí)行
然后封裝方法 - (UIViewController *)getAViewController; 供外界使用.
3. 主工程ManiProject: 就是你所做的項(xiàng)目, 它只與CTMediator (A)通訊, 引入頭文件,
通過分類中自定義的方法[[CTMediator sharedInstance] getAViewController]; 獲取到組件
-
代碼的提交順序, 嚴(yán)格遵守!!!
1. 一定要先提交所有變更信息
git remote add origin git@github.com:xxx.git
git add .
git commit -a -m "提交變更信息"
git push -u origin HEAD
2. 修改xxxxx.podspec 文件中homepage 和 source, 一定要準(zhǔn)確
s.homepage -> 你的 git 倉庫首頁的網(wǎng)頁 url悍汛,注意并不是 https/ssh這種代碼倉庫地址
s.source -> 這里就是你 git 倉庫的 https/ssh 地址了
3. 修改s.version
s.version = 0.2.0
4. 發(fā)版 -> 一定要與.podspec 文件中s.version 相同
git tag 0.2.0
git push origin 0.2.0
5. 將組建repo 地址推到私有源索引庫中 (只做一次就可以, 且必須在.podspec同級(jí)目錄下操作)
pod repo push [私有源名稱] 組件名稱.podspec --allow-warnings
-
關(guān)于 xib 和圖片
組件中若用到了圖片,或者 xib 資源至会,要指定資源的文件路徑离咐,否則不會(huì)把圖片打包到你的組件中:
s.resource_bundles = {
'A_Section' => ['A_Section/AImages/**/*.{png}', 'A_Section/Classes/*.{xib}']
}
或者
s.resources = ['A_Section/AImages/**/*.{png}', 'A_Section/Classes/*.{xib}']
如果使用 Xocde 工程自帶的 Assets 那個(gè)文件夾的話,圖片也無法打包到組件中奉件,最好自己重新創(chuàng)建一個(gè)新的文件夾用來存放圖片資源;
然后代碼中獲取 xib, png 等 resource 時(shí)宵蛀,bundle 重新設(shè)置,這樣就保證了無論在組件中县貌,還是在 MainProject 工程中术陶,都可以配置到正確的 Bundle,如果你使用 s.resource_bundles={},配置了自定義的 bundle名稱煤痕,那么 [bundle pathForResource:@"A_Section" ofType:@"bundle"]中就要替換成相應(yīng)的名稱.
//mainBundle
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *bundlePath = [bundle pathForResource:@"A_Section" ofType:@"bundle"];
if (bundlePath)
{
//組件資源所在的 bundle
bundle = [NSBundle bundleWithPath:bundlePath];
}
.End