【大娃一點技術】iOS獲取開機畫面(啟動圖)【9.21更新】

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都會用到開機畫面。如果你還沒有使用,看完本文就可以和產品同學一起規劃了。開機后,如果需要平滑延長開機畫面,然后做點什么,就需要使用到。

如果是要實現開機后再顯示一會開機圖(主要是為了將這個圖片作為背景,來顯示廣告,啊~~~~~還說出了自己的目的),大家可能會想到一些簡單的解決方案:

  1. 【基礎方案】除了APP配置使用到的Launch Image Source或者Launch Screen File,多存一份launch image,在代碼中引用到這張圖片,創建view。【毒舌】看似簡單,不過問題來了,如何解決多屏幕比例問題?
  2. 【加強版】存多個尺寸的圖片,在不同的設備,使用不同的。【毒舌】可以,沒毛病,和設計師配合好,切圖就是了。
  3. 【加強版EX】一般開機圖都是元素相同,布局不同。代碼中我也使用動態布局,獲取image,手動布局,嘔了。【毒舌】也是思路。
  4. 【加強版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啟動圖你需要關注的小問題》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容