[65→100] iOS開發(fā)02:從啟動頁看ViewController的生命周期

功能需求

功能完成App都會有一個(gè)頁面——啟動頁。啟動頁最簡單的功能就是

  1. 顯示一張宣傳頁
  2. 幾秒鐘后消失,跳轉(zhuǎn)其它頁面。

相關(guān)技術(shù)點(diǎn)

在iOS開發(fā)頁面,自然要用到ViewController、UIView、UIWindow。

可以將它們之間的關(guān)系想象成這樣一個(gè)場景:首先會拿出一個(gè)空的畫框(UIWindow),人在畫框上放置一塊畫布(ViewController),然后可以在這個(gè)畫布上進(jìn)行繪畫,畫布上有一個(gè)基本的底色(UIView *view),在這個(gè)底色上可以被添加上各種元素,例如UILabel、UIButton等。這些元素其實(shí)也是一個(gè)又一個(gè)UIView,它們會有一個(gè)層級關(guān)系管理,有點(diǎn)相當(dāng)于Photoshop圖層的概念,層級高的元素會覆蓋住層級低的元素,從而導(dǎo)致層級低的元素被部分或完全遮擋。一個(gè)畫框可以有多塊畫布,多塊畫布之間進(jìn)行邏輯跳轉(zhuǎn)就構(gòu)成了一個(gè)完整App。

按照用途,ViewController分為兩類:

  1. 展示具體內(nèi)容,與用戶交互。如UIViewController、 UITableViewController;
  2. 控制其他ViewController的顯示與隱藏。如UINavigationController、UITabbarController,它們都有一個(gè)屬性:viewControllers。其中UINavigationController表示一種Stack式結(jié)構(gòu),push一個(gè)ViewController或pop一次,因此后一個(gè)ViewController一般會依賴前一個(gè)ViewController。而UITabbarController表示一個(gè)Array結(jié)構(gòu),各個(gè)ViewController是并列的。

ViewController的生命周期

ViewController生命周期
  • 當(dāng)一個(gè)ViewController被創(chuàng)建,并在屏幕上顯示的時(shí)候代碼的執(zhí)行順序:

    1. alloc 創(chuàng)建對象,分配空間
    2. init (initWithNibName) 初始化對象
    3. loadView 從nib載入視圖 ,通常這一步不需要去干涉。除非你沒有使用xib文件創(chuàng)建視圖
    4. viewDidLoad 載入完成,可以進(jìn)行自定義數(shù)據(jù)以及動態(tài)創(chuàng)建其他控件
    5. viewWillAppear 視圖將出現(xiàn)在屏幕之前,馬上這個(gè)視圖就會被展現(xiàn)在屏幕上了
    6. viewDidAppear 視圖已在屏幕上渲染完成
  • 當(dāng)一個(gè)視圖控制器被移除屏幕并且銷毀的時(shí)候的執(zhí)行順序:

    1. viewWillDisappear 視圖將被從屏幕上移除之前執(zhí)行
    2. viewDidDisappear 視圖已經(jīng)被從屏幕上移除,用戶看不到這個(gè)視圖了
    3. dealloc 視圖被銷毀
  • view加載的循環(huán)為:

    1. 程序請求ViewController的view屬性
    2. 如果view在內(nèi)存中,則直接加載;如果不存在,則調(diào)用loadView方法
    3. loadView方法執(zhí)行如下方法:
    • 如果重載了這個(gè)方法,則必須創(chuàng)建必要的UIView并且將一個(gè)非nil值傳給ViewController的view屬性。
    • 如果沒有重載這個(gè)方法,ViewController會默認(rèn)使用自己的nibName和nibBundle屬性嘗試從nib文件加載view。如果沒有找到nib文件,它嘗試尋找一個(gè)與ViewController類名匹配的nib文件。如果沒有可用的nib文件,那么它創(chuàng)建一個(gè)空的UIView作為它的view。

所有周期函數(shù)中最常用的為viewDidLoad , 此時(shí)最適合創(chuàng)建一些附加的view和控件了。
針對啟動頁,其viewDidLoad如下

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // 新建一個(gè)UIImageView,放入view里面
    self.launchImgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.launchImgView setBackgroundColor:[UIColor greenColor]];
    [self.view addSubview:self.launchImgView];
}

