- 問題:從一個控制器push到另一個控制器時得湘,有時會出現(xiàn)卡頓的一種現(xiàn)象杖玲,如下
測試代碼里什么數(shù)據(jù)都沒有加載,所以排除是卡頓原因淘正,view沒有漸變效果摆马,而是瞬間變換的,loadView里創(chuàng)建新的UIView后沒有設置背景色(默認是透明)臼闻,所以切換時會看到上一頁的內(nèi)容。
解決:push的下一頁面的self.view設置一個顏色就可以解決
- 說到這里不免提下viewcontroller的生命周期問題
一囤采、ViewController結構
按結構可以對所有ViewController分成兩類:1述呐、主要用于展示內(nèi)容的ViewController,這種ViewController主要用于為用戶展示內(nèi)容蕉毯,并與用戶交互乓搬,如UITableViewController,UIViewController代虾。2进肯、用于控制和顯示其他ViewController的ViewController。這種ViewController一般都是一個ViewController的容器棉磨。如UINavigationController江掩,UITabbarController。它們都有一個屬性:viewControllers乘瓤。其中UINavigationController表示一種Stack式結構环形,push一個ViewController或pop一次,因此后一個ViewController一般會依賴前一個ViewController衙傀。而UITabbarController表示一個Array結構抬吟,各個ViewController是并列的。第一種ViewController會經(jīng)常被繼承差油,用來顯示不同的數(shù)據(jù)給用戶拗军。而第二種很少被繼承,除非你真的需要自定義它蓄喇。注:細心的同學應該能發(fā)現(xiàn)发侵,在Xcode中新建一個ViewController時,只可以選擇繼承自UIViewController和UITableViewController妆偏,而它們都是第一種刃鳄。
二、Controller和View的生命周期
這里指的View是指Controller的View钱骂。它作為Controler的屬性叔锐,生命周期在Controller的生命周期內(nèi)。就是說你的Controller不能在view釋放前就釋放了见秽。
圖2 ViewController生命周期
三陪毡、代碼組織(如何設計良好的viewcontroller)
ViewController生命周期中有那么多函數(shù)米母,一個重要問題就是什么代碼該寫在什么地方。
- 1毡琉、init里不要出現(xiàn)創(chuàng)建view的代碼。良好的設計,在init里應該只有相關數(shù)據(jù)的初始化推穷,而且這些數(shù)據(jù)都是比較關鍵的數(shù)據(jù)既绩。init里不要掉self.view,否則會導致viewcontroller創(chuàng)建view丐谋。(因為view是lazyinit的)芍碧。
- 2、loadView中只初始化view号俐,一般用于創(chuàng)建比較關鍵的view如tableViewController的tabView泌豆,UINavigationController的navgationBar,不可掉用view的getter(在掉super loadView前)吏饿,最好也不要初始化一些非關鍵的view踪危。如果你是從nib文件中創(chuàng)建的viewController在這里一定要首先調(diào)用super的loadView方法,但建議不要重載這個方法猪落。
- 3贞远、viewDidLoad 這時候view已經(jīng)有了,最適合創(chuàng)建一些附加的view和控件了笨忌。有一點需要注意的是蓝仲,viewDidLoad會調(diào)用多次(viewcontroller可能多次載入view,參見圖2)。
- 4杂曲、viewWillAppear 這個一般在view被添加到superview之前庶艾,切換動畫之前調(diào)用。在這里可以進行一些顯示前的處理擎勘。比如鍵盤彈出咱揍,一些特殊的過程動畫(比如狀態(tài)條和navigationbar顏色)。
- 5棚饵、viewDidAppear 一般用于顯示后煤裙,在切換動畫后,如果有需要的操作噪漾,可以在這里加入相關代碼硼砰。
- 6、viewDidUnload iOS6中欣硼,viewDidUnload回調(diào)方法被Deprecated掉了
出現(xiàn)的問題
1题翰、 (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 可以查找XIB中有沒有視圖view。如果有诈胜,則不會再走loadView豹障。如果這個時候你的VC是沒有xib的,哪么顯然走這個方法后焦匈,是找不到任何view的血公,即self.view 仍為nil.然后,就跑loadview,這個時候會被觸發(fā)缓熟,如果在loadView中累魔,什么也不做,也不實例化一個View够滑。哪么程序繼續(xù)跑到viewDidLoad里垦写,如果這里還是沒有實例化VIEW。哪么這個VC就沒有視窗版述。在這里很多時侯會出現(xiàn)一個誤區(qū)(死循環(huán))梯澜。
- 死循環(huán)的原因
1、沒有XIB渴析。
2晚伙、ViewController中的loadView方法中沒有做任何實例化self.view的操作。如:
-(void)loadView
{
寫了一大堆代碼俭茧,但最好并沒有執(zhí)行以下兩種方式中的其中一種咆疗。
//方式一:實例化時使用[supper loadView];
//方式二 : self.view = [UIView alloc]....
}
3、在viewDidLoad中調(diào)用了self.view母债。
只要這三個條件同時滿足午磁,必定死循環(huán)尝抖。方式一時,調(diào)用了[Supper LoadView] 這個時候由父類產(chǎn)生了一個(0,20,Width,height )迅皇。這里的寬高根據(jù)是IPAD昧辽,還是IPHONE不同而不同,但原點坐標一定是(0,20)即去除狀態(tài)條登颓。方式二搅荞,沒有對self.view作任可賦值,所以使得self.View = nil;
在條件二滿足的情況下框咙,程序運行到步驟三咕痛,這個時候,如果在這里調(diào)用了self.View喇嘱。因為self.View在步驟二中為空茉贡,所以又回調(diào)到了loadView來,但因loadView中沒有對self.View作實例化者铜,于是在跑完loadView后腔丧,又繼續(xù)跑viewDidLoad,但因ViewDidLoad中又沒有實例化的情況下王暗,使用了self.View.因此就出會現(xiàn)來回調(diào)用的現(xiàn)象悔据。
2庄敛、initWithCoder不調(diào)用
initWithCoder 是一個類在IB中創(chuàng)建但在xocde中被實例化時被調(diào)用的.比如,通過IB創(chuàng)建一個controller的nib文件,然后在xcode中通過 initWithNibName來實例化這個controller,那么這個controller的initWithCoder會被調(diào)用.或者是一個view的nib文件俗壹,類似方法創(chuàng)建時調(diào)用initWithCoder
UIView的機制
initWithFrame方法是什么?
initWithFrame方法用來初始化并返回一個新的視圖對象,根據(jù)指定的CGRect(尺寸)藻烤。
當然绷雏,其他UI對象,也有initWithFrame方法怖亭,但是涎显,我們以UIView為例,來搞清楚initWithFrame方法兴猩。什么時候用initWithFrame方法期吓?
簡單的說,我們用編程方式申明倾芝,創(chuàng)建UIView對象時讨勤,使用initWithFrame方法。
在此晨另,我們必須搞清楚潭千,兩種方式來進行初始化UIView。
1.使用 Interface Builder 方式借尿。
這種方式刨晴,就是使用nib文件屉来。通常我們說的“拖控件” 的方式。
實際編程中狈癞,我們?nèi)绻肐nterface Builder 方式創(chuàng)建了UIView對象茄靠。(也就是,用拖控件的方式)
那么蝶桶,initWithFrame方法方法是不會被調(diào)用的嘹黔。因為nib文件已經(jīng)知道如何初始化該View。(因為莫瞬,我們在拖該view的時候儡蔓,就定義好了長、寬疼邀、背景等屬性)喂江。
這時候,會調(diào)用initWithCoder方法旁振,我們可以用initWithCoder方法來重新定義我們在nib中已經(jīng)設置的各項屬性获询。
這就是為什么使用initWithCoder:的原因,因為BIDViewController.xib的view是BIDQuartzFunView類型拐袜,而不是UIView類型了吉嚣,所以其實是從nib中加載對象實例。
- 使用編程方式蹬铺。
就是我們聲明一個UIView的子類尝哆,進行“手工”編寫代碼的方式。
實際編程中甜攀,我們使用編程方式下秋泄,來創(chuàng)建一個UIView或者創(chuàng)建UIView的子類。這時候规阀,將調(diào)用initWithFrame方法恒序,來實例化UIView。
特別注意谁撼,如果在子類中重載initWithFrame方法歧胁,必須先調(diào)用父類的initWithFrame方法。在對自定義的UIView子類進行初始化操作厉碟。
比如:
- (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;
}
在這里,我想墨榄,應該對initWithFrame方法略知一二了玄糟。
當我們所寫的程序里沒用用Nib文件(XIB)時,用代碼控制視圖內(nèi)容,需要調(diào)用initWithFrame去初始化
- (id)initWithFrame:(CGRect)frame
{
if (self =[superinitWithFrame:frame]) {
// 初始化代碼
}
return self;
}
用于視圖加載nib文件袄秩,從nib中加載對象實例時阵翎,使用 initWithCoder初始化這些實例對象
- (id)initWithCoder:(NSCoder*)coder
{
if (self =[superinitWithcoder:coder]) {
// 初始化代碼
}
return self;
}
1.initWithCoder: 對于.xib逢并,當你嵌入一個視圖對象到xib,視圖加載時默認調(diào)用的是該方法郭卫;例如:假如創(chuàng)建的view來自nib砍聊,那么將會調(diào)用initWithCoder,由系統(tǒng)來調(diào)用贰军,自己不能調(diào)用玻蝌。
2.initWithFrame: 非.xib的手動編碼,視圖加載時默認調(diào)用的是該方法词疼。是由自己調(diào)用俯树,來初始化對象的
問題:
1、初始化view時不管用什么方式贰盗,
//添加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初始化時才會調(diào)用
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = [UIColor redColor];
}
return self;
}