【问题标题】:React-Native iOS - How can I navigate to a non-React-Native view (native iOS view controller) from a React-Native view with a button press?React-Native iOS - 如何通过按下按钮从 React-Native 视图导航到非 React-Native 视图(本机 iOS 视图控制器)?
【发布时间】:2018-01-26 06:28:33
【问题描述】:

RN doco 和其他示例展示了如何从原生 iOS 视图控制器启动 React-Native 视图,但反之则不然。有人可以解释一下我该怎么做吗?

【问题讨论】:

    标签: ios react-native


    【解决方案1】:

    我能够弄清楚这一点。就我而言,我正在使用带有我自己的 Swift 本机视图控制器的 Obj-C 基础项目(这是 RN 默认的)。我的解决方案在这里,以防其他人遇到这种情况:

    简单来说,答案就是使用一个 RCTBridge 模块,让 RN JavaScript 调用原生 iOS 方法。

    这是组件的概述,然后是实现:

    1. AppDelegate.h/.m - 为初始 RN 视图初始化 RN JavaScript 索引文件,同时设置一个将根视图控制器交换为本机视图控制器的方法(该方法将从 RTCBridge 调用模块。

    2. MyViewController.swift - 具有标准实现的普通UIViewController。

    3. MyProject-Bridging-Header.h - 提供Obj-C <-> Swift 通信

    4. ChangeViewBridge.h/.m - 这提供了允许您从 RN JavaScript 调用原生 iOS 方法的绑定。

    5. index.ios.js - 初始化您的自定义 RCTBridge 模块并调用绑定方法以通过按下按钮切换到您的原生视图。

    AppDelegate.h:

    #import <UIKit/UIKit.h>
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate> {
      NSDictionary *options;
      UIViewController *viewController;
    }
    
    @property (nonatomic, strong) UIWindow *window;
    
    - (void) setInitialViewController;
    - (void) goToRegisterView; // called from the RCTBridge module
    
    @end
    

    AppDelegate.m:

    #import "AppDelegate.h"
    #import <React/RCTBundleURLProvider.h>
    #import <React/RCTRootView.h>
    #import "FidoTestProject-Swift.h" // Xcode generated import to reference MyViewController.swift from Obj-C
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
      options = launchOptions;
      [self setInitialViewController];
      return YES;
    }
    
    - (void) setInitialViewController {
      NSURL *jsCodeLocation;
    
      jsCodeLocation = [NSURL URLWithString:@"http://192.168.208.152:8081/index.ios.bundle?platform=ios&dev=true"];
    
      RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"FidoTestProject" initialProperties:nil launchOptions:options];
    
      self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
      UIViewController *rootViewController = [UIViewController new];
      rootViewController.view = rootView;
      self.window.rootViewController = rootViewController;
    
      viewController = rootViewController;
    
      [self.window makeKeyAndVisible];
    }
    
    // this method will be called from the RCTBridge
    - (void) goToNativeView {
      NSLog(@"RN binding - Native View - MyViewController.swift - Load From "main" storyboard);
      UIViewController *vc = [UIStoryboard storyboardWithName:@"main" bundle:nil].instantiateInitialViewController;
      self.window.rootViewController = vc;
    }
    
    @end
    

    MyViewController.swift:

    class RegisterViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            print("MyViewController loaded...")
            // standard view controller will load from RN
        }
    }
    

    MyProject-Bridging-Header.h:

    @import Foundation;
    @import UIKit;
    @import CoreLocation;
    @import AVFoundation;
    
    #import "React/RCTBridge.h"
    #import "React/RCTBridgeModule.h"
    #import "React/RCTBundleURLProvider.h"
    #import "React/RCTRootView.h"
    #import "AppDelegate.h"
    

    ChangeViewBridge.h:

    #import <React/RCTBridgeModule.h>
    
    @interface ChangeViewBridge : NSObject <RCTBridgeModule>
    
    - (void) changeToNativeView;
    
    @end
    

    ChangeViewBridge.m:

    #import "RegisterBridge.h"
    #import "FidoTestProject-Swift.h"
    #import "AppDelegate.h"
    
    @implementation ChangeViewBridge
    
    // reference "ChangeViewBridge" module in index.ios.js
    RCT_EXPORT_MODULE(ChangeViewBridge);
    
    RCT_EXPORT_METHOD(changeToNativeView) {
      NSLog(@"RN binding - Native View - Loading MyViewController.swift");
      AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
      [appDelegate goToNativeView];
    }
    
    @end
    

    index.ios.js

    /**
     * Sample React Native App
     * https://github.com/facebook/react-native
     * @flow
     */
    
    'use strict';
    
    import React, { Component } from 'react';
    import {
      AppRegistry,
      StyleSheet,
      Alert,
      Text,
      View,
      NativeModules,
      TouchableHighlight
    } from 'react-native';
    
    export default class FidoTestProject extends Component {
    
      constructor(props) {
         super(props)
         this.done = false;
       }
    
        _changeView() {
          this.done = true;
          this.render();
          NativeModules.ChangeViewBridge.changeToNativeView();
        }
    
      render() {
        if (!this.done) {
          return (
            <View style={styles.container}>
              <TouchableHighlight onPress={() => this._changeView()}>
                <Text color="#336699">
                  Press to Change to Native View
                </Text>
              </TouchableHighlight>
            </View>
          );
        } else {
          return (<View></View>);
        }
      }
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
      }
    });
    
    AppRegistry.registerComponent('FidoTestProject', () => FidoTestProject);
    

    【讨论】:

    • IXUAFDelegate 我们从哪里找到这个委托?
    • 感谢@seggy 指出这一点。这是我从工作项目中复制/粘贴示例代码时错过的自定义委托。我已编辑帖子以将其从示例中删除。
    • 查看下面 Simeryn Denis 的更新答案:stackoverflow.com/a/63372245/5474176
    • RN 模块的默认值是静态库目标,所以我不明白如何使用 AppDelegate 和 Storyboard?那不需要应用程序目标吗?当我尝试这个时,我收到一个错误“AppDelegate 不在范围内”。
    • 我没有得到本地 VC,也没有错误...你能帮我解决这个问题吗
    【解决方案2】:

    使用 Swift 5 更新此答案。感谢

    https://github.com/facebook/react-native/issues/1148#issuecomment-102008892

    https://stackoverflow.com/a/46007680/7325179 - 由 MStrapko 回答

    https://codersera.com/blog/react-native-bridge-for-ios/?unapproved=2851&moderation-hash=77e42524b246d2fda0f763a496156db5#comment-2851 - William Dawson 的详细解释和教程

    进入解决方案:

    在 AppDelegate.swift 中

    import Foundation
    import UIKit
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
      var window: UIWindow?
      var bridge: RCTBridge!
      
      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let jsCodeLocation: URL
        
        jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index.js", fallbackResource:nil)
        let rootView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "RNModuleName", initialProperties: nil, launchOptions: launchOptions)
        
        self.window = UIWindow(frame: UIScreen.main.bounds)
        let reactNativeViewController = UIViewController()
        reactNativeViewController.view = rootView
        let reactNavigationController = UINavigationController(rootViewController: reactNativeViewController)
        self.window?.rootViewController = reactNavigationController
        self.window?.makeKeyAndVisible()
        
        return true
      }
    //  func goToReactNative() {
    //    window?.rootViewController?.dismiss(animated: true)
    //  }
      func goNativeStoryboard() {
        DispatchQueue.main.async {
          let vc = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()
          if let vc = vc {
            (self.window?.rootViewController as? UINavigationController)?.pushViewController(vc, animated: true)
          }
        }
      }
    }
    

    YourViewController.swift

    Your regular code
    

    YourApp-Bridging-Header。请注意,还有一些额外的标题,您可能不需要。

    #import "React/RCTBridgeModule.h"
    #import "React/RCTBridge.h"
    #import "React/RCTEventDispatcher.h"
    #import "React/RCTRootView.h"
    #import "React/RCTUtils.h"
    #import "React/RCTConvert.h"
    #import "React/RCTBundleURLProvider.h"
    #import "RCTViewManager.h"
    #import "React/RCTEventEmitter.h"
    

    ConnectingFile.swift

    @objc(Connect)
    class Connect: NSObject {
      @objc func goToNative() -> Void {
        DispatchQueue.main.async {
          if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
            appDelegate.goNativeStoryboard()
          }
        }
      }
    }
    

    连接.m

    #import "React/RCTViewManager.h"
    @interface RCT_EXTERN_MODULE(Connect, RCTViewManager)
    RCT_EXTERN_METHOD(goToNative)
    
    @end
    

    ReactNativeFile.js

    import React, { Component } from 'react';
    import { StyleSheet, View, NativeModules, Text, TouchableOpacity } from 'react-native';
    const { Connect } = NativeModules;
    export default class Feed extends Component {
      constructor(props) {
        super(props)
        this.done = false;
      }
      _changeView() {
        this.done = true;
        Connect.goToNative()
      }
      render() {
        return (
          <View style={styles.container}>
            <TouchableOpacity onPress={() => this._changeView()}>
              <Text color="#336699">
                Press to Change to Native View
                </Text>
            </TouchableOpacity>
          </View>
        );
      }
    }
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: 'pink',
        alignItems: 'center',
        justifyContent: 'center',
      }
    });
    

    就是这样,它对我有用,希望它也对你有用。再次感谢所有参考来源。

    【讨论】:

      【解决方案3】:
      There is little improvement on this solution.With present solution there is no way to come back to React-Native from iOS. 
      
      If you want to come back again from iOS to React-Native.Do the below
      
       // AppDelegate.h
      
          - (void) goToNativeView {
           UIViewController *vc =  [InitialViewController new];// This is your native iOS VC
           UINavigationController* navigationController = [[UINavigationController alloc] initWithRootViewController:vc];
      
            dispatch_async(dispatch_get_main_queue(), ^{
             // Never do the below, it will be difficult to come back to react-native
      
             // self.window.rootViewController = navigationController;
      
              // Do this instead
              [self.window.rootViewController presentViewController:navigationController animated:true completion:NULL];
            });
          }
      
      //InitialViewController.m
      
          **-Create a button for go back to React-native and on button action dismiss this view controller like below.**
      
          // Dismiss the VC so controll go back from iOS to react-native
              [self dismissViewControllerAnimated:TRUE completion:nil];
      

      【讨论】:

      • 来自我最初的问题:“RN doco 和其他示例展示了如何从原生 iOS 视图控制器启动 React-Native 视图,但反之则不然。”有很多关于如何从原生到 RN 的可用示例。这个问题和答案是为了展示如何从 RN 到 native。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-30
      • 2019-02-02
      • 2016-09-25
      • 2015-08-05
      • 2020-05-01
      相关资源
      最近更新 更多