我通过深入的研究发现,混编应用中第一次从原生打开一个RN页面之所以会出现白屏,有两方面的原因。其一是RN虚拟机本省加载需要耗费时间,但这并不是决定性的影响因素。还有另外一个被大部分人忽视了的原因,就是iOS的加载机制是优先渲染UI,而RN是优先执行底层逻辑。当第一个RN页面被打开的时候,实际上上是执行的如下操作。
实际上iOS系统的UI优先级为第一优先,而RN虚拟机则不是,因此,如果只是对RN的JSBridge等进行缓存处理,对RN模块进行拆包处理,加快了RN页面的加载速度,也不能完全视图过渡动画中出现的白屏,反而是治标不治理本,只能做到理论上的加载优化,但是达不到视觉上的加载优化。但反过来,如果在视图过渡动画中进行一定技巧性处理,则即使没有缓存,没有分包,也可以完美的在混合应用中加载RN页面。
只要稍微分析下iOS页面的视图过渡动画持续时间就知道,原生导航动画,Push、Present,等大概是0.6秒。现在要做的就是自定义一个导航动画,将视图过渡总时长定义为0.7秒,然后延迟0.2秒(0.2秒延迟后再立即执行Push动画,并不会让用户觉得卡顿,具体原因则是视觉效果的范畴了,不信可以拷贝以下代码试一下),再利用剩余的0.5秒时间执行过渡动画。再配合对JSBridge的缓存才能达到事倍功半的效果。同理,对H5页面的加载优化也可以用此法。
在 RN ViewController添加如下代码
//
// RNRootVC.m
// RNModule
//
// Created by 邱弘宇 on 2018/4/16.
// Copyright © 2018年 YHQiu@github.com All rights reserved.
//
#import "WSRNRootVC.h"
@interface WSRNRootVC ()<UINavigationControllerDelegate>
@end
@implementation RNRootVC
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush) {
return [WSNavigationAnimator pushAnimator];
}
else{
return nil;
}
}
@end
//
// NavigationAnimator.h
// Core
//
// Created by 邱弘宇 on 2018/1/11.
// Copyright © 2018年 YHQiu@github.com . All rights reserved.
//
#import "WSBaseAnimator.h"
@interface WSNavigationAnimator : WSBaseAnimator
+ (instancetype)pushAnimator;
+ (instancetype)popAnimator;
@end
//
// NavigationAnimator.m
// Core
//
// Created by 邱弘宇 on 2018/1/11.
// Copyright © 2018年 YHQiu@github.com . All rights reserved.
//
#import "WSNavigationAnimator.h"
#define fromViewOffsetSX 120.f
#define fromViewOffsetSY 0.f
#define kSpringWithDamping 0.9f //弹簧震动幅度
#define kInitialSpringVelocity 0.f //弹簧
@interface WSNavigationAnimator()
@property (nonatomic, assign) int type;
@end
@implementation WSNavigationAnimator
+ (instancetype)pushAnimator{
WSNavigationAnimator *this = [WSNavigationAnimator new];
this.type = 0;
this.transitionDuration = 0.7f;
return this;
}
+ (instancetype)popAnimator{
WSNavigationAnimator *this = [WSNavigationAnimator new];
this.type = 1;
this.transitionDuration = 0.6f;
return this;
}
- (void)animateTransitionEvent {
if (self.type == 0) {
[self pushAnimator];
}
else{
[self popAnimator];
}
}
- (void)pushAnimator{
UIView *fromView = self.fromViewController.view;
UIView *toView = self.toViewController.view;
UIImageView *fromImgView = [[UIImageView alloc]initWithImage:[self generateImageWithView:[UIApplication sharedApplication].keyWindow scale:[self scale] size:[UIApplication sharedApplication].keyWindow.bounds.size]];
CGRect fromImageViewFrame = CGRectMake(0, 0, fromImgView.frame.size.width, fromImgView.frame.size.height);
CGRect toViewFrame = CGRectMake(0, 0, toView.frame.size.width, toView.frame.size.height);
fromImgView.frame = fromImageViewFrame;
self.containerView.backgroundColor = [UIColor whiteColor];
[self.containerView addSubview:fromImgView];
[self.containerView addSubview:toView];
CGAffineTransform fromTransform = CGAffineTransformMakeScale(0.8, 0.9);
fromTransform = CGAffineTransformTranslate(fromTransform, fromViewOffsetSX, fromViewOffsetSY);
CGRect fromeViewInitFrame = fromImageViewFrame;
fromImageViewFrame.origin.x -= fromViewOffsetSX;
CGRect toViewInitFrame = toViewFrame;
toViewInitFrame.origin.x += toViewInitFrame.size.width;
toView.frame = toViewInitFrame;
[UIView animateWithDuration:self.transitionDuration-0.2
delay:0.2
usingSpringWithDamping:kSpringWithDamping initialSpringVelocity:kInitialSpringVelocity options:0 animations:^{
fromImgView.frame = fromImageViewFrame;
toView.frame = toViewFrame;
} completion:^(BOOL finished) {
[fromImgView removeFromSuperview];
[self.containerView addSubview:fromView];
[self completeTransition];
}];
}
- (void)popAnimator{
UIView *fromView = self.fromViewController.view;
UIView *toView = self.toViewController.view;
UIImageView *fromImgView = [[UIImageView alloc]initWithImage:[self generateImageWithView:[UIApplication sharedApplication].keyWindow scale:[self scale] size:[UIApplication sharedApplication].keyWindow.bounds.size]];
UIImageView *toImgView = [[UIImageView alloc]initWithImage:[self generateImageWithView:toView scale:[self scale] size:toView.bounds.size]];
CGRect fromImageViewFrame = CGRectMake(0, 0, fromImgView.frame.size.width, fromImgView.frame.size.height);
CGRect toImageViewFrame = CGRectMake(0, 0, toImgView.frame.size.width, toImgView.frame.size.height);
fromImgView.frame = fromImageViewFrame;
toImgView.frame = toImageViewFrame;
self.containerView.backgroundColor = [UIColor blackColor];
[self.containerView addSubview:toImgView];
[self.containerView addSubview:fromImgView];
CGAffineTransform fromTransform = CGAffineTransformMakeScale(0.8, 0.9);
fromTransform = CGAffineTransformTranslate(fromTransform, fromViewOffsetSX, fromViewOffsetSY);
CGRect toViewInitFrame = toImageViewFrame;
toViewInitFrame.origin.x += toViewInitFrame.size.width;
toImgView.frame = toViewInitFrame;
[UIView animateWithDuration:self.transitionDuration
delay:0.0f
usingSpringWithDamping:kSpringWithDamping initialSpringVelocity:kInitialSpringVelocity options:0 animations:^{
toImgView.layer.affineTransform = fromTransform;
fromImgView.frame = toImageViewFrame;
} completion:^(BOOL finished) {
[fromImgView removeFromSuperview];
[toImgView removeFromSuperview];
[self.containerView addSubview:toView];
[self completeTransition];
}];
}
- (UIImage *)generateImageWithView:(UIView *)view scale:(CGFloat)scale size:(CGSize)size{
UIImage *image = nil;
@autoreleasepool{
// 获取当前的context
UIGraphicsBeginImageContextWithOptions(size, view.opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
// 在context中绘制图像
[view.layer renderInContext:context];
// 绘制image
image = UIGraphicsGetImageFromCurrentImageContext();
// 使当前的context出堆栈
UIGraphicsEndImageContext();
}
return image;
}
- (CGFloat)scale{
return [UIScreen mainScreen].scale;
}
@end
/
* 在跳转入口
*
*
/
- (void)jumpMethod{
UIViewController *vc = [RNRootVC new];
self.navigationViewController.delegate = vc;
[self.navigationViewControlller pushToViewController:vc animated:YES];
}
如果要执行Present动画也是同理,自定义对应动画。