代码改变世界

iOS,视图控制器相关(UIViewController)

2016-09-20 09:35  帅不过三秒  阅读(554)  评论(0编辑  收藏  举报

1.视图控制器各个方法调用时机

2.选项卡(Tab Bar)和导航栏(Navigation Bar)

3.有无控制器的页面跳转

4.页面跳转隐藏底部选项卡

5.获取导航栏和状态栏高度,隐藏导航栏返回按钮、导航控制器,隐藏状态栏

6.模态视图的弹出动画和弹出风格

7.移除main.storyboard,解决总是进入ViewController

9.视图控制器不进入dealloc的几种原因 

10.app锁屏相关

11.设置导航控制器字体颜色等

12.获得当前屏幕显示的ViewController

视图控制器各个方法调用时机

init:方法

在init方法中实例化必要的对象(遵从LazyLoad思想)
init方法中初始化ViewController本身
 
loadView:方法
这是当没有正在使用nib视图页面,子类将会创建自己的自定义视图层。绝不能直接调用
 
viewDidLoad:方法
在视图加载后被调用
 
viewWillAppear:方法
视图即将可见时调用
 
viewDidAppear:方法
视频完全过渡到屏幕上时调用
 
viewWillDisappear:方法
视图被驳回时调用,覆盖或以其他方式隐藏
 
viewDidDisappear:方法
视图被驳回后调用,覆盖或以其他方式隐藏
 
当A视图切换到B视图时,顺序流程
1.B视图的viewDidLoad
2.A视图的viewWillDisappear
3.B视图的viewWillAppear
4.A视图viewDidDisappear
5.B视图viewDidAppear
 
 
viewWillLayoutSubviews:方法
控制器的view将要布局子控件
 
viewDidLayoutSubviews:方法
控制器的view布局子控件完成
 
任何原因引起View的尺寸被改变;这期间系统可能会多次调用viewWillLayoutSubviews、viewDidLayoutSubviews 俩个方法
 

didReceiveMemoryWarning:方法

内存不足情况下视图控制器会收到这条消息;并不代表该应用占用了很多内存,也有可能是后台有很多程序占用了内存;收到警告后可以尝试释放内存(内存大户图像、视频、声音等)不是立刻需要的东西。(如果iOS认为你占用了很多内存是可以杀掉app的,比如在堆中存放20个视频等不合理使用内存的操作)

 

选项卡(Tab Bar)和导航栏(Navigation Bar)

//AppDelegate.h文件

#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property(strong,nonatomic) UITabBarController *tabBarController;//底部选项卡控制器

@end

 

//AppDelegate.m文件

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window=[[UIWindow alloc]initWithFrame:[[UIScreen mainScreen] bounds]];

    //消息控制器
    MessageViewController *msgController=[[MessageViewController alloc] init];
    UINavigationController *navMsg=[[UINavigationController alloc] initWithRootViewController:msgController];
    //选项卡的图片样式以这种方式处理,避免图片无法完全显示
    UITabBarItem *msgTabItem = [[UITabBarItem alloc]initWithTitle:@"消息" image:[[UIImage imageNamed:@"tab_message@3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] selectedImage:[[UIImage imageNamed:@"tab_message_select@3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
    navMsg.tabBarItem=msgTabItem;

    

    //联系人控制器
    ContactViewController *conController=[[ContactViewController alloc] init];
    UINavigationController *navCon=[[UINavigationController alloc] initWithRootViewController:conController];
    UITabBarItem *conTabItem = [[UITabBarItem alloc]initWithTitle:@"联系人" image:[[UIImage imageNamed:@"tab_ contact@3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] selectedImage:[[UIImage imageNamed:@"tab_ contact_select@3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
    navCon.tabBarItem=conTabItem;
    

    //动态控制器
    DynamicViewController *dynController=[[DynamicViewController alloc] init];
    UINavigationController *navDyn=[[UINavigationController alloc] initWithRootViewController:dynController];
    UITabBarItem *dynTabItem = [[UITabBarItem alloc]initWithTitle:@"动态" image:[[UIImage imageNamed:@"tab_ dynamic@3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] selectedImage:[[UIImage imageNamed:@"tab_ dynamic_select@3x.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
    navDyn.tabBarItem=dynTabItem;

    self.tabBarController=[[UITabBarController alloc] init];
    self.tabBarController.viewControllers=@[navMsg,navCon,navDyn];
    self.tabBarController.selectedIndex=0;

    //由于iPhone是单窗口程序,所以也就只有这么一个Window对象,而且是UIWindow,不是NSWindow。而根据文档上所说:这个是便捷方法,去使被使用对象的主窗口显示到屏幕的最前端。
    [self.window makeKeyAndVisible];
    return YES; 
}

 

 

//底部选项卡显示未读消息

self.navigationController.tabBarItem.badgeValue = [NSString stringWithFormat:@"%ld",(long)count];

 //应用图标消失未读消息

 [UIApplication sharedApplication].applicationIconBadgeNumber = count;

 

有无控制器的页面跳转

pushviewController是UINavigationController推到一个新viewController

//以栈的方式管理视图,各个视图的切换就是压栈和出栈操作,出栈后的视图会立即销毁。

[self.navigationController pushViewController:qqContactController animated:YES];//将视图压入栈内,后显示

//直接跳转到根控制器

 [self.navigationController popToRootViewControllerAnimated:YES];

//返回到上一层控制器(可以改变索引指定返回到前几个控制器)

[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:([self.navigationController.viewControllers count]-2)] animated:YES];

  //从栈中弹出该视图控制器(返回到上一层控制器)

[self.navigationController popViewControllerAnimated:YES];
 
presentViewController弹出模态视图(模态视图显示的时候不能对其他视图进行操作,例如UIAlertView)

//弹出模态视图

[self presentViewController:view animated:YES completion:nil];//弹出视图,不过是全屏不带navigation bar的。

//隐藏销毁模态视图

[self dismissViewControllerAnimated:YES completion:nil];

 

页面跳转隐藏底部选项卡

//在push到其他控制器要隐藏底部选项卡时,在需要跳转的控制器初始化方法下处理hidesBottomBarWhenPushed

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
            // 将hidesBottomBarWhenPushed属性设置如果为YES,当这个控制器push的时候,底部的Bar,比如Tabbar会滑走,也就是不会在push后的视图上显示出来,默认值为NO。
            //这个属性只支持非自定义的Tabbar,也就是只支持原生Tabbar,
            self.hidesBottomBarWhenPushed = YES;
        }
     return self;
}

//或者在跳转前设置

[ctr setHidesBottomBarWhenPushed:YES];

 

获取导航栏和状态栏高度,隐藏导航栏返回按钮、导航控制器,隐藏状态栏

CGRect rectStatus = [[UIApplication sharedApplication] statusBarFrame]; // 状态栏(statusbar)

float statusHeight=rectStatus.size.height; //状态栏高度

float navHeight=self.navigationController.navigationBar.frame.size.height; //导航栏高度

 

 //隐藏导航控制器返回按钮

[self.navigationItem setHidesBackButton:YES]; 

 //隐藏导航控制器

[self.navigationController setNavigationBarHidden:YES animated:NO];

//隐藏状态栏

- (BOOL)prefersStatusBarHidden
{
    return YES;//隐藏为YES,显示为NO
}

 

 

模态视图的弹出动画和弹出风格

//弹出动画

typedef enum {
        UIModalTransitionStyleCoverVertical = 0,//视图从屏幕底部幻灯片,默认
        UIModalTransitionStyleFlipHorizontal,//当前视图启动水平3 d从从右到左的翻转,导致新视图的暴露,就好像它是在前面的视图
        UIModalTransitionStyleCrossDissolve,//视图淡出
        UIModalTransitionStylePartialCurl,//从当前视图右下角卷起
} UIModalTransitionStyle;

//弹出风格

typedef enum {
    UIModalPresentationFullScreen = 0,//弹出VC充满全屏
    UIModalPresentationPageSheet,//VC高度和当前屏幕高度相同,宽度和竖屏模式下屏幕宽度相同,剩余未覆盖区域将会变暗并阻止用户点击,这种弹出模式下,竖屏时跟UIModalPresentationFullScreen的效果一样,横屏时候两边则会留下变暗的区域。
    UIModalPresentationFormSheet,//VC的高度和宽度均会小于屏幕尺寸,VC居中显示,四周留下变暗区域。
    UIModalPresentationCurrentContext,//VC的弹出方式和该VC的父VC的方式相同。
} UIModalPresentationStyle;
这四种方式在iPad上面统统有效,但在iPhone和iPod touch上面系统始终已UIModalPresentationFullScreen模式显示presented VC。

//例:
    TestViewController *view=[[TestViewController alloc] init];
//弹出动画
//    view.modalTransitionStyle=UIModalTransitionStyleCoverVertical;//视图从屏幕底部幻灯片,默认
    view.modalTransitionStyle=UIModalTransitionStyleCrossDissolve;//视图淡出
//    view.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;//当前视图启动水平3 d从从右到左的翻转,导致新视图的暴露,就好像它是在前面的视图
//    view.modalTransitionStyle=UIModalTransitionStylePartialCurl;//从当前视图右下角卷起
    [self presentViewController:view animated:YES completion:nil];
 

移除main.storyboard,解决总是进入ViewController

在info.plist中删除下面一行

在工程中General中去掉关联

注意APPDelegate里面要加

[self.window makeKeyAndVisible];

 

视图控制器不进入dealloc的几种原因 

 
dealloc方法之所以没有被调用时因为控制器的一个或多个强引用仍然在内存中,也就是当前控制器的计数器不为0
一般的几种原因:

 

  • 1.定时器没有被销毁, 解决方法:在viewWillDisappear需要把控制器用到的NSTimer销毁

    //调用一次定时器 
    NSTimer  *myTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:selfselector:@selector(hideView) userInfo:nil repeats:NO];
    //多次调用定时器,将reperats设置为YES
     NSTimer  *myTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:selfselector:@selector(hideView) userInfo:nil repeats:YES];
    
    //取消定时器,永久停止,需要将timer设置为nil
    [myTimer invalidate];
        myTimer = nil;
    
    //关闭定时器
    [myTimer setFireDate:[NSDate distantFuture]];
    
    //开启定时器
    [myTimer setFireDate:[NSDate distantPast]];
  • 2.block块使用不当,因为block会对方法中的变量自动retain移除,请检查控制器中的block代码
  • 3.代理必须得用weak修饰,用strong强引用会导致计数器加1,无法释放内存。
  • 4.在getter方法里使用self,导致死循环

app锁屏相关

// 禁止自动锁屏
[UIApplication sharedApplication].idleTimerDisabled = YES;

// 允许自动锁屏
[UIApplication sharedApplication].idleTimerDisabled = NO;

 

 

设置导航控制器字体颜色等

//设置标题
self.title=@"牛牛设置";
//设置标题颜色字体大小
[self.navigationController.navigationBarsetTitleTextAttributes:@{NSFontAttributeName:[UIFontsystemFontOfSize:19],NSForegroundColorAttributeName:[UIColorcolorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1]}];
//修改导航栏背景色
[self.navigationController.navigationBar setBarTintColor:[UIColorcolorWithRed:30/255.0 green:144/255.0 blue:255/255.0 alpha:0.5]];
//更改返回按钮颜色
self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
//下一个视图的返回按钮文件会改变为下面设置的值
UIBarButtonItem *returnButtonItem = [[UIBarButtonItem alloc] init];
returnButtonItem.title = @"";
self.navigationItem.backBarButtonItem = returnButtonItem;  

 

获得当前屏幕显示的ViewController

/**
 获得当前屏幕显示的ViewController
 适用范围tabbar的子视图都是NavigationController,
 或有NavigationController的视图控制器
 @return 返回当前正在显示的视图控制器
 */
- (UIViewController *)getCurrentVC{
    UIViewController *result = nil;
    UIWindow * window = [[UIApplication sharedApplication] keyWindow];
    //app默认windowLevel是UIWindowLevelNormal(默认的水平。使用这个级别的大部分内容,包括为您的应用程序的主窗口。),如果不是,找到UIWindowLevelNormal的
    if (window.windowLevel != UIWindowLevelNormal)
    {
        NSArray *windows = [[UIApplication sharedApplication] windows];
        for(UIWindow * tmpWin in windows)
        {
            if (tmpWin.windowLevel == UIWindowLevelNormal)
            {
                window = tmpWin;
                break;
            }
        }
    }
    id  nextResponder = nil;
    UIViewController *appRootVC=window.rootViewController;
    //    如果是present上来的appRootVC.presentedViewController 不为nil
    if (appRootVC.presentedViewController) {
        nextResponder = appRootVC.presentedViewController;
    }else if(appRootVC.childViewControllers){
        //如果有子视图控制器
        while (appRootVC.childViewControllers>0) {
            if ([appRootVC isKindOfClass:[UINavigationController class]]) {
                appRootVC=[appRootVC.childViewControllers lastObject];
                break;
            }
            else if ([appRootVC isKindOfClass:[UITabBarController class]]){
                UITabBarController * tabbar = (UITabBarController *)appRootVC;
                appRootVC=(UINavigationController *)tabbar.viewControllers[tabbar.selectedIndex];
            }
            else{
                for (int i=0; i<appRootVC.childViewControllers.count; i++) {
                    //isViewLoaded 表示已经视图被加载过 view.window表示视图正在显示
                    if ([appRootVC.childViewControllers objectAtIndex:i].isViewLoaded && [appRootVC.childViewControllers objectAtIndex:i].view.window) {
                        appRootVC=[appRootVC.childViewControllers objectAtIndex:i];
                        break;
                    }
                }
            }
        }
        nextResponder=appRootVC;
    }else{
        UIView *frontView = [[window subviews] objectAtIndex:0];
        //nextResponder返回接收器的下一个应答器(见UIResponder+Router进行事件分发)
        nextResponder = [frontView nextResponder];
    }
    result = nextResponder;
    return result;
}