新版 iOS 使用 .storyboard
文件作为启动屏文件,RN 自带了一个 启动屏,在生成项目的路径为 ios/[project]/LaunchScreen.storyboard
,如果可以接受纯文字格式的启动屏,打开这个文件修改文字即可。
若需要自定义,可通过 xcode 进行可视化编辑,如下图
上图所标注的地方为主要操作区域
- 点击 + 号按钮,拖拽
UIImageVIew
到屏幕窗口。 - 选择要编辑的图层或约束条件
- 设置当前选中图层的属性
- 添加约束条件
可通过上述方法直接编辑 LaunchScreen.storyboard
或新建一个 .storyboard
文件,最后可通过以下方式设置启动屏所用文件,RN 默认已设置了 LaunchScreen.storyboard
为启动屏,若直接修改该文件,则无需进行这一步。
下面展示两种常见的启动屏设计
另外,启动屏可能存在一定的 瑕疵,使用时要注意一下。
与 Android 的不同之处
相比 Android 启动屏,iOS 启动屏类似于 Android 使用 activey 作为启动屏的方案,而没有启动背景的方案。这意味着可以有更好的灵活度,可以制作更加复杂的效果(上面只是最简单的介绍,更多玩法需自行探索)。并且该启动屏会在主页面加载成功后移除屏幕,替换为主界面,主界面是何时载成功是由接口函数反馈的,在 AppDelegate.m
中
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
.....
// 该函数处理完毕后返回结果,此时启动屏移除屏幕
return YES;
}
对于 RN 而言,该过程发生在主线程,在处理该逻辑的同时,JS 线程会同步加载并执行,若在主线程处理完毕、启动屏幕移除的时候,JS 线程还未处理完毕(即主界面未完成渲染),此时会显示为白屏。
一般情况下,若 JS 线程在启动时没有任何异步任务,直接在 render
返回界面,倒也问题不大,几乎不会出现白屏的情况,但如果 JS 在启动时执行一些异步请求,之后才 render
界面,那么这个白屏就很难受了。
这不同于 Android 使用 window 背景图的方式,背景图不会消失,直到主界面渲染完成后自动覆盖,视觉上是连贯的。所以对于 RN 而言,直接使用 .storyboard
作为启动屏并不完美。
react-native-splash-screen 的方案是,使用 NSRunLoop
方法阻塞主线程,等待 JS 线程加载完毕的通知,待收到通知后,解除阻塞,移除启动屏,显示主界面。
react-native-bootsplash 的方案是,在未移除启动屏前,复制启动屏创建一个层,覆盖到主界面的上面,此时看到的是这个复制的层,但完全一样,之后启动屏会按预期移除,整个过程视觉上是没有任何变化的。待主界面加载完毕,JS 线程发送通知,此时移除覆盖在主界面上的层,显示主界面。
二者都需要在 AppDelegate.m
添加相应的代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
.....
// react-native-splash-screen 需再此调用函数阻塞主线程,收到通知后跳出循环
// react-native-bootsplash 在此调用函数,即启动屏移除前,创建一个完全一样的层
// 该函数处理完毕后返回结果,此时启动屏移除屏幕
return YES;
}
这两种方式,第二种要更好一点,阻塞主线程不是一个好主意。之所以能够在 RN 上使用,是因为 RN 的 JS 单独使用了一个线程,如果为原生开发,阻塞主线程就死循环了。即使对 RN 而言,也最好不要使用这种方式,因为可能有其他第三方组件需要在主线程执行一些任务。
对于第二种方式,可以将复制的启动屏插入到主界面的下面,这样就与使用 Android 启动背景的逻辑一致了,主界面渲染完毕后会自动覆盖启动屏,哪怕不移除这个复制的层也没什么影响。react-native-splashbg
便是使用这种方式。
继续: