921更新宣言
感謝大家回來聽我道個歉。Sooooorryyyyy~~~,更好的解決方案來晚了。
最近抽空繼續研究了下,又和中國好同事們一起商討(在西貝門口圍著13寸電腦邊排隊邊討論,此情此景)。終于有了一些新的發現。
之前B哥哥(我)說,storyboard的開機圖,我只能通過截圖的方式拿到view。這句話即對又錯,的確如果是拿view,那么目前是只有截圖的方式,但是原因并不只是約束,具體的后面說。然后換種思路,如果我們拿controller,會更加的方便。
接下來我會更新本篇文檔,增加補充controller的相關處理。根據實踐,就展示開屏,我覺得(controller + new window) is more 簡單 than (keywindow add view)
。
bBbBbBbBbBbBbBbBbBbB 華麗麗的分割線,以下正文 BbBbBbBbBbBbBbBbBbBb
前言的前言
作者 ==> 我 ==> 大娃 ==> Big Baby ==> BB ==> BB ==> B
以后文中出現B,大家不要驚訝,請保持冷靜。
前言
所謂的開機畫面,就是應用啟動圖。打開應用的第一福畫面,這個畫面不是通過代碼寫的,而是工程配置。那么我們是否有辦法通過代碼獲取到,并且呈現出來呢?這里B給你YES。
發展至今有2種方式實現:Launch Image Source、Launch Screen File。如下圖:
其實,B一直理解為 3 種,Launch Screen File可以是xib和storyboard。
基本上每個APP都會用到開機畫面。如果你還沒有使用,看完本文就可以和產品同學一起規劃了。開機后,如果需要平滑延長開機畫面,然后做點什么,就需要使用到。
如果是要實現開機后再顯示一會開機圖(主要是為了將這個圖片作為背景,來顯示廣告,啊~~~~~還說出了自己的目的),大家可能會想到一些簡單的解決方案:
- 【基礎方案】除了APP配置使用到的
Launch Image Source
或者Launch Screen File
,多存一份launch image,在代碼中引用到這張圖片,創建view。【毒舌】看似簡單,不過問題來了,如何解決多屏幕比例問題? - 【加強版】存多個尺寸的圖片,在不同的設備,使用不同的。【毒舌】可以,沒毛病,和設計師配合好,切圖就是了。
- 【加強版EX】一般開機圖都是元素相同,布局不同。代碼中我也使用動態布局,獲取image,手動布局,嘔了。【毒舌】也是思路。
- 【加強版EX PRO】……
方法有很多,都能解決問題,不過有個共通的問題,一旦開機圖更新,復工量不小。接下來B介紹下目前自己的方法。
B介紹的方式,適用iPad、iPhone及其橫、豎屏情況下。如果有不適配的,請及時聯系我,謝謝。
一、思路
開機圖至少有3種方式設置,我們需要通過代碼的方式獲取到這些資源。
期初我想獲取到啟動圖的UIView,然后在keywindow上add。的確這種方式挺好,也方便。不過目前看來獲取UIView會有一些麻煩,而且如果APP的window一直變化,那么會造成更多意料之外的事情。所以B現在更傾向獲取controller,然后創建一個新的window。
接下來分別介紹下如何有效獲取各種資源。
二、獲取Launch Image Source
此處先獲取開機圖的View(畢竟image的直接獲取就是view),我們在后面組建controller。
+ (NSString *)launchImageName {
NSString *viewOrientation = UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) ? @"Portrait" : @"Landscape";
CGSize viewSize = [UIScreen mainScreen].bounds.size;
NSString *launchImage = nil;
NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
for (NSDictionary* dict in imagesDict) {
CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
if ([self size1:imageSize equleToSize2:viewSize] &&
[viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]]) {
launchImage = dict[@"UILaunchImageName"];
}
}
return launchImage;
}
+ (UIView *)picLaunchView {
UIView *launchView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[self launchImageName]]];
launchView.frame = [UIScreen mainScreen].bounds;
launchView.contentMode = UIViewContentModeScaleAspectFill;
return launchView;
}
其中有個size比較的方法,因為考慮到了屏幕旋轉,所以寫了個模糊判斷。寫的比較隨意,大家隨便看看即可,這里的吐槽B就不管了。
+ (BOOL)size1:(CGSize)size1 equleToSize2:(CGSize)size2 {
CGSize _size1;
CGSize _size2;
_size1.width = MIN(size1.width, size1.height);
_size1.height = MAX(size1.width, size1.height);
_size2.width = MIN(size2.width, size2.height);
_size2.height = MAX(size2.width, size2.height);
return CGSizeEqualToSize(_size1, _size2);
}
要點
- 通過
UILaunchImages
獲取到圖片的名字; - 根據size比較,拿到適合本設備的圖片;
- 判斷圖片的方向,拿到正確的圖片。這里估計有同學會問為什么要判斷方向,因為B做過一個應用同時適配豎屏iPhone和橫屏iPad。(>_<)
三、獲取Launch Screen File(xib)
此處有更新,直接獲取controller。
+ (UIViewController *)nibLaunchView {
UIView *launchView = nil;
NSString *xibName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"];
if ([xibName length] > 0) {
@try {
launchView = [[[NSBundle mainBundle] loadNibNamed: xibName owner:nil options:nil] firstObject];
} @catch (NSException *exception) {
}
}
[launchView setFrame:[UIScreen mainScreen].bounds];
UIViewController *controller = [UIViewController new];
[controller.view addSubview:view];
return controller;
}
要點
- 通過
UILaunchStoryboardName
獲取到nib的名字。沒錯,雖然是xib,但是因為都是Launch Screen File
; - 通過name,獲取到view array。B偷懶,在這里直接使用了
firstObject
,作為嚴謹的人,其實是可以判斷下的; - 這里的
try
大家也要加上。雖然你拿到了名字,但是不一定可以拿到nib。如果拿不到,loadNibNamed
會拋異常的,在沒有try
的情況下,就直接再見了。 -
setFrame
是一個必要過程,如果不加,你可以試試看哦。
四、獲取Launch Screen File(storyboard)
執意獲取UIView,把事情變的復雜許多。直接獲取Controller,簡單、有效。
+ (UIViewController *)nibLaunchView { __FunctionPoint__
UIViewController *controller = nil;
NSString *storyboardName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"];
if ([storyboardName length] > 0) {
@try {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:name bundle:nil];
controller = [storyboard instantiateInitialViewController];
} @catch (NSException *exception) {
}
}
return controller;
}
說明
刪除了之前的解釋,B來說說之前所謂的約束當道,storyboard就無效的原因。
其實這么理解是不對的。當我們獲取到storyboard.controller.view
時,大部分約束是有效的,那么無效的是什么——Top Layout Guide。就是它,在拿到view的時候,這個約束是無效的。各種姿勢都試了,始終無法獲取有效的view,除非截圖。
題外話,可以忽略這段。為什么B一開始沒發現問題根源。說來慚愧,B做頁面的經驗一般,特別是IB方式,還好有龍哥和山哥幫忙,讓我瞬間領悟。懷念自己曾經的iOS團隊:無所不知的龍哥、最穩的特種兵強哥、務實的偉哥、無所不會的山哥。現在想想,都能笑出聲來。匆匆2年多,感謝你們陪我一起玩。分開不重要……說多了。
好了,現在看來,storyboard的情況下獲取controller,尤為的簡單。同樣是通過UILaunchStoryboardName
獲取到storyboard文件。并且通過instantiateInitialViewController
拿到controller。
既然xib和storyboard是類似的,那么代碼可以整合下,一次性獲取有效內容。
+ (UIViewController *)nibLaunchViewController {
UIViewController *launchViewController = nil;
NSString *storyboardName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"];
if ([storyboardName length] > 0) {
@try {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
launchViewController = [storyboard instantiateInitialViewController];
} @catch (NSException *exception) {
}
if (!launchViewController) {
@try {
UIView *view = [[[NSBundle mainBundle] loadNibNamed:storyboardName owner:nil options:nil] firstObject];
[view setFrame:[UIScreen mainScreen].bounds];
launchViewController = [UIViewController new];
[launchViewController.view addSubview:view];
} @catch (NSException *exception) {
}
}
}
return launchViewController;
}
五、其余代碼
現在使用UIWindow來啟動圖。省略的代碼上文查找。
+ (BOOL)size1:(CGSize)size1 equleToSize2:(CGSize)size2 {
...
}
+ (UIViewController *)nibLaunchViewController {
...
}
+ (UIView *)picLaunchView {
...
}
+ (NSString *)launchImageName {
...
}
+ (UIViewController *)defaultLaunchViewController {
UIViewController *launchViewController = [self nibLaunchViewController];
if (launchViewController) {
return launchViewController;
}
UIView *picLaunchView = [self picLaunchView];
if (picLaunchView) {
launchViewController = [UIViewController new];
[launchViewController.view addSubview:picLaunchView];
}
return launchViewController;
}
+ (void)showLaunchView {
UIViewController *controller = [self defaultLaunchViewController];
if (!controller) {
controller = [UIViewController new];
[controller.view setBackgroundColor:[UIColor whiteColor]];
}
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = rootController;
[window makeKeyAndVisible];
}
- 通過
[obj showLaunchView]
直接顯示; - 優先級storyboard > xib > launch image;
- 支持iPhone、iPad,橫屏、豎屏。
本文又臭又長,感謝再次品味~~~~
其實,其實,在window顯示的地方還是有點細節,本文可以讓你做完事情,但不是做好。
B的口頭禪是“不要在意這些細節”,然而又是個非常注重細節的人。
本來打住的話題,重新打開,發現有更多想說的。下篇預告《iOS啟動圖你需要關注的小問題》