iOS initWithFrame: 與initWithCoder:區(qū)別

?當(dāng)從代碼實例化UIView的時候,initWithFrame會執(zhí)行;

當(dāng)從文件加載UIView的時候,initWithCoder會執(zhí)行。

您定義的每個新的視圖對象都應(yīng)該包含initWithFrame:初始化方法。該方法負(fù)責(zé)在創(chuàng)建對象時對類進行初始化,使之處于已知的狀態(tài)。在通過代碼創(chuàng)建您的視圖實例時,需要使用這個方法。

程序清單1-1顯示了標(biāo)準(zhǔn)的initWithFrame:方法的一個框架實現(xiàn)。該實現(xiàn)首先調(diào)用繼承自超類的實現(xiàn),然后初始化類的實例變量和狀態(tài)信息,最后返回初始化完成的對象。您通常需要首先執(zhí)行超類的實現(xiàn),以便在出現(xiàn)問題時可以簡單地終止自己的初始化代碼,返回nil。

程序清單1-1初始化一個視圖的子類


- (id)initWithFrame:(CGRect)aRect {

self = [super initWithFrame:aRect];

if (self) {

// setup the initial properties of the view

... }

return self;

}


如果您從nib文件中裝載定制視圖類的實例,則需要知道:在iPhone OS中,裝載nib的代碼并不通過initWithFrame:方法來實例化新的視圖對象,而是通過NSCoding協(xié)議定義的initWithCoder:方法來進行。

即使您的視圖采納了NSCoding協(xié)議,Interface Builder也不知道它的定制屬性,因此不知道如何將那些屬性編碼到nib文件中。所以,當(dāng)您從nib文件裝載定制視圖時,initWithCoder:方法不具有進行正確初始化所需要的信息。為了解決這個問題,您可以在自己的類中實現(xiàn)awakeFromNib方法,特別用于從nib文件裝載的定制類。

程序清單1-2顯示了drawRect:方法的一個簡單實現(xiàn),即在視圖邊界描畫一個10像素寬的紅色邊界。由于UIKit描畫操作的實現(xiàn)也是基于Quartz,所以您可以像下面這樣混合使用不同的描畫調(diào)用來得到期望的結(jié)果。


程序清單1-2一個描畫方法

- (void)drawRect:(CGRect)rect {

CGContextRef context = UIGraphicsGetCurrentContext();

CGRectmyFrame = self.bounds;

CGContextSetLineWidth(context, 10);

[[UIColor redColor] set];

UIRectFrame(myFrame);

}


如果您能確定自己的描畫代碼總是以不透明的內(nèi)容覆蓋整個視圖的表面,則可以將視圖的opaque屬性聲明設(shè)置為YES,以提高描畫代碼的總體效率。當(dāng)您將視圖標(biāo)識為不透明時,UIKit會避免對該視圖正下方的內(nèi)容進行描畫。這不僅減少了描畫開銷的時間,而且減少內(nèi)容合成需要的工作。然而,只有當(dāng)您能確定視圖提供的內(nèi)容為不透明時,才能將這個屬性設(shè)置為YES;如果您不能保證視圖內(nèi)容總是不透明,則應(yīng)該將它設(shè)置為NO。

提高描畫性能(特別是在滾動過程)的另一個方法是將視圖的clearsContextBeforeDrawing屬性設(shè)置為NO。當(dāng)這個屬性被設(shè)置為YES時,UIKIt會在調(diào)用drawRect:方法之前,把即將被該方法更新的區(qū)域填充為透明的黑色。將這個屬性設(shè)置為NO可以取消相應(yīng)的填充操作,而由應(yīng)用程序負(fù)責(zé)完全重畫傳給drawRect:方法的更新矩形中的部分。這樣的優(yōu)化在滾動過程中通常是一個好的折衷。

當(dāng)控件從xib或storyboard中加載的時候,情況就變得復(fù)雜了,首先我們知道有initWithCoder方法,該方法會在對象被反序列化的時候調(diào)用,,一般是在xib或storyboard中寫一個View,然后讓系統(tǒng)來完成反序列化的工作,此時在initWithCoder調(diào)用之后,awakeFromNib方法也會被執(zhí)行,既然在awakeFromNib方法里也能做初始化操作,那我們?nèi)绾尉駬?

一般來說要盡量在initWithCoder中做初始化操作,畢竟這是最合理的地方,只要你的控件支持序列化,那么它就能在任何被反序列化的時候執(zhí)行初始化操作,這里適合做全局?jǐn)?shù)據(jù)、狀態(tài)的初始化工作,也適合手動添加子視圖。

awakeFromNib相較于initWithCoder的優(yōu)勢是:當(dāng)awakeFromNib執(zhí)行的時候,各種IBOutlet也都連接好了;而initWithCoder調(diào)用的時候,雖然子視圖已經(jīng)被添加到視圖層級中,但是還沒有引用。如果你是基于xib或storyboard創(chuàng)建的控件,那么你可能需要對IBOutlet連接的子控件進行初始化工作,這種情況下,你只能在awakeFromNib里進行處理。同時xib或storyboard對靈活性是有打折的,因為它們創(chuàng)建的代碼無法被繼承,所以當(dāng)你選擇用xib或storyboard來實現(xiàn)一個控件的時候,你已經(jīng)不需要對靈活性有很高的要求了,唯一要做的是要保證用戶一定是通過xib創(chuàng)建的此控件,否則可能是一個空的視圖,可以在initWithFrame里放置一個斷言或者異常來通知控件的用戶。

最后還要注意視圖層級的問題,比如你要給View放置一個背景,你可能會在initWithCoder或awakeFromNib中這樣寫:

1?[self?addSubview:self.backgroundView];//通過懶加載一個背景View,然后添加到視圖層級上

你的本意是在控件的最下面放置一個背景,卻有可能將這個背景覆蓋到控件的最上方,原因是用戶可能會在xib里寫入這個控件,然后往它上面添加一些子視圖,這樣一來,用戶添加的這些子視圖會在你添加背景之前先進入視圖層級,你的背景被添加后就擋住了用戶的子視圖。如果你想支持用戶的這種操作,可以把addSubview替換成insertSubview:atIndex:。

同時支持從代碼和文件中加載

如果你要同時支持initWithFrame和initWithCoder,那么你可以提供一個commonInit方法來做統(tǒng)一的初始化:


-?(id)initWithCoder:(NSCoder?*)aDecoder?{

self?=?[superinitWithCoder:aDecoder];

if(self)?{

[self?commonInit];

}

returnself;

}

-?(id)initWithFrame:(CGRect)frame?{

self?=?[superinitWithFrame:frame];

if(self)?{

[self?commonInit];

}

returnself;

}

-?(void)commonInit?{

//?do?something?...

}


awakeFromNib方法里就不要再去調(diào)用commonInit了。

支持sizeToFit

如果你的控件對尺寸有嚴(yán)格的限定,比如有一個統(tǒng)一的寬高比或者是固定尺寸,那么最好能實現(xiàn)系統(tǒng)給出的約定成俗的接口。

sizeToFit用在基于frame布局的情況下,由你的控件去實現(xiàn)sizeThatFits:方法:


-?(CGSize)sizeThatFits:(CGSize)size?{

CGSize?fitSize?=?[supersizeThatFits:size];

fitSize.height?+=?self.label.frame.size.height;

//如果是固定尺寸,就像UISwtich那樣返回一個固定Size就OK了

returnfitSize;

}


然后在外部調(diào)用該控件的sizeToFit方法,這個方法內(nèi)部會自動調(diào)用sizeThatFits并更新自身的Size:

[self.customView?sizeToFit];

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容