How to Go to Specific Native View Controller from React-Native Code

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?

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. 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.

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.

  2. MyViewController.swift - A normal UIViewController with a standard implementation.

  3. MyProject-Bridging-Header.h - provides Obj-C <-> Swift communication

  4. ChangeViewBridge.h/.m - This provides the binding to allow you to call native iOS methods from the RN JavaScript.

  5. index.ios.js - Initialize your custom RCTBridge module and call the bound method to switch to your native view with a button press.

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

How to open existing ViewController in swift from react native page?

Have you tried this one: https://stackoverflow.com/a/46007680/4189507 ?

I was manage to do this in my application. It took me a few weeks to fully integrate react-native with native iOS and android (passing data, events etc. between them).

My current flow:

  • running iOS native app with button "go to React-Native app"
  • click on that button will open new Controller with react-native application.
  • there is a button "go back" which simply closing the "react-native controller".

You can call native swift / objective-c methods from react-native using RCT_EXPORT_METHOD.

Im not sure if this is what you are looking for.

Dismiss a React Native RCTRootView back to a Native ViewController

If your are looking to dismiss the native swift view controller presented from RN View

self.dismiss(animated: true, completion: nil)

I present the swift view controller as below

 let modelVC = ModelViewController()  <-- sub class of UIViewController

DispatchQueue.main.async {
let navController = UINavigationController(rootViewController: modelVC)
navController.modalPresentationStyle = .fullScreen
let topController = UIApplication.topMostViewController()
topController?.present(navController, animated: true, completion: nil)
}

I have an extension on the UIApplication to find out what is the top most view controller which is used above

extension UIApplication {

class func topMostViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {

if let navigationController = controller as? UINavigationController {
return topMostViewController(controller: navigationController.visibleViewController)
}

if let tabController = controller as? UITabBarController {
if let selected = tabController.selectedViewController {
return topMostViewController(controller: selected)
}
}

if let presented = controller?.presentedViewController {
return topMostViewController(controller: presented)
}

return controller
}
}

React Native call iOS ViewController

You need to use Export Method .

AppDelegate.h

#import <UIKit/UIKit.h>
#import "RCTBridgeModule.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate,RCTBridgeModule>

@property (nonatomic, strong) UIWindow *window;

@end

AppDelegate.m

@implementation AppDelegate
RCT_EXPORT_MODULE()

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//NO Change Here
}

RCT_EXPORT_METHOD(pushVC:(NSString *)vcName){

Class ctrlClass = NSClassFromString(vcName);
UIViewController *newVc = [[ctrlClass alloc] initWithNibName: vcName bundle: nil];
[[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:newVc animated:YES completion:nil];
}


Related Topics



Leave a reply



Submit