简体   繁体   English

React-Native iOS - 如何通过按下按钮从 React-Native 视图导航到非 React-Native 视图(本机 iOS 视图控制器)?

[英]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?

The RN doco and other examples show how to launch a React-Native view from a native iOS view controller, but not the other way around. RN doco 和其他示例展示了如何从原生 iOS 视图控制器启动 React-Native 视图,而不是相反。 Can someone explain how I can do this?有人可以解释我如何做到这一点吗?

I was able to figure this out.我能够弄清楚这一点。 In my case, I am using an Obj-C base project (which is the RN default) with my own Swift native view controller.就我而言,我使用的是 Obj-C 基础项目(这是 RN 默认的)和我自己的 Swift 本机视图控制器。 My solution is here in case this comes up for anyone else:我的解决方案在这里,以防其他人遇到这种情况:

Simply put, the answer is to use an RCTBridge module to allow the RN JavaScript to call a native iOS method.简单来说,答案就是使用一个 RCTBridge 模块,让 RN JavaScript 调用原生的 iOS 方法。

Here is an outline of the components, followed by the implementation:这是组件的概述,然后是实现:

  1. AppDelegate.h / .m - Initialize the RN JavaScript index file for the initial RN view, also setup a method to swap the root view controller to a native view controller (this method will be called from the RTCBridge module. AppDelegate.h / .m - 为初始 RN 视图初始化 RN JavaScript 索引文件,同时设置一个方法将根视图控制器交换到本地视图控制器(该方法将从 RTCBridge 模块调用。

  2. MyViewController.swift - A normal UIViewController with a standard implementation. MyViewController.swift - 具有标准实现的普通UIViewController

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

  4. ChangeViewBridge.h / .m - This provides the binding to allow you to call native iOS methods from the RN JavaScript. ChangeViewBridge.h / .m - 这提供了允许您从 RN JavaScript 调用本机 iOS 方法的绑定。

  5. index.ios.js - Initialize your custom RCTBridge module and call the bound method to switch to your native view with a button press. index.ios.js - 初始化您的自定义 RCTBridge 模块并调用绑定方法以通过按下按钮切换到您的本机视图。

AppDelegate.h : 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 : 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 : MyViewController.swift

class RegisterViewController: UIViewController {

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

MyProject-Bridging-Header.h : 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 : ChangeViewBridge.h :

#import <React/RCTBridgeModule.h>

@interface ChangeViewBridge : NSObject <RCTBridgeModule>

- (void) changeToNativeView;

@end

ChangeViewBridge.m : 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);

An update to this answer with Swift 5. Thanks to使用 Swift 5 更新此答案。感谢

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

https://stackoverflow.com/a/46007680/7325179 - answer by MStrapko https://stackoverflow.com/a/46007680/7325179 - Mstrapko 的回答

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

Getting into the Solution:进入解决方案:

In AppDelegate.swift在 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 YourViewController.swift

Your regular code

YourApp-Bridging-Header. YourApp-Bridging-Header。 Please note there are some extra headers as well, that you might not need.请注意,还有一些您可能不需要的额外标题。

#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连接文件.swift

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

Connect.m连接.m

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

@end

ReactNativeFile.js 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',
  }
});

That was it, it worked for me, hope it works for you as well.就是这样,它对我有用,希望它也适用于你。 Thanks again to all the sources of references.再次感谢所有参考资料来源。

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];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM