一、為什么蘋果在iOS5添加了addChildViewController阁苞?
蘋果新的API增加了addChildViewController方法差购,并且希望我們?cè)谑褂胊ddSubview時(shí)亭罪,同時(shí)調(diào)用[self addChildViewController:child]方法將sub view對(duì)應(yīng)的viewController也加到當(dāng)前ViewController的管理中复哆。
對(duì)于那些當(dāng)前暫時(shí)不需要顯示的subview欣喧,只通過(guò)addChildViewController把subViewController加進(jìn)去;需要顯示時(shí)再調(diào)用transitionFromViewController方法梯找。將其添加進(jìn)入底層的ViewController中。
這樣做的好處:
1.無(wú)疑益涧,對(duì)頁(yè)面中的邏輯更加分明了锈锤。相應(yīng)的View對(duì)應(yīng)相應(yīng)的ViewController。
2.當(dāng)某個(gè)子View沒(méi)有顯示時(shí)闲询,將不會(huì)被Load久免,減少了內(nèi)存的使用。
3.當(dāng)內(nèi)存緊張時(shí)扭弧,沒(méi)有Load的View將被首先釋放阎姥,優(yōu)化了程序的內(nèi)存釋放機(jī)制。
在iOS5中鸽捻,ViewController中新添加了下面幾個(gè)方法:
* addChildViewController:
* removeFromParentViewController
* transitionFromViewController:toViewController:duration:options:animations:completion:
* willMoveToParentViewController:
* didMoveToParentViewController:
能想到的其它的一些使用場(chǎng)景或者說(shuō)是優(yōu)勢(shì)吧
- 不以navVC管理的呼巴,transition需要?jiǎng)赢嫷?(例如某些login模塊相關(guān)有注冊(cè),忘記密碼御蒲,修改密碼等)
- 容器vc
- 左右聯(lián)動(dòng)衣赶,或上下聯(lián)動(dòng)
- 條件顯示,一個(gè)vc不是一直顯示的view厚满,且該view比較復(fù)雜府瞄,業(yè)務(wù)功能比較多
不管怎樣,開發(fā)過(guò)程中還是看具體使用場(chǎng)景碘箍,addChildViewController本質(zhì)優(yōu)點(diǎn)還是清晰易管理遵馆,但是濫用的話也會(huì)造成vc耦合度太高的問(wèn)題
二、該demo代碼實(shí)現(xiàn)的功能
1.addChildViewController使用
2.選中下劃線標(biāo)識(shí)(兩種實(shí)現(xiàn)方案)
3.切換標(biāo)簽下劃線動(dòng)畫
4.頭部標(biāo)簽headscrollview抽離封裝
5.childViewController里子view布局時(shí)機(jī)注意事項(xiàng)
三丰榴、addChildViewController使用(代碼有詳細(xì)的注釋和注意點(diǎn))
主容器VC--MainViewController
//
// MainViewController.m
// 測(cè)試addChildViewController
// 1货邓、選中下劃線標(biāo)識(shí)(兩種實(shí)現(xiàn)方案) 2、切換標(biāo)簽下劃線動(dòng)畫 3多艇、headscrollview抽離封裝
// Created by caohx on 2021/1/13.
//
#import "MainViewController.h"
#import "FirstViewController.h"
#import "SecondViewController.h"
#import "ThirdViewController.h"
#import "HeadTitlesScrollView.h"
static CGFloat headHeight = 40;
@interface MainViewController ()
@property (nonatomic, strong) HeadTitlesScrollView *headScrollView;
@property (nonatomic, strong) UIViewController *currentVC;
@property (nonatomic, strong) FirstViewController *firstVC;
@property (nonatomic, strong) SecondViewController *secondVC;
@property (nonatomic, strong) ThirdViewController *thirdVC;
@end
@implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor grayColor];
self.automaticallyAdjustsScrollViewInsets = NO;
[self setupSubViews];
[self requestData];
}
/// 請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)
- (void)requestData {
//nothing
}
/// 初始化并布局子view
- (void)setupSubViews {
NSArray *headTitles = @[@"第一個(gè)",@"第二個(gè)",@"第三個(gè)",@"第一個(gè)",@"第二個(gè)",@"第三個(gè)"];
//初始換headTitleview
self.headScrollView = [[HeadTitlesScrollView alloc] initWithFrame:CGRectMake(0, 0, ScreenW, 40) titles:headTitles];
self.headScrollView.backgroundColor = [UIColor blueColor];
[self.view addSubview:self.headScrollView];
//點(diǎn)擊頭部標(biāo)簽回調(diào)事件
__weak typeof(self) weakSelf = self;
self.headScrollView.blockClickHeadTitle = ^(NSInteger btnTag) {
[weakSelf clickHeadBtn:btnTag];
};
//初始化子vc
CGFloat originY = headHeight;
CGFloat childVCHeight = ScreenH - headHeight;
self.firstVC = [FirstViewController new];
self.firstVC.view.frame = CGRectMake(0, originY, ScreenW, childVCHeight);
self.secondVC = [SecondViewController new];
self.secondVC.view.frame = CGRectMake(0, originY, ScreenW-100, childVCHeight-100);
self.thirdVC = [ThirdViewController new];
self.thirdVC.view.frame = CGRectMake(0, originY, ScreenW, childVCHeight);
//默認(rèn)添加第一個(gè)子vc
[self.view addSubview:self.firstVC.view]; //這個(gè)不能刪除
[self addChildViewController:self.firstVC];
self.currentVC = self.firstVC;
}
/// 處理點(diǎn)擊某個(gè)頭部標(biāo)簽逻恐,展示對(duì)應(yīng)的vc
/// @param btnTag 正在被點(diǎn)擊按鈕的tag
- (void)clickHeadBtn:(NSInteger)btnTag {
//點(diǎn)擊的是同一個(gè)則返回
if (((self.currentVC == self.firstVC) && btnTag == 100) ||
((self.currentVC == self.secondVC) && btnTag == 101) ||
((self.currentVC == self.thirdVC) && btnTag == 102)) {
return;
}
//改變子vc
switch (btnTag) {
case 100:
//顯示第一個(gè)vc
[self replaceOldVC:self.currentVC toNewVC:self.firstVC];
break;
case 101:
//顯示第二個(gè)vc
[self replaceOldVC:self.currentVC toNewVC:self.secondVC];
break;
case 102:
//顯示第三個(gè)vc
[self replaceOldVC:self.currentVC toNewVC:self.thirdVC];
break;
default:
break;
}
}
- (void)replaceOldVC:(UIViewController*)oldVC toNewVC:(UIViewController*)newVC {
[self addChildViewController:newVC];
[self transitionFromViewController:self.currentVC toViewController:newVC duration:1 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) {
if (finished) {
//下面兩句刪除不會(huì)影響
[self.view addSubview:newVC.view];
[newVC didMoveToParentViewController:self];
//移除舊的的vc
[oldVC willMoveToParentViewController:nil];
[oldVC removeFromParentViewController];
self.currentVC = newVC;
}else {
self.currentVC = oldVC;
}
}];
}
@end
抽離封裝的HeadTitlesScrollView
#import "HeadTitlesScrollView.h"
//以下都可以暴露給外面,動(dòng)態(tài)設(shè)置
static CGFloat headScrollViewHeight = 40;
static CGFloat headScrollViewBtnWidth = 100;
static CGFloat lineWidth = 50.f;
//按鈕的樣式:顏色,字體复隆,背景色等
//底部線的樣式:顏色拨匆,高度,寬度等
@interface HeadTitlesScrollView () <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *mainScrollView;
@property (nonatomic, strong) UILabel *bLine;
@property (nonatomic, strong) NSArray *arrTitles;
@property (nonatomic, strong) NSMutableArray *mutArrBtns;
@property (nonatomic, strong) NSMutableArray *mutArrBottomLines;
@end
@implementation HeadTitlesScrollView
#pragma mark- 初始化
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray*)titles {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.arrTitles = titles.copy;
self.mutArrBtns = [NSMutableArray array];
self.mutArrBottomLines = [NSMutableArray array];
headScrollViewHeight = self.frame.size.height;
[self setupSubViews];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
NSAssert(NO, @"請(qǐng)使用initWithFrame:titles:初始化");
return [self initWithFrame:CGRectZero titles:@[]];
}
- (instancetype)initWithFrame:(CGRect)frame {
return [self initWithFrame:frame titles:@[]];
}
#pragma mark- 設(shè)置并布局子view
- (void)setupSubViews {
//主scrollview
self.mainScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, ScreenW, headScrollViewHeight)];
self.mainScrollView.backgroundColor = [UIColor blueColor];
CGFloat contentSizeWidth = self.arrTitles.count*headScrollViewBtnWidth;
self.mainScrollView.contentSize = CGSizeMake(contentSizeWidth, 0);
// self.mainScrollView.pagingEnabled = YES;
self.mainScrollView.scrollEnabled = YES;
self.mainScrollView.bounces = NO;
self.mainScrollView.showsHorizontalScrollIndicator = NO;
[self addSubview:self.mainScrollView];
//頭部scrollview添加button 和 button初始化
for (int i = 0; i<self.arrTitles.count; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.frame = CGRectMake(i*headScrollViewBtnWidth, 0, headScrollViewBtnWidth, headScrollViewHeight);
[btn setTitle:self.arrTitles[i] forState:UIControlStateNormal];
[btn setTitleColor:[UIColor purpleColor] forState:UIControlStateNormal];
btn.titleLabel.textAlignment = NSTextAlignmentCenter;
btn.tag = 100+i;
[self.mainScrollView addSubview:btn];
[btn addTarget:self action:@selector(clickHeadBtn:) forControlEvents:UIControlEventTouchUpInside];
// //選中標(biāo)示線方案一:每一個(gè)btn對(duì)應(yīng)一個(gè)下劃線挽拂,通過(guò)hidden來(lái)控制
// UILabel *bottomLine = [UILabel new];
// bottomLine.backgroundColor = [UIColor blackColor];
// CGFloat lineOriginY = headScrollViewBtnWidth*i + (headScrollViewBtnWidth - lineWidth)/2.0;
// bottomLine.frame = CGRectMake(lineOriginY, headScrollViewHeight-2, lineWidth, 2);
// [self.mainScrollView addSubview:bottomLine];
// //默認(rèn)第一個(gè)選中惭每,顯示下劃線標(biāo)識(shí)線
// bottomLine.hidden = !(i == 0);
// [self.mutArrBottomLines addObject:bottomLine];
}
//選中標(biāo)示線方案二:一個(gè)line 通過(guò)改變offset.x來(lái)改變選中標(biāo)識(shí)
UILabel *bottomLine = [UILabel new];
bottomLine.backgroundColor = [UIColor blackColor];
CGFloat lineOriginX = headScrollViewBtnWidth*0 + (headScrollViewBtnWidth - lineWidth)/2.0;
bottomLine.frame = CGRectMake(lineOriginX, headScrollViewHeight-2, lineWidth, 2);
[self.mainScrollView addSubview:bottomLine];
self.bLine = bottomLine;
}
/// 回調(diào)點(diǎn)擊標(biāo)簽事件
/// @param currClickedBtn 當(dāng)前被點(diǎn)擊的btn
- (void)clickHeadBtn:(UIButton*)currClickedBtn {
NSInteger currIndex = currClickedBtn.tag -100;
// //改變選中下劃線 + 方案一
// [self.mutArrBottomLines enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
// UILabel *currLine = obj;
// currLine.hidden = !(idx == currIndex);
// }];
//改變選中下劃線 + 方案二
CGFloat lineOriginX = headScrollViewBtnWidth*currIndex + (headScrollViewBtnWidth - lineWidth)/2.0;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.bLine.frame = CGRectMake(lineOriginX, headScrollViewHeight-2, lineWidth, 2);
} completion:nil];
if (self.blockClickHeadTitle) {
self.blockClickHeadTitle(currClickedBtn.tag);
}
}
#pragma mark- UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
//監(jiān)聽(tīng)contentoffset 改變標(biāo)識(shí)下劃線位置
}
@end
childViewController里子view布局時(shí)機(jī)注意
#import "SecondViewController.h"
@interface SecondViewController () <UITableViewDataSource,UITableViewDelegate>
@property (nonatomic, strong) UITableView *mainTb;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor purpleColor];
// //不能在這布局;該vc的view此時(shí)還沒(méi)布局完成亏栈,取的會(huì)是主容器view的frame
// [self.view addSubview:self.mainTb];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
//子view正確布局時(shí)機(jī)
[self.view addSubview:self.mainTb];
}
#pragma mark- UITableViewDatasource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellid"];
cell.textLabel.text = @"test";
return cell;
}
#pragma mark- getter
- (UITableView *)mainTb {
if (!_mainTb) {
_mainTb = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStylePlain];
_mainTb.delegate = self;
_mainTb.dataSource = self;
_mainTb.rowHeight = 100;
[_mainTb registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cellid"];
}
return _mainTb;
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@end
效果圖
結(jié)束語(yǔ)
addChildViewController參考鏈接
本來(lái)想放demo地址的,但是覺(jué)得還是自己敲一遍比較好绒北,主要代碼都在上面黎侈,能力有限 有什么不對(duì)的地方,歡迎指正[Salute]