xib、stoaryboard详解
一、xib、sb簡介
1、xib執行效率的確沒有代碼效率高,因為加載要多一步——把xib文件加載到內存中
2、SB還會省去很多頁面跳轉之間的膠水代碼(segue),甚至不用寫代碼就能實現在各個頁面中切換,tableView的cell可以直接拖到tableView里,可以給tableView添加header、footer,可以添加手勢、設置代理、size classes使得適配變得更加容易、xib也使得國際化變得很容易、可以通過代碼給xib動態加入屬性...這些東西,有些根本不用寫代碼,有些只需寫極少量代碼就能實現。
二、xib文件的使用
1.基于UIViewController子類的xib的使用
這種情況下使用很簡單,對VC直接alloc,init就可以,VC會自動去找自己對應的xib文件,即使我們自定義了一些init方法,也不需要對加載他的xib做處理,系統會自動幫我們找是否有與其對應的xib文件,例如我們有這樣一個初始化方法:
- (instancetype)initWithCustemData:(id)aData;
我們在創建VC實例的時候可以直接調用這個函數,不用理會xib文件的問題,我們的父類在初始化的時候去自動幫我們找與之對應的xib文件,那么問題來了,父類怎么知道我有沒有xib文件呢?是這樣,父類會判斷有沒有和我們這個要初始化的VC相同名字的xib文件,如果有就會加載該xib文件,如果沒有,父類就認為我們該VC沒有xib文件,就會走正常的init方法。
加載xib的init方法是:
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
如果一旦我們的VC類的名字與對應的xib文件名字不同的時候,我們就必須調用這個初始化方法來創建VC實例了;
[[ViewController alloc] initWithNibName:@"xxx" bundle:[NSBundle mainBundle]]
xxx的地方填那個與我們VC類名不同的那個xib文件的名字,不過一般情況下,我們的VC都與xib保持相同的名字,這里只是想說明,如果名字不同也是可以的。
2.基于UIView子類的xib的使用
TestView *tView = [[NSBundle mainBundle] loadNibNamed:@"TestView" owner:self options:nil][0];
上述代碼再次說明xib文件是資源文件,放在main bundle中,@"TestView"是xib文件的名稱,后面兩個參數暫時不用了解,就固定傳self和nil就行,值得說的是,loadNibNamed: owner: options方法返回的是一個數組,而不直接是對象,這是考慮到了Mac開發會有多個對象返回的情況,在iOS開發中就只有一個,固定取[0]就行。
注:一般的UIView對象,代碼初始化的時候都會調用initWithFrame:方法,但是用xib創建的UIView對象是不會調用此方法的,因為該對象的Frame在xib文件中就可以確定了。以xib的形式保存控件對象的過程其實叫做固化(archive),通過xib文件創建控件的過程叫做解固(unarchive),固化是iOS持久化的一種比較好的解決方案,以后有機會會說說iOS持久化的各種方式的優劣,這里不再深入,而與固化相關的初始化函數是:
- (instancetype)initWithCoder:(NSCoder *)aDecoder
所以,當以xib創建UIView對象的時候這個函數會調用,之前在initWithFrame:中要做的事情,可以放在initWithCoder:中,或者放在:
- (void)awakeFromNib
{
? ? [super awakeFromNib];
? ? //...
}
該函數會在initWithCoder:后調用,從名字我們就能看出,這個函數的觸發時機是控件已經從xib文件中“解固”之后,兩個函數之間的關系有點像VC的loadView和viewDidLoad之間的關系。
三、SB文件的使用
由于SB文件與VC一般是一對多的關系,所以我們不僅要知道即將創建的這個VC的實例對象是加載的哪個SB,而且還要知道加載的是該SB中的哪個具體的VC。
SecVC *secVC = [[UIStoryboard storyboardWithName:@"Demo" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"SecVC"];
@"Demo"參數代表SB的文件名字,@"SecVC"代表該VC在SB中的ID,具體使用會在下面的文章中提到。個人建議,該ID最好與類名相同,這樣便于將實例化的方法封在基類中。
四、xib布局系統詳解
1、xib右側標簽說明
第一個標簽(一頁紙):
主要介紹了xib文件的一些基本信息,一般不用修改。是否開啟auto layout、size classes、launch screen等功能在interface builder document菜單下,Localization菜單是與國際化相關的,這幾個功能以后會詳細介紹。
第二個標簽(問號)
是一些幫助說明之類的東西,可以忽略,應用中不會用到
第三個標簽(文檔)
1)、該標簽中最經常用到的就是Custom Class菜單中的Class,一般就是將你的類與xib文件掛鉤
2)、User Define Runtime Attribute是一個不經常用,但是很有用的功能。
3)、其他菜單都不常用,其中Accessibility是支持給殘疾人開發更利于他們使用的app的時候用到的。
第四個標簽(尖角向下五邊形)
這個是屬性標簽,是xib中最重要的一個標簽,對應的是一個控件的可設置的一些屬性,根據你選擇的控件不同,屬性標簽中呈現出來的東西也是不同的。
1)、設置控件的顏色屬性時通過Opacity改變顏色的透明度。注意:改變顏色的透明度有時候很重要,它與直接改變view的alpha產生的效果不同,改變view的alpha,它的subView的alpha也會改變,而改變顏色的alpha,是不影響自己的subView的。
2)、UIButton等控件是有不同狀態的:Default、Highlighted、Selected、Disabled,可以針對不同的狀態設置不同的屬性值,還可以設置shadow、edge等屬性,十分方便。
第五個標簽(豎著的尺子)
這是與控件frame相關的標簽,該標簽里的屬性隨著控件類型不同而變化,最典型的例子是Cell,是否開啟auto layout也會影響該標簽中的屬性,但一般常用的就是設置x、y、Width、Height,我們給一個控件添加的layout約束也都在這里顯示。
第六個標簽(向右的箭頭)
該標簽主要負責xib文件和類的源文件交互用,都是“連線”相關的操作,例如UITableView的delegate與datasoutce,IBAction、IBOutlet等相關,如果我們用了xib的頁面產生了莫名其妙的crash那你就要看看是不是自己的“連線”有問題了。
2、中間右下角標簽說明:
第一個圖標(文檔中間一個向下的箭頭)
這是xcode7在iOS9中新加入的功能——stack view,相當于一個容器view用來統一管理他所有subView的約束,其實普通的UIView也可以作為容器view來管理其subView的約束,我們之前做復雜UI顯示邏輯的時候往往也會放一個背景的容器view,stack view就是起到這個作用,意義不是很大,它做的事情UIView也可以做,但是他的優勢在于:可以通過設置屬性的方式讓系統自動添加對其subView的約束,而且該view是不渲染在頁面上的,對它設置背景色等屬性是無效的。
第二個圖標(左側豎線)
用于添加多個控件間對齊關系,從上到下依次是左對齊、右對齊、上對齊、下對齊、水平對齊、豎直對齊,這些現在都是灰色的不能選擇,只有同時選中多個控件,他們才是可用的,或者先選擇一個控件,然后按住control拖動到另一個控件上,就會彈出一個控件對齊的窗口,可以在里面設置兩個控件的對齊關系。下面兩個是相對于superView設置水平、豎直居中,選中單個控件就可以設置
第三個圖標(兩條豎線中間方框)
用于對單個控件設置約束,說明如下:
1)、上面的四個框分別填寫上下左右的約束。
2)、Constrain to margins選項的解釋:當拖動一個控件到另一個控件里時,作為super的控件會有幾條參考線(藍色虛線),上下左右四個方向的邊緣會有,水平、豎直的中心處會有。若勾選Constrain to margins實際super與sub之間的參考邊緣就是這些參考線,而不是實際的super的frame的邊緣,如果我們不勾選的話就是以frame的邊緣為參考。
3)、Equal Widths、Equal Heights是與其他控件保持相同的寬高,默認是灰色不可用狀態,只有選擇兩個以上的控件,才可使設置,同樣也可以先選中一個控件,按住control拖動,彈出的窗口中也有該選項。
4)、Aspect Ratio 是設置自身的寬高比的。
5)、Align選項同樣是設置兩個以上控件的對齊關系的。
6)、Update Frames一般是用來更新frame的。我們設置的約束如果與當前控件的frame產生沖突的時候就要解決沖突,要么修改約束,要么修改frame,最后使系統可以沒有歧義的確定UI布局。
第四個圖標(兩條豎線中間正三角)
1)、該圖標就是為了解決沖突而產生的,以更新frame,添加約束、刪除約束等等方式去解決frame與約束間的沖突。
2)、當你設置約束發生錯誤,或者不知道怎么設置的時候Clear Constraints會清除所有約束,讓你重新設置。
3、使用auto layout時為什么代碼修改frame不生效?
不使用auto layout時是可以在viewDidLoad:里設置frame的,一旦開啟了auto layout,就要注意,通常在viewDidLoad:中設置frame就不再生效,因為iOS5加入的viewWillLayoutSubviews會在viewDidLoad之后調用,而該函數會在執行的時候去加載該文件對應的xib設置的約束,就是說在viewDidLoad:中設置frame的時機太早了,沒有生效就又改成了xib中的樣子,而且在viewWillLayoutSubviews中修改frame也是不生效的,那么,如何才能用代碼修改布局生效?
方法一:
在viewDidLayoutSubviews中修改frame,這是最簡單的方法。
方法二:
選中xib中的約束(藍色線段,左邊欄Constants里的item)像拖動其他控件一樣,將其拖動成為IBOutlet的屬性或全局變量
4、Files Owner。
Files Owner指這個xib文件的所屬文件是誰,簡單的說是xib文件和誰建立起交互,用戶通與該xib呈現的頁面進行交互的時候,誰來處理背后的邏輯。具體來講xib文件能拖動“連線”到哪個源文件中去建立IBAction、IBOutlet、delegate、datasource等。
一般基于View創建的xib的Files Owner都指定為一個VC==我說不用!。基于VC創建的xib,創建的時候系統就已經把該xib文件的Files Owner指向了該VC,一般這種情況就不對Files Owner做修改了。
5、Files Owner的應用舉例
有這樣一個場景,VC中有一個textfield要設置inputAccessoryView屬性,該屬性的view顯示起來很復雜,有多個按鈕,每個按鈕對應不同的事件。
一般的做法是用代碼寫一個這樣的view賦值給inputAccessoryView屬性,其實這個例子可以用xib實現的更優雅,不用寫代碼就可以完成(當然點擊每個按鈕后的事件處理代碼是要自己寫的)。
例子中要考慮的重點是:如果創建了一個AccessoryView.xib去拖出這樣一個view,雖然不用“畫”UI了,但是我們要建一個AccessoryView.h、AccessoryView.m類去與xib文件對應,在AccessoryView.m中把它上面的按鈕事件記錄下來,一旦觸發事件,要通過delegate或通知等其他形式把事件從AccessoryView類傳遞給VC類,這樣使事情更加的麻煩了,如何解決?
有人會想:創建AccessoryView.h、AccessoryView.m是沒有必要的,因為他們除了傳遞事件,根本沒做任何事情,這樣的話就不創建他們,只有AccessoryView.xib文件,然后把xib中的按鈕分別拖動到VC類中建立起IBAction的“連線”關系,事情就搞定了。
這個思路很好,但是我們會發現,并不能實現AccessoryView.xib與VC中的“連線”,因為VC類根本不認識這個xib,因此該VC是不允許這個xib通過“連線”向它內部添加代碼的,如何解決這個問題?——Files Owner!
將AccessoryView.xib的Files Owner指定成該VC的類,此時再拖“連線”到VC就可以了,這樣xib中按鈕的事件就能直接回調到VC中我們設置的方法里了。
這是解決這個問題最簡單的方法,不用寫一行代碼,他給我們的啟示是:xib文件是可以不依托于UIView子類、UIViewController子類單獨使用的,只是這種情況比較少見,這是一個例子。
這個例子稍稍變一下需求,就是我們在一天一點xib:4簡單使用xib里談論Files Owner要說的例子了,如果點擊textField的AccessoryView會有UI上的變化,或者交互的話,最好就是要創建AccessoryView.h、AccessoryView.m,然后把AccessoryView.xib分別與AccessoryView.m和VC的.m連線,UI的處理在AccessoryView.m中完成,邏輯的處理在VC的.m中完成,這樣的“雙連線”很好的解決了要把事件從AccessoryView.m傳遞給VC的.m的問題。
6、IBAction與IBOutlet
這是我們最常接觸的兩個,IBAction與IBOutlet分別標識方法與屬性,它們標識著由它們修飾的方法和屬性是來自xib的,我猜它們是給編譯器看的。
7、IBOutletCollection(ClassName):
將基于IBOutlet創建的對象放在一個NSarray里。
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *testViewArr;
創建了一個array,里面放的是用IBOutlet創建的UIView.
注意最好用strong進行修飾,而且如果你聲明的不是NSArray,即便是UIColor,系統也不會報錯,你打印這個color發現,系統用的還是NSArray。這個array的順序是連線時候的順序,但是不排除不同版本的xcode會改變這個順序,所以最好不要依賴這個順序。
8、IBInspectable
在OC中使用IBInspectable,在swift中使用@IBInspectable
它是xcode6引入的新功能,它修飾的屬性或者實例變量,會顯示在xib中的屬性欄中(Show the Attributes inspector),我們之前講的東西都是xib是如何影響代碼的,而IBInspectable是用代碼影響xib的,可能我的表述不是很正確,還是看一個具體例子吧。
@interface ViewController: UIViewController
//gj_testFlag用IBInspectable修飾后,就能在xib中看到這個屬性了,當然也可以用xib進行賦值了
@property (assign, nonatomic) IBInspectable BOOL gj_testFlag;
@end
9、IB_DESIGNABLE
在OC中將IB_DESIGNABLE寫在@implementation前,在swift中將@IBDesignable寫在class前,它也是xcode6引入的新功能,它的作用是可以在不運行的情況下把你的代碼顯示在xib或SB文件中。兩點說明:
1)、這是一個針對UI顯示的功能,所以只能是在UIView及其子類或者NSView及其子類上生效。
2)、要想使IBDesignable起作用必須把代碼寫在drawRect里才能顯示,同樣的代碼,我寫在了awakeFromNib里就不會再xib中看出效果,只有寫在了drawRect才可以。
10、xib的國際化
參考網址http://www.jianshu.com/p/6b05c05bfc39。
11、User Define Runtime Attribute
把一些xib中不能設置的屬性,寫在這里,就可以了,弄個圓角矩形的button再也不用寫代碼了,很方便。
12、Object
這是一個更高冷的xib用法,相對來說比較復雜,我們一點點的開始。
1)、我們先建立一個Person類,繼承自NSObject。
2)、在xib或SB文件右邊欄中找到Object這個對象,拖動它到左側邊欄中,把Object對象的class設置為Person!
3)、將object的引用添加到VC中
#import "Person.h"
@interface ViewController : UIViewController
@property (nonatomic, strong) IBOutlet Person *aPerson;
@end
4)、在xib或SB文件右邊欄中拖入一個button
5)、把這個button的響應函數拉到Person類中,命名為sayHello:函數。
- (IBAction)sayHello:(id)sender {
? ? NSLog(@"hello person");
}
6)、運行測試。
我個人認為,這種用法其實意義很大:
1)、你如果遇到這種Controller需要其他類處理的情況,xib也是可以應用的,而且還是不用寫什么代碼,設置什么delegate,相當的方便,如果你不知道如何應用,只能說明,你之前沒有想到過xib會如此的強大,可以做如此多的事情。
2)、我們都熟悉Category,知道他的一個優點就是把不同的實現可以分散到不同文件中,或者把相同的功能放在一個Category中管理,更清晰,就像蘋果自身的代碼那樣,你跳進UIViewController中看看,雖然是一個文件,但是不同功能的實現基本上都是在不同的Category中的,這給我們一個思路,如果一個VC交互復雜、UI上又很多button有很多跳轉,我們完全可以用這種方式把耦合性地的部分抽離出來,或把相同功能或業務的部分抽離出來,獨立封裝成類,這樣就可以避免幾千行代碼的超級VC類的出現,當大家在爭著解決臃腫VC的問題而提出的各種五花八門的設計模式的時候,我想這也是一個不錯的解決方案(以后會對MVC、MVP、MVVM、MVCS等各種模式提出一些自己的看法)。說到解決方案,如果有看過《Objective-C編程之道——iOS設計模式解析》這本書的朋友會知道,里面為了解決頁面跳轉混亂、交互復雜的情況引入了中介者模式,個人認為,充當終結者最好是一個NSObject類的子類,而xib的這種用法是中介者模式的一個很好的應用。
13、iOS9中關于xib的一些新特性
UIStackView和storyboard reference,請參考http://www.jianshu.com/p/63e5078ca9e0
14、xib的原理、優化
請參考http://www.jianshu.com/p/2f9e71ef7f52。
五、sb使用詳解
1、sb為什么比xib強大了?
xib可以基于View、VC甚至自己獨立的使用,而SB只能基于VC使用,為什么說比xib更加強大呢?主要是下面的兩個原因:
1).SB支持segue
2).SB對cell的支持更加強大
2、什么是segue?
在一個VC中選擇要發生跳轉的按鈕,按control拖動到另一個VC上就會出現一個菜單,在菜單上你就可以選擇跳轉的方式push、present,這樣不用寫一行代碼就能完成頁面間的跳轉,而兩個VC之間的像紐扣一樣用線連著兩個VC的的東西就是segue,是一個UIStoryboardSegue對象,我們可以簡單的理解成是完成頁面跳轉相關功能的一個類。
3、segue雖然簡單,如何傳參?
假設我們要點擊ViewController這個VC里的一個按鈕,跳轉到SecVC這個VC中,把testTitle這個參數傳過去。第一步還是要選中按鈕,拖到SecVC里,然后選中這個segue,給它一個id值,確保用代碼可以找到它,然后在ViewController中編碼:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
? ? if ([segue.identifier isEqualToString:@"sec"]) {
? ? ? ? SecVC *secVC = (SecVC *)segue.destinationViewController;
? ? ? ? secVC.testTite = @"hello world";
? ? }
}
id類型的sender參數就是觸發跳轉的那個button或其他控件。
4、不確定的跳轉如何使用segue?
我們假設我們要點擊ViewController這個VC里的一個按鈕,有可能要跳到SecVC,有可能要跳thirdVC。
1).在SB中準備segue。
注意我們這次創建的segue的方式并不是選中button按control拖到另一個VC中,而是選中VC,右鍵選中triggered segues里manual后面的+分別拖動到另兩個VC里,這個很重要,而且還是要給segue一個id。
2)、編碼實現跳轉:
- (IBAction)testSegue:(id)sender
{
? ? BOOL flag = NO;
? ? if (flag) {
? ? ? ? [self performSegueWithIdentifier:@"sec" sender:sender];
? ? } else {
? ? ? ? [self performSegueWithIdentifier:@"third" sender:sender];
? ? }
}
在segue跳轉頁面的過程中還會調一個函數,我們可以重寫這個函數,來決定是否讓這個跳轉發生。eg:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
? ? if (_islogin) {
? ? ? ? return YES;
? ? } else {
? ? ? ? NSLog(@"您還沒有登錄,請先登錄"); //用log舉個例子
? ? ? ? return NO;
? ? }
}
5、更好的使用Cell
用xib的時候,要想用cell,通常是建立一個cell子類,一個與之對應的.xib文件。用SB的時候要想用cell就簡單的多了,因為cell是可以直接拖動到tableView里的,直接在tableView里管理cell設置屬性,當然如果是復雜的cell還是要子類話對應的.h、.m的。SB在使用cell分兩種情況:1靜態cell,2動態cell。
靜態cell
設置靜態cell:
注意靜態cell一定要基于UITableViewController,否則會報錯。
展示靜態cell的tableView是不用調用自己必須實現的datasource協議的你會很驚訝,為什么必須調用的datasource協議都不用實現?答案是靜態,靜態的cell,意義就在于你在xib中設置成什么樣,他就展現什么樣,不會再調用datasource向你要cell了,因為在SB文件中已經確定下來了。
在做一些“死”頁面的時候SB的靜態cell是很好的選擇,靜態cell也不是什么都不能做,靜態cell里的button還是可以拖到@implementation中形成IBAction的,但是是無法生成IBOutlet屬性或字段的。
即使你強行的給一個靜態的cell指定了一個cell的類,也是無法向其內部拖入IBOutlet的。這就是靜態cell的局限性,但是如果你要設置的數據不多還是可以考慮用靜態cell,因為你可以通過給cell上的控件設tag來找到它從而賦值。
動態cell的使用
設置TableView的content屬性為Dynamic Prototypes就可以使用動態cell。使用動態cell的話就要在VC中實現那兩個必須實現的datasource協議,下面舉個簡單的例子:
1.給cell指定一個class,如TestCell
2.給cell設置identifier,重用機制使用
3.給cell添加一些控件,并拖到TestCell源文件中生成IBOutlet的屬性,在VC中實現那兩個必須實現的協議方法:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
? ? return 20;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
? ? TestCell *testCell = (TestCell *)[tableView dequeueReusableCellWithIdentifier:@"testCell"];
? ? testCell.testLabel.text = @"hello world";
? ? return testCell;
}
這里注意,不需要在tableView:cellForRowAtIndexPath:中創建cell了,這是與xib使用cell最大的區別,這是很重要的,千萬記住,因為我們是直接拖cell到SB文件的tableView里的,所以直接從重用池里取就好了。
這里稍微提一個地方:我們這里取重用cell用的是函數:dequeueReusableCellWithIdentifier:,而系統中還有另一個重用的函數:dequeueReusableCellWithIdentifier:forIndexPath:這個函數比我們代碼中用的那個好,是我們用的那個api的優化版,它是iOS6引入的api,完全符合我們一般app對版本兼容的要求。
6、添加tableHeaderView和tableFooterVie
不用代碼,“拖”出header于footer,其實很簡單,選中tablView,在控件中找到View拖到tableView上,往最上方拖動,直到看到左右有兩個圈的時候松手,這個View就是tableHeaderView了,同理,往最下方拖,就是tableFooterView。
7、LaunchScreen.storyboard
為了更好、更方面的配置啟動圖,LaunchScreen.storyboard出現了,簡單來說,啟動的時候會加載這個SB文件,我們可以同過它更方便的設置啟動圖,可以用auto layout減少啟動圖數量的使用,但此功能只支持iOS8及其以上的系統。
那么問題來了,我要想適配更低的系統怎么辦?答:不用。
如何禁止該功能?點擊工程->General->Launch Images Source
8、size classes
size classes也是iOS8的新功能,.xib文件也是可以使用的,但大部分情況還是基于SB來使用,從xcode6開始我們新建的xib或SB文件中對應的View變成了正方形,而且下方顯示"wAny hAny",點擊后發現是可以選擇的,選擇不同的情況,View又變成了不同的形狀,這就是size classes。簡單的理解:size classes就是對設備的屏幕尺寸進行了抽象,寬高都分別用Regular、Compact來表示,我們其實不用太在意名稱,只知道,以后不同的設備或者不同的狀態(橫豎屏)可以由這種描述來表示即可,具體的表示如下:
iPhone4S,iPhone5/5s,iPhone6,iPhone6s
豎屏:(w:Compact h:Regular)
橫屏:(w:Compact h:Compact)
iPhone6P、iPhone6sP
豎屏:(w:Compact h:Regular)
橫屏:(w:Regular h:Compact)
iPad
豎屏:(w:Regular h:Regular)
橫屏:(w:Regular h:Regular)
由此我們可以看出,雖然把屏幕抽象了,但是用Regular、Compact并不能標識出各種不同的設備,豎屏情況下,所有iPhone用這種抽象表示是一樣的(w:Compact h:Regular) ,所以:如果我們不適配橫屏,或者iPad,只做豎屏iPhone顯示的話,size classes并沒有什么用,我個人認為目前的size classes應用面還是很窄的。
如果我們適配橫屏,或iPad應該怎么做?因為橫屏后或者設備是iPad,那么size classes這種描述就會發生改變,而一旦描述發生改變,我們就能根據不同描述,做不同的布局,例如:我們在w:Compact h:Regular的情況下向View里拖了一個label,此時我們改變size classes為w:Regular h:Regular,你會發現該label不顯示了,也就是說你在w:Compact h:Regular的情況下顯示的label只在該情況下顯示,這樣就可以針對不同類型的描述,設置不同的UI布局了,除此之外,還可以給控件添加適配不同類型的描述。開啟size classes功能后,選中控件的屬性標簽會發現,有些屬性前有“+”的標志,點擊這個標志就可以給這個控件的這個屬性添加不同類型的描述了。
9、Storyboard Entry Point
如果我們用xcode6或者更高版本的xcode創建工程的話,你會發現自動就有了一個Main.storyboard,此時application:didFinishLaunchingWithOptions:中沒有一行代碼運行就沒有問題,并不像之前那樣,要創建window,指定rootViewController,這些是如何實現的?在項目的General->Main Interface路徑下默認設置的就是Main。
那么問題來了:一個SB是可以對應多個VC的,他選哪個VC作為window的rootViewController?答案是Storyboard Entry Point,這個東西就是用來指定那個作為rootViewController的,也就是說,xcode會找到表示為Storyboard Entry Point的那個VC加載它成為rootViewController,那么如何來指定Storyboard Entry Point?勾選vc的Is Initial View Controller就是設置了Storyboard Entry Point,設置了Storyboard Entry Point的VC會有一個向右的箭頭指向它,注意你在Main Interface里選的SB文件中一定要有VC勾選了這個,不然xcode是不知道如何設置rootViewController的,你不用擔心多選的問題,你如果選擇一個新的VC,舊的那個VC就自然沒有了Storyboard Entry Point,但是如果你又取消了勾選那么舊的VC并不會自動又添加Storyboard Entry Point的,要小心。
總結
以上是生活随笔為你收集整理的xib、stoaryboard详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个垃圾分类项目带你玩转飞桨(2)
- 下一篇: Scratch精通之侦测的使用