深究iOS启动图问题

在开发中遇见如下问题

  • 应用更新后,启动图没有更新
  • 启动图渲染失败
  • 实现动态更换启动图的需求

问题

  • 应用更新后,启动图没有更新
  • 启动图渲染失败
  • 实现动态更换启动图的需求

定位

首先怀疑是配置方式、编译缓存等导致的问题。针对这些猜测做了以下测试:

  • 不同系统、不同机型测试,均有复现,排除该问题只发生在特定机型或系统上;
  • 清空编译缓存,仍旧复现,故排除编译缓存问题;
  • 给imageView添加背景色,启动时正常显示imageView的背景色,但图片内容未显示,故排除了布局问题;
  • 将图片从Assets中迁移至工程根目录下,出现空白启动图概率降低,但仍会偶现;
  • 修改图片名,前几次正常,之后依旧偶现;
  • 卸载应用重新安装,大概率恢复正常,仍复现;
  • 将LaunchScreen.storyboard文件复制到新建的空工程中,仍复现;

结论:系统缓存问题

解决思路

完全抛弃系统缓存的启动图,由自己生成的启动图替换。

用户安装应用后,系统会自动生成启动图并缓存至沙盒目录,接着用户启动应用时,我们通过代码将沙盒目录下缓存的启动图文件全部替换为我们通过代码生成的启动图。

生成启动图

1
2
3
4
5
6
NSString *launchScreenName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:launchScreenName bundle:nil];
UIViewController *vc = storyboard.instantiateInitialViewController;
UIGraphicsBeginImageContextWithOptions([UIScreen mainScreen].bounds.size, NO, [UIScreen mainScreen].scale);
[vc.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

替换启动图

通过测试,该方法不会报权限问题

1
2
3
- (BOOL)moveItemAtPath:(NSString *)srcPath 
toPath:(NSString *)dstPath
error:(NSError **)error API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
graph TD
A(应用启动)-->B[监听被Kill通知]
B-->C[应用Kill回调]
C-->D[获取当前App版本号]
D-->E{指定目录下</br>以版本号命名的</br>启动图是否存在}
E--YES-->F[读取该文件]
E--NO-->G[清空该目录文件]
subgraph 此步骤耗时
G-->H[生成自定义启动图]
H-->I[以版本号为名写入指定目录]
end
I-->J[计算默认启动图MD5]
F-->J
J-->K{遍历系统</br>缓存启动图</br>目录}
K--遍历中-->L{iPhone </br>Or iPad}
L--iPad-->M{对比图片</br>宽高是否</br>相同}
M--YES-->X{对比图片</br>md5值是否</br>相同}
X-->N[替换系统缓存启动图]
K--遍历完成-->S(应用杀死)
N--继续遍历-->K
L--iPhone-->X
X--NO-->K

缓存路径:
iOS13.0 及以上:

1
Library/SplashBoard/Snapshots/${PRODUCT_BUNDLE_IDENTIFIER} - {DEFAULT GROUP};

iOS13.0 以下:

1
Library/Caches/Snapshots/${PRODUCT_BUNDLE_IDENTIFIER};

图片格式:
iOS10.0 及以上:KTX;
iOS10.0 以下:PNG。

系统缓存图目录读写权限:
iOS10.0 及以上:有权限;
iOS10.0 以下:无权限。