React-Native iOS - How to Navigate to a Non-React-Native View (Native iOS View Controller) from a React-Native View with a Button Press

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

Native iOS navigation on React-Native View Component button press

It's black because you're initializing a viewController programmatically and push it UIViewController *detail = [[UIViewController alloc]init];.

Try this:

UIViewController *detail = [[UIViewController alloc] init];
detail.view.backgroundColor = [UIColor whiteColor];

EDIT

You shouldn't really be initializing view controllers like that unless you need to. You should be using storyboards for creating view controllers and laying out the views and then presenting them onto a UINavigationViewController. Here is a good tutorial.

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

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.



Related Topics



Leave a reply



Submit