本文会建一个自定义的tabBarController
--------------------------------------------
在开始之前,我们先了解一下 Adding and Removing a Child ViewController方法
先看一下SDK:
/*
These two methods are public for container subclasses to call when transitioning between child
controllers. If they are overridden, the overrides should ensure to call the super. The parent argument in
both of these methods is nil when a child is being removed from its parent; otherwise it is equal to the new
parent view controller.
addChildViewController: will call [child willMoveToParentViewController:self] before adding the
child. However, it will not call didMoveToParentViewController:. It is expected that a container view
controller subclass will make this call after a transition to the new child has completed or, in the
case of no transition, immediately after the call to addChildViewController:. Similarly
removeFromParentViewController: does not call [self willMoveToParentViewController:nil] before removing the
child. This is also the responsibilty of the container subclass. Container subclasses will typically define
a method that transitions to a new child by first calling addChildViewController:, then executing a
transition which will add the new child's view into the view hierarchy of its parent, and finally will call
didMoveToParentViewController:. Similarly, subclasses will typically define a method that removes a child in
the reverse manner by first calling [child willMoveToParentViewController:nil].
*/
- (void)willMoveToParentViewController:(UIViewController *)parent NS_AVAILABLE_IOS(5_0);
- (void)didMoveToParentViewController:(UIViewController *)parent NS_AVAILABLE_IOS(5_0);
苹果新的API增加了addChildViewController方法,并且希望我们在使用addSubview时,同时调用[self addChildViewController:child]方法将sub view对应的viewController也加到当前ViewController的管理中。对于那些当前暂时不需要显示的subview,只通过addChildViewController把subViewController加进去。需要显示时再调用transitionFromViewController:toViewController:duration:options:animations:completion方法。
另外,当收到系统的Memory Warning的时候,系统也会自动把当前没有显示的subview unload掉,以节省内存。
---------------------
然后下面是添加,移除,转换三个方法的实现
1.Adding another view controller’s view to the container’s view hierarchy
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.frame = [self frameForContentController]; // 2
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self]; // 3
}
Here’s what the code does:
It calls the container’s addChildViewController: method to add the child. Calling the addChildViewController: method also calls the child’s willMoveToParentViewController: method automatically.
It accesses the child’s view property to retrieve the view and adds it to its own view hierarchy. The container sets the child’s size and position before adding the view; containers always choose where the child’s content appears. Although this example does this by explicitly setting the frame, you could also use layout constraints to determine the view’s position.
It explicitly calls the child’s didMoveToParentViewController: method to signal that the operation is complete.
Eventually, you want to be able to remove the child’s view from the view hierarchy. In this case, shown in Listing 14-2, you perform the steps in reverse.
----------
2.Removing another view controller’s view to the container’s view hierarchy
- (void) hideContentController: (UIViewController*) content
{
[content willMoveToParentViewController:nil]; // 1
[content.view removeFromSuperview]; // 2
[content removeFromParentViewController]; // 3
}
Here’s what this code does:
Calls the child’s willMoveToParentViewController: method with a parameter of nil to tell the child that it is being removed.
Cleans up the view hierarchy.
Calls the child’s removeFromParentViewController method to remove it from the container. Calling the removeFromParentViewController method automatically calls the child’s didMoveToParentViewController: method.
For a container with essentially static content, adding and removing view controllers is as simple as that. Whenever you want to add a new view, add the new view controller as a child first. After the view is removed, remove the child from the container. However, sometimes you want to animate a new child onto the screen while simultaneously removing another child. Listing 14-3 shows an example of how to do this.
----------
3.Transitioning between two view controllers
- (void) cycleFromViewController: (UIViewController*) oldC
toViewController: (UIViewController*) newC
{
[oldC willMoveToParentViewController:nil]; // 1
[self addChildViewController:newC];
newC.view.frame = [self newViewStartFrame]; // 2
CGRect endFrame = [self oldViewEndFrame];
[self transitionFromViewController: oldC toViewController: newC // 3
duration: 0.25 options:0
animations:^{
newC.view.frame = oldC.view.frame; // 4
oldC.view.frame = endFrame;
}
completion:^(BOOL finished) {
[oldC removeFromParentViewController]; // 5
[newC didMoveToParentViewController:self];
}];
}
Here’s what this code does:
Starts both view controller transitions.
Calculates two new frame positions used to perform the transition animation.
Calls the transitionFromViewController:toViewController:duration:options:animations:completion: method to perform the swap. This method automatically adds the new view, performs the animation, and then removes the old view.
The animation step to perform to get the views swapped.
When the transition completes, the view hierarchy is in its final state, so it finishes the operation by sending the final two notifications.
--------------------------------------------
要开始耍啦啦啦。。。
在.h文件中,留下一些外部访问的属性和方法:
@property (nonatomic, copy, readonly) NSArray * viewControllers;
@property (nonatomic, copy, readonly) NSArray * barItemImages;
- (instancetype)initWithViewControllers:(NSArray *)viewControllers barItemImages:(NSArray *)barItemImages;
- (instancetype)initWithViewControllers:(NSArray *)viewControllers;
在.m文件中 首先会定义一些属性:
#import "DHTabViewController.h"
typedef NS_ENUM(NSUInteger, ViewTag) {
ButtonTag = 100
};
@interface DHTabViewController ()
@property (nonatomic, copy) NSArray * viewControllers;
@property (nonatomic, copy) NSArray * barItemImages;
@property (nonatomic, assign) NSUInteger selectedControllerIndex;
@property (nonatomic, strong) UIView * buttonContainerView;
@property (nonatomic, strong) UIView * childControllerContainerView;
- (void)initializeAppearance;
@end
--------------------------------------------
说明一下,我们应该遵循一定的代码规范(叫吗?我也不知道。。)
#init
#life circle
#system delegate
#custom delegate
#private method
#getter setter
就是关于各种方法位置的顺序啦。。
--------------------------------------------
@implementation DHTabViewController
#pragma mark - initializer
- (instancetype)initWithViewControllers:(NSArray *)viewControllers
{
self = [self initWithViewControllers:viewControllers barItemImages:@[]];
return self;
}
- (instancetype)initWithViewControllers:(NSArray *)viewControllers barItemImages:(NSArray *)barItemImages
{
self = [super init];
if (self) {
self.selectedControllerIndex = 0;
self.viewControllers = viewControllers;
self.barItemImages = barItemImages;
}
return self;
}
- (instancetype)init
{
self = [self initWithViewControllers:@[]];
return self;
}
pragma mark - 生命周期
- (void)viewDidLoad {
[super viewDidLoad];
[self initializeAppearance];
}
#pragma mark - action/callback
- (void)action_onButton:(UIButton *)sender
{
NSUInteger index = sender.tag - ButtonTag;
if (index == self.selectedControllerIndex) {
return;
}
// 移除当前显示的controller
UIViewController * currentController = self.viewControllers[self.selectedControllerIndex];
[currentController.view removeFromSuperview];
[currentController removeFromParentViewController];
[currentController willMoveToParentViewController:nil];
// 加载选择的controller
UIViewController * selectedController = self.viewControllers[index];
[self addChildViewController:selectedController];
self.selectedControllerIndex = index;
}
#pragma mark - system protocol implentations
#pragma mark - custom protocol implentations
#pragma mark - private methods
- (void)initializeAppearance
{
if (self.viewControllers.count == 0) {
return;
}
[self.view addSubview:self.childControllerContainerView];
[self.view addSubview:self.buttonContainerView];
// 默认加载第一个controller的内容
UIViewController * firstViewController = self.viewControllers.firstObject;
[self addChildViewController:firstViewController];
// 初始化切换controller的按钮
CGFloat buttonWidth = CGRectGetWidth(self.view.bounds) / self.viewControllers.count;
[self.viewControllers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setBackgroundColor:[UIColor yellowColor]];
[button addTarget:self action:@selector(action_onButton:) forControlEvents:UIControlEventTouchUpInside];
[button setFrame:CGRectMake(idx * buttonWidth, 0, buttonWidth, 40)];
if (self.barItemImages.count > 0) {
[button setImage:self.barItemImages[idx] forState:UIControlStateNormal];
} else {
[button setTitle:[NSString stringWithFormat:@"%ld",idx+1] forState:UIControlStateNormal];
}
button.tag = ButtonTag+idx;
[self.buttonContainerView addSubview:button];
}];
}
#pragma mark - override
- (void)addChildViewController:(UIViewController *)childController
{
[self.childControllerContainerView addSubview:childController.view];
childController.view.frame = CGRectOffset(self.view.frame, 0, 40);
[super addChildViewController:childController];
[childController didMoveToParentViewController:self];
}
#pragma mark - getter
- (UIView *)buttonContainerView
{
if (!_buttonContainerView) {
_buttonContainerView = ({
UIView * view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 40)];
[self childControllerContainerView];
view;
});
}
return _buttonContainerView;
}
- (UIView *)childControllerContainerView
{
if (!_childControllerContainerView) {
_childControllerContainerView = ({
UIView * view = [[UIView alloc] initWithFrame:self.view.bounds];
[self buttonContainerView];
view;
});
}
return _childControllerContainerView;
}
@end
懒得翻译了。。都是很简单的单词,大家理解的哈