V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
FaiChou
V2EX  ›  iOS

React Navigation 如何做到使用原生的 View Controller 来做 navigation 的?

  •  
  •   FaiChou · 2022-02-13 16:27:45 +08:00 · 1664 次点击
    这是一个创建于 1019 天前的主题,其中的信息可能已经有所发展或是发生改变。

    已经有段时间没有写 RN, 之前用 Navigation 还是 2.x 版本, 现在已经 6.x .

    看到RN 官方有这一段:

    This native-stack navigator uses the native APIs: UINavigationController on iOS and Fragment on Android so that navigation built with createNativeStackNavigator will behave the same and have the same performance characteristics as apps built natively on top of those APIs.

    比较疑惑, RN 不是只有一个 RootView 吗? (rootViewController.view = rootView)

    之前页面跳转(RN 里面)都是 Navigation 库给做的转场动画, 现在 它是怎么实现的用原生的 API 来做跳转?

    猜测: ViewController 如果没有原生的 Navigation 只能 present, 不能 push/pop; 所以应该是做了某些魔法操作, 当用户使用 native-stack 的情况下, 在原生里添加的一层 Navigation.

    但具体是怎么做的呢?

    Bijiabo
        1
    Bijiabo  
       2022-02-13 16:56:53 +08:00
    将 NavigationController 的 View 作为 child view ,可以仔细分析一下 react-native-screens 中的 `RNSScreenStack.m`

    在 SDK 中将继承自 `UINavigationController` 的 `RNScreensNavigationController` 作为原生导航管理用途,将 RNScreensNavigationController 的 View 作为 RNSScreenNavigationContainer 的 Native UI Component 的子 View ,大概是这样的。
    FaiChou
        2
    FaiChou  
    OP
       2022-02-13 17:32:03 +08:00
    @Bijiabo 谢谢回复. 还是有几个问题:

    ```
    RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
    RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
    moduleName:@"AwesomeProject"
    initialProperties:nil];
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    UIViewController *rootViewController = [UIViewController new];
    rootViewController.view = rootView;
    self.window.rootViewController = rootViewController;
    ```

    这是使用 RN 创建的项目, 里面只有一个 rootViewController. 它是如何(在哪里)实现 `将 NavigationController 的 View 作为 child view` 的? 是不是在编译 .h .m 时候给替换掉了? 具体是怎么完成的?

    在 js 里写了很多 view, 使用原生的 navigation 跳转新的页面, 数据传输不会有影响吧? RN 的 js 那边都是一个 runtime, 比如要 push 新的页面, 在原生端得有一个 Controller 的生命周期, didLoad 后再将 RN 的 view 设置为它的 view? 还需要 bridge 来获取 RN 的 view 吗?
    Bijiabo
        3
    Bijiabo  
       2022-02-13 19:43:14 +08:00   ❤️ 1
    @FaiChou

    关于这个问题: 这是使用 RN 创建的项目, 里面只有一个 rootViewController. 它是如何(在哪里)实现 `将 NavigationController 的 View 作为 child view` 的? 是不是在编译 .h .m 时候给替换掉了? 具体是怎么完成的?


    看到你的问题顺手看了一下 react-native-screens ,目前初步判断是这样的,可能不够准确。可以在 `RNSScreenStack.m` 中看到:

    - 具体实现应该是对应此文件 249 行的 `[self addSubview:controller.view];`
    - 这个 Native UI Component 封装实现了 `- (void)insertReactSubview:(RNSScreenView *)subview atIndex:(NSInteger)atIndex` 这个方法,可以拿到 JSX 中嵌套的子组件,渲染在 UINavigationController 中的子 controller 中

    ------

    数据传输应该不会受到影响,目前还没遇到这方面的明显问题。看到现在新的 Fabric 布局引擎是基于 JSI 来做通讯的,不需要通过走 Bridge 的方式复制大量数据了。

    关于生命周期这块不是看的很清楚(没仔细看,先做家务去 T_T...),应该是在 Native UI Component 首次渲染的时候设定为 child view 的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1268 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 18:04 · PVG 02:04 · LAX 10:04 · JFK 13:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.