定時(shí)器

在iOS開發(fā)中,我們可以通過三種途徑來實(shí)現(xiàn)定時(shí)調(diào)用某一個(gè)方法的功能。

  1. 使用NSTimer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(reloop) userInfo:nil repeats:NO];
// A:自動激活定時(shí)器,這種方法已經(jīng)被廢棄
//    [timer fire]; 
// B:手動加入主循環(huán)
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 在循環(huán)調(diào)用時(shí),必須手動釋放定時(shí)器,否則不必手動釋放
//    [timer invalidate];
  1. 使用CADisplayLink(屏幕刷新時(shí)調(diào)用,比如視頻播放的時(shí)候需要不停地獲取下一幀用于界面渲染。)
    CADisplayLink是一個(gè)能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時(shí)器類。CADisplayLink以特定模式注冊到runloop后,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時(shí)候,runloop就會向CADisplayLink指定的target發(fā)送一次指定的selector消息,CADisplayLink類對應(yīng)的selector就會被調(diào)用一次。
    通常情況下,按照iOS設(shè)備屏幕的刷新率60次/秒,CADisplayLink在正常情況下會在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。但如果調(diào)用的方法比較耗時(shí),超過了屏幕刷新周期,就會導(dǎo)致跳過若干次回調(diào)調(diào)用機(jī)會。
// 1創(chuàng)建出displaylink對象
CADisplayLink *displyLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(reloop)];
// 2 將該對象加入循環(huán)中
[displyLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// 3再不需要時(shí)釋放(停止循環(huán))
[displyLink invalidate];
displyLink = nil;
  1. 使用Grand Central Dispatch (GCD)
    // 只執(zhí)行一次
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        // 執(zhí)行事件
        [self reloop];
    });
      
    // 重復(fù)執(zhí)行
    NSTimeInterval period = 1.0; //設(shè)置時(shí)間間隔
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //每秒執(zhí)行
    dispatch_source_set_event_handler(_timer, ^{
        //在這里執(zhí)行事件
        [self reloop];
    });
    dispatch_resume(_timer);

代碼實(shí)現(xiàn):

  1. 新建LaunchViewController類
    LaunchViewController.h
//
//  LaunchViewController.h
//  PandaiOSDemo
//
//  Created by shitianci on 16/6/30.
//  Copyright ? 2016年 Panda. All rights reserved.
//
#import <UIKit/UIKit.h>

@interface LaunchViewController : UIViewController

@property (strong, nonatomic)  UIImageView *launchImgView;

@end

LaunchViewController.m

//
//  LaunchViewController.m
//  PandaiOSDemo
//
//  Created by shitianci on 16/6/30.
//  Copyright ? 2016年 Panda. All rights reserved.
//

#import "LaunchViewController.h"

@interface LaunchViewController ()

@end

@implementation LaunchViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.launchImgView = [[UIImageView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.launchImgView setBackgroundColor:[UIColor greenColor]];
    
    [self.view addSubview:self.launchImgView];
    
    // 1、使用nstimer創(chuàng)建定時(shí)器
    // A.自動加入主循環(huán)
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(reloop) userInfo:nil repeats:NO];
    // 激活定時(shí)器
    // 激活定時(shí)器,這種方法已經(jīng)被廢棄
    //    [timer fire];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    // 在循環(huán)調(diào)用時(shí),必須手動釋放定時(shí)器,否則不必手動釋放
    //    [timer invalidate];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)reloop {
    NSLog(@"循環(huán)執(zhí)行");
    exit(-1);
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end
  1. 在AppDelegate中引用LaunchViewController.h
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen]bounds]];
    
//    [MBProgressHUD showHUDAddedTo:self.window animated:YES];
    
    // 新建一個(gè)ViewController
    LaunchViewController *launchVC = [[LaunchViewController alloc] init];
    self.window.rootViewController = launchVC;
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    
    //測試引用庫
//    PandaiOSLib *pil = [[PandaiOSLib alloc] init];
//    [pil showWithA:1];

    return YES;
}

參考

  1. iOS開發(fā),定時(shí)器的使用
  2. ViewController的生命周期分析和使用
  3. UIViewController的基本概念與生命周期

Panda
2016-07-01

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容