一、为什么苹果在iOS5添加了addChildViewController?
苹果新的API增加了addChildViewController方法,并且希望我们在使用addSubview时,同时调用[self addChildViewController:child]方法将sub view对应的viewController也加到当前ViewController的管理中。
对于那些当前暂时不需要显示的subview,只通过addChildViewController把subViewController加进去;需要显示时再调用transitionFromViewController方法。将其添加进入底层的ViewController中。
这样做的好处:
1.无疑,对页面中的逻辑更加分明了。相应的View对应相应的ViewController。
2.当某个子View没有显示时,将不会被Load,减少了内存的使用。
3.当内存紧张时,没有Load的View将被首先释放,优化了程序的内存释放机制。
在iOS5中,ViewController中新添加了下面几个方法:
* addChildViewController:
* removeFromParentViewController
* transitionFromViewController:toViewController:duration:options:animations:completion:
* willMoveToParentViewController:
* didMoveToParentViewController:
能想到的其它的一些使用场景或者说是优势吧
- 不以navVC管理的,transition需要动画的 (例如某些login模块相关有注册,忘记密码,修改密码等)
- 容器vc
- 左右联动,或上下联动
- 条件显示,一个vc不是一直显示的view,且该view比较复杂,业务功能比较多
不管怎样,开发过程中还是看具体使用场景,addChildViewController本质优点还是清晰易管理,但是滥用的话也会造成vc耦合度太高的问题
二、该demo代码实现的功能
1.addChildViewController使用
2.选中下划线标识(两种实现方案)
3.切换标签下划线动画
4.头部标签headscrollview抽离封装
5.childViewController里子view布局时机注意事项
三、addChildViewController使用(代码有详细的注释和注意点)
主容器VC--MainViewController
//
// MainViewController.m
// 测试addChildViewController
// 1、选中下划线标识(两种实现方案) 2、切换标签下划线动画 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];
}
/// 请求网络数据
- (void)requestData {
//nothing
}
/// 初始化并布局子view
- (void)setupSubViews {
NSArray *headTitles = @[@"第一个",@"第二个",@"第三个",@"第一个",@"第二个",@"第三个"];
//初始换headTitleview
self.headScrollView = [[HeadTitlesScrollView alloc] initWithFrame:CGRectMake(0, 0, ScreenW, 40) titles:headTitles];
self.headScrollView.backgroundColor = [UIColor blueColor];
[self.view addSubview:self.headScrollView];
//点击头部标签回调事件
__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);
//默认添加第一个子vc
[self.view addSubview:self.firstVC.view]; //这个不能删除
[self addChildViewController:self.firstVC];
self.currentVC = self.firstVC;
}
/// 处理点击某个头部标签,展示对应的vc
/// @param btnTag 正在被点击按钮的tag
- (void)clickHeadBtn:(NSInteger)btnTag {
//点击的是同一个则返回
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:
//显示第一个vc
[self replaceOldVC:self.currentVC toNewVC:self.firstVC];
break;
case 101:
//显示第二个vc
[self replaceOldVC:self.currentVC toNewVC:self.secondVC];
break;
case 102:
//显示第三个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) {
//下面两句删除不会影响
[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"
//以下都可以暴露给外面,动态设置
static CGFloat headScrollViewHeight = 40;
static CGFloat headScrollViewBtnWidth = 100;
static CGFloat lineWidth = 50.f;
//按钮的样式:颜色,字体,背景色等
//底部线的样式:颜色,高度,宽度等
@interface HeadTitlesScrollView ()
@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, @"请使用initWithFrame:titles:初始化");
return [self initWithFrame:CGRectZero titles:@[]];
}
- (instancetype)initWithFrame:(CGRect)frame {
return [self initWithFrame:frame titles:@[]];
}
#pragma mark- 设置并布局子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
childViewController里子view布局时机注意
#import "SecondViewController.h"
@interface SecondViewController ()
@property (nonatomic, strong) UITableView *mainTb;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor purpleColor];
// //不能在这布局;该vc的view此时还没布局完成,取的会是主容器view的frame
// [self.view addSubview:self.mainTb];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
//子view正确布局时机
[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
效果图
结束语
addChildViewController参考链接
本来想放demo地址的,但是觉得还是自己敲一遍比较好,主要代码都在上面,能力有限 有什么不对的地方,欢迎指正[Salute]