UITabBarController中的坑

当你创建一个继承与UITabBarController的子类 并想给其自定义构造方法 传一些值的时候这时候问题出现了:

在创建的时候里面的init方法回调用了 viewdidload,导致每次传值的时候都会在viewdidload加载完了之后传的值才能传过去

如下代码

- (instancetype)initWithURLStrings:(NSArray *)urls{
    self = [super init];//在调用init的时候回调用viewDidLoad 导致参数传入进来时已经加载完了
    if (self) {
        self.urls = urls;
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
     [self setUpChild];
    self.tabBar.backgroundColor = [UIColor whiteColor];

}

- (void)setUpChild{
    // 1.初始化子控制器
    KHHomeVC *home = [[KHHomeVC alloc] initWithURL:self.urls[0]];
    [self addChildVc:home title:@"首页" image:@"tab_home_n" selectedImage:@"tab_home_s"];

    KHVipVC *vipVC = [[KHVipVC alloc] initWithURL:self.urls[1]];
    [self addChildVc:vipVC title:@"会员" image:@"tab_user_n" selectedImage:@"tab_user_s"];

    KHMineDetailVC *settingVC = [[KHMineDetailVC alloc] initWithURL:self.urls[2]];
    [self addChildVc:settingVC title:@"设置" image:@"tab_setting_n" selectedImage:@"tab_setting_s"];
}

- (void)addChildVc:(UIViewController *)childVc title:(NSString *)title image:(NSString *)image selectedImage:(NSString *)selectedImage
{
    // 设置子控制器的文字
    childVc.title = title; // 同时设置tabbar和navigationBar的文字

    // 设置子控制器的图片
    childVc.tabBarItem.image = [UIImage imageNamed:image];

    if (iOS7) {
        childVc.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImage] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    } else {
        childVc.tabBarItem.selectedImage = [UIImage imageNamed:selectedImage];
    }

    // 设置文字的样式
    NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
    textAttrs[NSForegroundColorAttributeName] = [UIColor colorWithRed:136/255.0 green:136/255.0 blue:136/255.0 alpha:1];
    NSMutableDictionary *selectTextAttrs = [NSMutableDictionary dictionary];
    selectTextAttrs[NSForegroundColorAttributeName] = [UIColor colorWithRed:0 green:183/255.0 blue:283/255.0 alpha:1];
    [childVc.tabBarItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
    [childVc.tabBarItem setTitleTextAttributes:selectTextAttrs forState:UIControlStateSelected];

    // 先给外面传进来的小控制器 包装 一个导航控制器
    KHNavigationController *nav = [[KHNavigationController alloc] initWithRootViewController:childVc];
    nav.navigationBarHidden = YES;
    // 添加为子控制器
    [self addChildViewController:nav];
}

这是怎么回事的 我们知道在一般控制器中我们想要自定义构造方法 会先走完初始化方法然后再走 viewDidLoad 但是这个UITabBarController怎么这么特殊呢呢?

经过一番查找终于知道了原来这是UITabBarController的一个特性

其实UITabBarController在init的时候,会增UITabbar到self.view,这时候必须加载self.view出来,从而就调用了loadview,进而调用viewdidload、viewwillappear等。这个是UITabBarController的特性

UITabBarController在调用【super init】的时候,系统调用了self.view的东西,但是此时view还没加载出来,于是系统强制先调用viewdidload去加载self.view,最后加载完了,再接着走init方法中的 if(self)条件语句。

解决方案:

就是不要在viewdidload 方法里写加载界面的代码,可以单独再写个方法,直接在init结束的时候调用。所以我的建议就是直接把viewdidload和viewwillappear方法删了,加载界面全都在init里写,如下:

- (instancetype)initWithURLStrings:(NSArray *)urls{
    self = [super init];//在调用init的时候回调用viewDidLoad 导致参数传入进来时已经加载完了
    if (self) {

        self.urls = urls; //初始化数据
         [self setUpChild]; //这是解决的重点

    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.tabBar.backgroundColor = [UIColor whiteColor];

}

- (void)setUpChild{
    // 1.初始化子控制器
    KHHomeVC *home = [[KHHomeVC alloc] initWithURL:self.urls[0]];
    [self addChildVc:home title:@"首页" image:@"tab_home_n" selectedImage:@"tab_home_s"];

    KHVipVC *vipVC = [[KHVipVC alloc] initWithURL:self.urls[1]];
    [self addChildVc:vipVC title:@"会员" image:@"tab_user_n" selectedImage:@"tab_user_s"];

    KHMineDetailVC *settingVC = [[KHMineDetailVC alloc] initWithURL:self.urls[2]];
    [self addChildVc:settingVC title:@"设置" image:@"tab_setting_n" selectedImage:@"tab_setting_s"];
}

- (void)addChildVc:(UIViewController *)childVc title:(NSString *)title image:(NSString *)image selectedImage:(NSString *)selectedImage
{
    // 设置子控制器的文字
    childVc.title = title; // 同时设置tabbar和navigationBar的文字

    // 设置子控制器的图片
    childVc.tabBarItem.image = [UIImage imageNamed:image];

    if (iOS7) {
        childVc.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImage] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    } else {
        childVc.tabBarItem.selectedImage = [UIImage imageNamed:selectedImage];
    }

    // 设置文字的样式
    NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
    textAttrs[NSForegroundColorAttributeName] = [UIColor colorWithRed:136/255.0 green:136/255.0 blue:136/255.0 alpha:1];
    NSMutableDictionary *selectTextAttrs = [NSMutableDictionary dictionary];
    selectTextAttrs[NSForegroundColorAttributeName] = [UIColor colorWithRed:0 green:183/255.0 blue:283/255.0 alpha:1];
    [childVc.tabBarItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
    [childVc.tabBarItem setTitleTextAttributes:selectTextAttrs forState:UIControlStateSelected];

    // 先给外面传进来的小控制器 包装 一个导航控制器
    KHNavigationController *nav = [[KHNavigationController alloc] initWithRootViewController:childVc];
    nav.navigationBarHidden = YES;
    // 添加为子控制器
    [self addChildViewController:nav];
}

我发现只有继承自UINavigationController和UITabBarController的类的init函数会产生loadView、viewDidLoad函数在初始化[super init]函数调用后就执行,一般UIViewController没有这个问题

@implementation UIViewControllerSubclass

- (id)init {
        NSLog(@"0");
    if (self = [super init]) {
        NSLog(@"1");
    }
    return self;
}

- (void)loadView {
    [super loadView];
    NSLog(@"2");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"3");
}

@end

console output

0
2
3
1

d

时间: 2024-10-19 18:42:03

UITabBarController中的坑的相关文章

UITabBarController中自定义UITabBar

1.创建多个视图控制器,放如UITabBarController中 AViewController *aa = [[AViewController alloc] init]; UINavigationController* ayNav = [[UINavigationController alloc]initWithRootViewController:aa]; BViewController *bb = [[BViewController alloc] init]; UINavigationC

Windows API中的坑

本文主页链接:Windows API中的坑 ExpandEnvironmentStrings 风险: 进程会继承其父进程的环境变量,在展开如%APPDATA%等目录时,有可能父进程对此环境变量进行过修改,那么可能你获取的就不是你想要的当前SESSION的%APPDATA%了. 建议: 使用SHGetFolderPath系列函数来做这件事. GetModuleFileName 风险: 在DLL中调用时,若传入的instance参数为NULL,那获取的将是加载DLL的进程的EXE的路径,若需要获取D

Torch-RNN运行过程中的坑 [2](Lua的string sub函数,读取中文失败,乱码?)

0.踩坑背景 仍然是torch-rnn/LanguageModel.lua文件中的一些问题,仍然是这个狗血的LM:encode_string函数: function LM:encode_string(s) local encoded = torch.LongTensor(#s) for i = 1, #s do local token = s:sub(i, i) local idx = self.token_to_idx[token] assert(idx ~= nil, 'Got invali

Torch-RNN运行过程中的坑 [1](读取Lua非空table,size为0)

0.踩坑背景 执行Torch-RNN的时候,在LanguageModel.lua中的encode_string函数中,对start_text的各个character进行id映射编码,实现功能类似"北京天安门"-->"5 10 88 32 111",方便后面的计算. 这个函数会利用一个全局的类似HashMap的table,hashmap中的key是character(char),value是id(int),涉及到一个从hashmap中按照key取值的操作,代码如

MVC 4中的坑

1.VirtualPathUtility.ToAbsolute 这个方法的官方解释是 Converts a virtual path to an application absolute path. 意思是应用程序虚拟路径转换为绝对路径 但实际是取不到绝对路径的 VirtualPathUtility.ToAbsolute("~/App_Data/"); 这个取到的实际是 C:/App_Data 这个目录 需要取绝对路径还是需要 System.Web.HttpContext.Curren

memset()函数中的坑

一 写在开头1.1 本节内容内存填充函数memset()中的坑. 二 函数原型 1 /* 来自man memset */ 2 #include <string.h> 3 void * memset(void * s, int c, size_t n); 功能描述:memset()函数用常量c的值填充由指针s所指向的内存地址空间的前n个字节的内存空间. DESCRIPTION : The memset() function fills the first n bytes of the memor

Linux编程中的坑——C++中exit和return的区别

今天遇到一个坑,折腾了一天才把这个坑填上,情况是这样的: 写了段代码,在main()函数中创建一个分离线程,结果这个线程什么都没干就直接挂掉了,代码长这样: [cpp] view plain copy int main() { 创建一个分离线程(): return 0: } 后来百度)了一下,原来在main()函数中写return,会被编译器优化成,也会像exit一样直接杀死所有进程,所以分离出来的线程什么都没干,就随着进程一起挂掉了. 所以多线程编程中,main()函数想退出线程应当使用 [c

lua中的坑

在工作中使用lua也有一年了,代码也写了不少,踩过不少坑,这里记录一下. table.sort table.sort是lua自带的排序函数,数据量小时,也还是不错的.不过要注意你传入的compare函数.例如: local tb = { 9,8,3,777,0,36548,556,0 } table.sort( tb,function(a,b) return a>=b end ) 上面的代码够简单了,但是你运行起来,却是报错了. Program starting as '"D:\progr

Jenkins 配置任务中的坑s

Jenkins 坑1:sh: adb: command not found 背景:在任务中使用了adb命令 adb 使用时要在服务器上配Android-home的环境变量的 配置完成之后发现在服务器上 adb OK,但是在jenkins 执行job时不OK 参考一下两篇文档 https://stackoverflow.com/questions/29216244/jenkins-build-failed-due-to-missing-android-sdk http://www.linuxdiy