- 問題:從一個(gè)控制器push到另一個(gè)控制器時(shí),有時(shí)會出現(xiàn)卡頓的一種現(xiàn)象,如下
測試代碼里什么數(shù)據(jù)都沒有加載,所以排除是卡頓原因,view沒有漸變效果,而是瞬間變換的,loadView里創(chuàng)建新的UIView后沒有設(shè)置背景色(默認(rèn)是透明),所以切換時(shí)會看到上一頁的內(nèi)容。
解決:push的下一頁面的self.view設(shè)置一個(gè)顏色就可以解決
- 說到這里不免提下viewcontroller的生命周期問題
一、ViewController結(jié)構(gòu)
按結(jié)構(gòu)可以對所有ViewController分成兩類:1、主要用于展示內(nèi)容的ViewController,這種ViewController主要用于為用戶展示內(nèi)容,并與用戶交互,如UITableViewController,UIViewController。2、用于控制和顯示其他ViewController的ViewController。這種ViewController一般都是一個(gè)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會經(jīng)常被繼承,用來顯示不同的數(shù)據(jù)給用戶。而第二種很少被繼承,除非你真的需要自定義它。注:細(xì)心的同學(xué)應(yīng)該能發(fā)現(xiàn),在Xcode中新建一個(gè)ViewController時(shí),只可以選擇繼承自UIViewController和UITableViewController,而它們都是第一種。
二、Controller和View的生命周期
這里指的View是指Controller的View。它作為Controler的屬性,生命周期在Controller的生命周期內(nèi)。就是說你的Controller不能在view釋放前就釋放了。
圖2 ViewController生命周期
三、代碼組織(如何設(shè)計(jì)良好的viewcontroller)
ViewController生命周期中有那么多函數(shù),一個(gè)重要問題就是什么代碼該寫在什么地方。
- 1、init里不要出現(xiàn)創(chuàng)建view的代碼。良好的設(shè)計(jì),在init里應(yīng)該只有相關(guān)數(shù)據(jù)的初始化,而且這些數(shù)據(jù)都是比較關(guān)鍵的數(shù)據(jù)。init里不要掉self.view,否則會導(dǎo)致viewcontroller創(chuàng)建view。(因?yàn)関iew是lazyinit的)。
- 2、loadView中只初始化view,一般用于創(chuàng)建比較關(guān)鍵的view如tableViewController的tabView,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前),最好也不要初始化一些非關(guān)鍵的view。如果你是從nib文件中創(chuàng)建的viewController在這里一定要首先調(diào)用super的loadView方法,但建議不要重載這個(gè)方法。
- 3、viewDidLoad 這時(shí)候view已經(jīng)有了,最適合創(chuàng)建一些附加的view和控件了。有一點(diǎn)需要注意的是,viewDidLoad會調(diào)用多次(viewcontroller可能多次載入view,參見圖2)。
- 4、viewWillAppear 這個(gè)一般在view被添加到superview之前,切換動畫之前調(diào)用。在這里可以進(jìn)行一些顯示前的處理。比如鍵盤彈出,一些特殊的過程動畫(比如狀態(tài)條和navigationbar顏色)。
- 5、viewDidAppear 一般用于顯示后,在切換動畫后,如果有需要的操作,可以在這里加入相關(guān)代碼。
- 6、viewDidUnload iOS6中,viewDidUnload回調(diào)方法被Deprecated掉了
出現(xiàn)的問題
1、 (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 可以查找XIB中有沒有視圖view。如果有,則不會再走loadView。如果這個(gè)時(shí)候你的VC是沒有xib的,哪么顯然走這個(gè)方法后,是找不到任何view的,即self.view 仍為nil.然后,就跑loadview,這個(gè)時(shí)候會被觸發(fā),如果在loadView中,什么也不做,也不實(shí)例化一個(gè)View。哪么程序繼續(xù)跑到viewDidLoad里,如果這里還是沒有實(shí)例化VIEW。哪么這個(gè)VC就沒有視窗。在這里很多時(shí)侯會出現(xiàn)一個(gè)誤區(qū)(死循環(huán))。
- 死循環(huán)的原因
1、沒有XIB。
2、ViewController中的loadView方法中沒有做任何實(shí)例化self.view的操作。如:
-(void)loadView
{
寫了一大堆代碼,但最好并沒有執(zhí)行以下兩種方式中的其中一種。
//方式一:實(shí)例化時(shí)使用[supper loadView];
//方式二 : self.view = [UIView alloc]....
}
3、在viewDidLoad中調(diào)用了self.view。
只要這三個(gè)條件同時(shí)滿足,必定死循環(huán)。方式一時(shí),調(diào)用了[Supper LoadView] 這個(gè)時(shí)候由父類產(chǎn)生了一個(gè)(0,20,Width,height )。這里的寬高根據(jù)是IPAD,還是IPHONE不同而不同,但原點(diǎn)坐標(biāo)一定是(0,20)即去除狀態(tài)條。方式二,沒有對self.view作任可賦值,所以使得self.View = nil;
在條件二滿足的情況下,程序運(yùn)行到步驟三,這個(gè)時(shí)候,如果在這里調(diào)用了self.View。因?yàn)閟elf.View在步驟二中為空,所以又回調(diào)到了loadView來,但因loadView中沒有對self.View作實(shí)例化,于是在跑完loadView后,又繼續(xù)跑viewDidLoad,但因ViewDidLoad中又沒有實(shí)例化的情況下,使用了self.View.因此就出會現(xiàn)來回調(diào)用的現(xiàn)象。
2、initWithCoder不調(diào)用
initWithCoder 是一個(gè)類在IB中創(chuàng)建但在xocde中被實(shí)例化時(shí)被調(diào)用的.比如,通過IB創(chuàng)建一個(gè)controller的nib文件,然后在xcode中通過 initWithNibName來實(shí)例化這個(gè)controller,那么這個(gè)controller的initWithCoder會被調(diào)用.或者是一個(gè)view的nib文件,類似方法創(chuàng)建時(shí)調(diào)用initWithCoder
UIView的機(jī)制
initWithFrame方法是什么?
initWithFrame方法用來初始化并返回一個(gè)新的視圖對象,根據(jù)指定的CGRect(尺寸)。
當(dāng)然,其他UI對象,也有initWithFrame方法,但是,我們以UIView為例,來搞清楚initWithFrame方法。什么時(shí)候用initWithFrame方法?
簡單的說,我們用編程方式申明,創(chuàng)建UIView對象時(shí),使用initWithFrame方法。
在此,我們必須搞清楚,兩種方式來進(jìn)行初始化UIView。
1.使用 Interface Builder 方式。
這種方式,就是使用nib文件。通常我們說的“拖控件” 的方式。
實(shí)際編程中,我們?nèi)绻肐nterface Builder 方式創(chuàng)建了UIView對象。(也就是,用拖控件的方式)
那么,initWithFrame方法方法是不會被調(diào)用的。因?yàn)閚ib文件已經(jīng)知道如何初始化該View。(因?yàn)椋覀冊谕显搗iew的時(shí)候,就定義好了長、寬、背景等屬性)。
這時(shí)候,會調(diào)用initWithCoder方法,我們可以用initWithCoder方法來重新定義我們在nib中已經(jīng)設(shè)置的各項(xiàng)屬性。
這就是為什么使用initWithCoder:的原因,因?yàn)锽IDViewController.xib的view是BIDQuartzFunView類型,而不是UIView類型了,所以其實(shí)是從nib中加載對象實(shí)例。
- 使用編程方式。
就是我們聲明一個(gè)UIView的子類,進(jìn)行“手工”編寫代碼的方式。
實(shí)際編程中,我們使用編程方式下,來創(chuàng)建一個(gè)UIView或者創(chuàng)建UIView的子類。這時(shí)候,將調(diào)用initWithFrame方法,來實(shí)例化UIView。
特別注意,如果在子類中重載initWithFrame方法,必須先調(diào)用父類的initWithFrame方法。在對自定義的UIView子類進(jìn)行初始化操作。
比如:
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];// 先調(diào)用父類的initWithFrame方法
if (self) {
// 再自定義該類(UIView子類)的初始化操作。
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
[_scrollView setFrame:CGRectMake(0, 0, 320, 480)];
_scrollView.contentSize = CGSizeMake(320*3, 480);
[self addSubview:_scrollView];
}
return self;
}
在這里,我想,應(yīng)該對initWithFrame方法略知一二了。
當(dāng)我們所寫的程序里沒用用Nib文件(XIB)時(shí),用代碼控制視圖內(nèi)容,需要調(diào)用initWithFrame去初始化
- (id)initWithFrame:(CGRect)frame
{
if (self =[superinitWithFrame:frame]) {
// 初始化代碼
}
return self;
}
用于視圖加載nib文件,從nib中加載對象實(shí)例時(shí),使用 initWithCoder初始化這些實(shí)例對象
- (id)initWithCoder:(NSCoder*)coder
{
if (self =[superinitWithcoder:coder]) {
// 初始化代碼
}
return self;
}
1.initWithCoder: 對于.xib,當(dāng)你嵌入一個(gè)視圖對象到xib,視圖加載時(shí)默認(rèn)調(diào)用的是該方法;例如:假如創(chuàng)建的view來自nib,那么將會調(diào)用initWithCoder,由系統(tǒng)來調(diào)用,自己不能調(diào)用。
2.initWithFrame: 非.xib的手動編碼,視圖加載時(shí)默認(rèn)調(diào)用的是該方法。是由自己調(diào)用,來初始化對象的
問題:
1、初始化view時(shí)不管用什么方式,
//添加lifeview,檢測view生命周期
self.lifeView = [[LifeView alloc] initWithFrame:CGRectMake(10, 500, 200, 20)];
self.lifeView = [[LifeView alloc] init];
[self.view addSubview:self.lifeView];
都會先調(diào)用
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor yellowColor];
}
return self;
}
init只有view用init初始化時(shí)才會調(diào)用
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor redColor];
}
return self;
}