關于 initWithNibName 和 loadNibNamed 的區別和聯系:
iOS initWithFrame: 與initWithCoder:區別
http://www.lxweimin.com/p/d90c7bc20132
?當從代碼實例化UIView的時候,initWithFrame會執行;
當從文件加載UIView的時候,initWithCoder會執行。
您定義的每個新的視圖對象都應該包含initWithFrame:初始化方法。該方法負責在創建對象時對類進行初始化,使之處于已知的狀態。在通過代碼創建您的視圖實例時,需要使用這個方法。
程序清單1-1顯示了標準的initWithFrame:方法的一個框架實現。該實現首先調用繼承自超類的實現,然后初始化類的實例變量和狀態信息,最后返回初始化完成的對象。您通常需要首先執行超類的實現,以便在出現問題時可以簡單地終止自己的初始化代碼,返回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協議定義的initWithCoder:方法來進行。
即使您的視圖采納了NSCoding協議,Interface Builder也不知道它的定制屬性,因此不知道如何將那些屬性編碼到nib文件中。所以,當您從nib文件裝載定制視圖時,initWithCoder:方法不具有進行正確初始化所需要的信息。為了解決這個問題,您可以在自己的類中實現awakeFromNib方法,特別用于從nib文件裝載的定制類。
程序清單1-2顯示了drawRect:方法的一個簡單實現,即在視圖邊界描畫一個10像素寬的紅色邊界。由于UIKit描畫操作的實現也是基于Quartz,所以您可以像下面這樣混合使用不同的描畫調用來得到期望的結果。
程序清單1-2一個描畫方法
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGRectmyFrame = self.bounds;
CGContextSetLineWidth(context, 10);
[[UIColor redColor] set];
UIRectFrame(myFrame);
}
如果您能確定自己的描畫代碼總是以不透明的內容覆蓋整個視圖的表面,則可以將視圖的opaque屬性聲明設置為YES,以提高描畫代碼的總體效率。當您將視圖標識為不透明時,UIKit會避免對該視圖正下方的內容進行描畫。這不僅減少了描畫開銷的時間,而且減少內容合成需要的工作。然而,只有當您能確定視圖提供的內容為不透明時,才能將這個屬性設置為YES;如果您不能保證視圖內容總是不透明,則應該將它設置為NO。
提高描畫性能(特別是在滾動過程)的另一個方法是將視圖的clearsContextBeforeDrawing屬性設置為NO。當這個屬性被設置為YES時,UIKIt會在調用drawRect:方法之前,把即將被該方法更新的區域填充為透明的黑色。將這個屬性設置為NO可以取消相應的填充操作,而由應用程序負責完全重畫傳給drawRect:方法的更新矩形中的部分。這樣的優化在滾動過程中通常是一個好的折衷。
當控件從xib或storyboard中加載的時候,情況就變得復雜了,首先我們知道有initWithCoder方法,該方法會在對象被反序列化的時候調用,,一般是在xib或storyboard中寫一個View,然后讓系統來完成反序列化的工作,此時在initWithCoder調用之后,awakeFromNib方法也會被執行,既然在awakeFromNib方法里也能做初始化操作,那我們如何抉擇?
一般來說要盡量在initWithCoder中做初始化操作,畢竟這是最合理的地方,只要你的控件支持序列化,那么它就能在任何被反序列化的時候執行初始化操作,這里適合做全局數據、狀態的初始化工作,也適合手動添加子視圖。
awakeFromNib相較于initWithCoder的優勢是:當awakeFromNib執行的時候,各種IBOutlet也都連接好了;而initWithCoder調用的時候,雖然子視圖已經被添加到視圖層級中,但是還沒有引用。如果你是基于xib或storyboard創建的控件,那么你可能需要對IBOutlet連接的子控件進行初始化工作,這種情況下,你只能在awakeFromNib里進行處理。同時xib或storyboard對靈活性是有打折的,因為它們創建的代碼無法被繼承,所以當你選擇用xib或storyboard來實現一個控件的時候,你已經不需要對靈活性有很高的要求了,唯一要做的是要保證用戶一定是通過xib創建的此控件,否則可能是一個空的視圖,可以在initWithFrame里放置一個斷言或者異常來通知控件的用戶。
最后還要注意視圖層級的問題,比如你要給View放置一個背景,你可能會在initWithCoder或awakeFromNib中這樣寫:
1?[self?addSubview:self.backgroundView];//通過懶加載一個背景View,然后添加到視圖層級上
你的本意是在控件的最下面放置一個背景,卻有可能將這個背景覆蓋到控件的最上方,原因是用戶可能會在xib里寫入這個控件,然后往它上面添加一些子視圖,這樣一來,用戶添加的這些子視圖會在你添加背景之前先進入視圖層級,你的背景被添加后就擋住了用戶的子視圖。如果你想支持用戶的這種操作,可以把addSubview替換成insertSubview:atIndex:。
同時支持從代碼和文件中加載
如果你要同時支持initWithFrame和initWithCoder,那么你可以提供一個commonInit方法來做統一的初始化:
-?(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方法里就不要再去調用commonInit了。
支持sizeToFit
如果你的控件對尺寸有嚴格的限定,比如有一個統一的寬高比或者是固定尺寸,那么最好能實現系統給出的約定成俗的接口。
sizeToFit用在基于frame布局的情況下,由你的控件去實現sizeThatFits:方法:
-?(CGSize)sizeThatFits:(CGSize)size?{
CGSize?fitSize?=?[supersizeThatFits:size];
fitSize.height?+=?self.label.frame.size.height;
//如果是固定尺寸,就像UISwtich那樣返回一個固定Size就OK了
returnfitSize;
}
然后在外部調用該控件的sizeToFit方法,這個方法內部會自動調用sizeThatFits并更新自身的Size:
[self.customView?sizeToFit];
iOS - layoutSubviews、drawRect、awakeFromNib和 loadNibNamed解釋
http://blog.csdn.net/xiaohe901216/article/details/50380625
awakeFromNib
當.nib文件被加載的時候,會發送一個awakeFromNib的消息到.nib文件中的每個對象,每個對象都可以定義自己的 awakeFromNib函數來響應這個消息,執行一些必要的操作。也就是說通過nib文件創建view對象是執行awakeFromNib 。
viewDidLoad
當view對象被加載到內存是就會執行viewDidLoad,所以不管通過nib文件還是代碼的方式創建對象都會執行viewDidLoad。
layoutSubviews
layoutSubviews方便數據計算,drawRect方便視圖重繪。
layoutSubviews在以下情況下會被調用:
1、init初始化不會觸發layoutSubviews。
2、addSubview會觸發layoutSubviews。
3、設置view的Frame會觸發layoutSubviews,當然前提是frame的值設置前后發生了變化。
4、滾動一個UIScrollView會觸發layoutSubviews。
5、旋轉Screen會觸發父UIView上的layoutSubviews事件。
6、改變一個UIView大小的時候也會觸發父UIView上的layoutSubviews事件。
7、直接調用setLayoutSubviews。
drawRect
drawRect在以下情況下會被調用:
1、如果在UIView初始化時沒有設置rect大小,將直接導致drawRect不被自動調用。drawRect 掉用是在Controller->loadView,?Controller->viewDidLoad?兩方法之后掉用的.所以不用擔心在 控制器中,這些View的drawRect就開始畫了.這樣可以在控制器中設置一些值給View(如果這些View?draw的時候需要用到某些變量 值).
2、該方法在調用sizeToFit后被調用,所以可以先調用sizeToFit計算出size。然后系統自動調用drawRect:方法。
3、通過設置contentMode屬性值為UIViewContentModeRedraw。那么將在每次設置或更改frame的時候自動調用drawRect:。
4、直接調用setNeedsDisplay,或者setNeedsDisplayInRect:觸發drawRect:,但是有個前提條件是rect不能為0。
以上1,2推薦;而3,4不提倡 ? drawRect方法使用注意點:
1、 若使用UIView繪圖,只能在drawRect:方法中獲取相應的contextRef并繪圖。如果在其他方法中獲取將獲取到一個invalidate 的ref并且不能用于畫圖。drawRect:方法不能手動顯示調用,必須通過調用setNeedsDisplay?或 者?setNeedsDisplayInRect,讓系統自動調該方法。
2、若使用calayer繪圖,只能在drawInContext:?中(類似于drawRect)繪制,或者在delegate中的相應方法繪制。同樣也是調用setNeedDisplay等間接調用以上方法
3、若要實時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來調用setNeedsDisplay實時刷新屏幕
initWithNibName
initWithNibName這個方法是在controller的類在IB中創建,但是通過Xcode實例化controller的時候用的.
initWithCoder
initWithCoder 是一個類在IB中創建但在xocde中被實例化時被調用的.比如,通過IB創建一個controller的nib文件,然后在xcode中通過 initWithNibName來實例化這個controller,那么這個controller的initWithCoder會被調用.或者是一個view的nib文件,類似方法創建時調用initWithCoder
關于 initWithNibName 和 loadNibNamed 的區別和聯系
之所以要把這兩者來一起講,我覺的我也有點困惑,到底用那種?其實真正搞清楚了他們之間的差別,就不會這么迷惘了。因為這兩個方法,根本就不是一路貨色。
既然,是要說明這2個方法,那就著重將區別吧。
但是第一步,還是要羅嗦一下,他們的聯系:可以使用此方法加載用戶界面(xib文件)到我們的代碼中,這樣,可以通過操作這個加載進來的(xib)對象,來操作xib文件內容。
下面進入主題,談區別:
1. ShowViewController的initWithNibName方法
ShowViewController * showMessage = [[ShowViewController alloc]
initWithNibName:@"ShowViewController" bundle:nil];
self.showViewController = showMessage;
2.VideoCellController的loadNibNamed方法
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:@"SaveViewController"
owner:self options:nil] ;
self.showViewController = [nib lastObject];
[nib objectAtIndex:0];
總結:
只看他們初始化,那可能感覺是一樣的。但是如果,打開分別看xib的關系的時候,才恍然大悟,原來他們的集成類都不一樣。
1. initWithNibName要加載的xib的類為我們定義的視圖控制器類
2.加載方式不同
initWithNibName方法:是延遲加載,這個View上的控件是 nil 的,只有到 需要顯示時,才會不是 nil
loadNibNamed方法:即時加載,用該方法加載的xib對象中的各個元素都已經存在。
(認真理解這句幫規:when using loadNibNamed:owner:options:, the File's Owner should be NSObject, the main view should be your class type, and all outlets should be hooked up to the view, not the File's Owner.)
initWithCoder和initWithFrame的區別
initWithoder 是當從nib文件中加載對象的時候會調用,比如你的view來自nib那么就會調用這個view的這個函數。(由框架調用)
initWithFrame (是由用戶調用,來初始化對象的)
//當你通過storyboared或者xib中創建控件就會調用這個方法
- (id)initWithCoder:(NSCoder *)aDecoder