緊急需求‼️實現iOS啟動圖動態置灰

語言: CN / TW / HK

本文正在參加「金石計劃 . 瓜分6萬現金大獎」

前言

相信這幾天各大互聯網應用首頁置灰已經接踵而至,事情緣由我就不太贅述。毫無疑問,我司從30號當晚就收到緊急需求,我們要求1號必須緊急發版,除了常規的首頁支持配置的動態置灰外,我們還要求另外一個需求就是,啟動圖也需要支持動態配置灰功能,經過幾個同事的努力,於1號當晚順利的發版了,第二天一早便成功上線,在此記錄一下實現iOS啟動圖動態置灰的方案心得。

方案過程

實話説,當我接到此需求時,我負責的是實現iOS啟動圖動態置灰,當時我不太確認是否能實現,我能想到的是馬上搜百度、谷歌、掘金等看是否有現成的輪子,答案肯定是有的,分別是

此方案非常輕量級,只有BBADynamicLaunchImage一個類,功能也只有一個,即查找系統緩存的啟動圖路徑,使用我們提供的UIImage替換掉。其他版本控制本非必要需求我們自己代碼控制即可。最終我也是直接採用了這個方案,其他控制由我代碼自己編寫核心方法如下。PS:(雖然提供iOS13之前的啟動圖路徑查找,但是經過我實測一台iOS12的設備是不生效的,只有iOS13意思機型生效)

```

/// 系統啟動圖緩存路徑

  • (NSString *)launchImageCacheDirectory {

NSString *bundleID = [NSBundle mainBundle].infoDictionary[@"CFBundleIdentifier"];

NSFileManager *fm = [NSFileManager defaultManager];

// iOS13之前

NSString *cachesDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];

NSString *snapshotsPath = [[cachesDirectory stringByAppendingPathComponent:@"Snapshots"] stringByAppendingPathComponent:bundleID];

if ([fm fileExistsAtPath:snapshotsPath]) {

return snapshotsPath;

}

// iOS13

NSString *libraryDirectory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];

snapshotsPath = [NSString stringWithFormat:@"%@/SplashBoard/Snapshots/%@ - {DEFAULT GROUP}", libraryDirectory, bundleID];

if ([fm fileExistsAtPath:snapshotsPath]) {

return snapshotsPath;

}

return nil;

}

```

稍微吐槽下這個庫,此庫也是我一開始使用的。它也是基於BBADynamicLaunchImage做了一些拓展。比如版本控制,但是它內置的版本控制有漏洞,它只支持CFBundleShortVersionString,也就是我們俗稱的大版本,如果我build號改了版本號不變豈不是有問題?(這也是我打包後不生效調試了好久才發現的問題)而且要支持動態置灰,不發版恢復原圖就更加有問題。最後也是棄用了,當然這個庫支持暗黑模式下的啟動圖,但是我本身app就是不支持的這個功能就聊勝於無了,最終該用了上邊的方案,動態控制由我自己處理。

啟動圖如何置灰

要實現啟動圖和原圖一模一樣只是變成灰白,這裏就稍微要花一點點心思了。眾所周知我們現在iOS啟動圖都是直接用LaunchScreen這個Storyborad生成的,那我們是否能加載這個LaunchScreen,然後截取UIView的圖片,之後再通過bitmap轉換成一張灰白圖?答案是顯而易見的,代碼如下。

首先我們要給LaunchScreen定義一個id,因為默認沒有人去加載它,它也沒有id。

代碼如下:

生成啟動圖原圖或灰白圖方法,注意此方法要在主線程跑。

```

  • (UIImage *)createLaunchScreenImage:(BOOL)isNeedGray {

UIStoryboard *sb = [UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil];

UIViewController *vc = [sb instantiateViewControllerWithIdentifier:@"LaunchScreen"];

[vc loadViewIfNeeded];

vc.view.frame = UIScreen.mainScreen.bounds;

UIImage *image = [vc.view snapshotImage];

if (isNeedGray) {

image = [image createGrayImage];

}

return image;

}

```

UIView截圖

``` func snapshotImage() -> UIImage? {

UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0);

self.layer.render(in: UIGraphicsGetCurrentContext()!)

let image = UIGraphicsGetImageFromCurrentImageContext()

UIGraphicsEndImageContext()

return image

}

```

生成灰白圖方法,由於啟動圖必須size匹配,所以scale那些要處理好。

```

-(UIImage*)createGrayImage {

int width = self.size.width * self.scale;

int height = self.size.height * self.scale;

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();

CGContextRef context =CGBitmapContextCreate(nil,

width,

height,

8,// bits per component

0,

colorSpace,

kCGBitmapByteOrderDefault);

CGColorSpaceRelease(colorSpace);

if(context ==NULL) {

return nil;

}

CGContextDrawImage(context,

CGRectMake(0,0, width, height), self.CGImage);

UIImage*grayImage = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context) scale:self.scale orientation:self.imageOrientation];

CGContextRelease(context);

return grayImage;

}

```

動態替換

我們只需要請求後台配置,需要灰白就提供灰白圖,當配置失效,需要還原時候,根據上面方法,直接渲染一個LaunchScreen原圖即可,當然其中還要做好持久化控制,不要處理多次替換,替換生效後不再處理。

末尾

以上就是我實現此次iOS啟動圖動態置灰的全過程,由於過程的艱辛,加之我自己是一個Swifter。估計不久將來,我也會基於Swift寫一個稍微友好點的庫,在此立個Flag。