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:
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.MyViewController.swift
- A normalUIViewController
with a standard implementation.MyProject-Bridging-Header.h
- providesObj-C <->
Swift communicationChangeViewBridge.h
/.m
- This provides the binding to allow you to call native iOS methods from the RN JavaScript.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
Could Not Cast Value of Type 'Uitableviewcell' to '(Appname).(Customcellname)'
iOS Check If Application Has Access to Microphone
Xcode Error While Validation - "Your Binary Is Not Optimized for iPhone 5"
Open Links in Safari Instead of Uiwebview
Exclude Pod When Porting to MAC with Catalyst
How Detect Swipe Gesture Direction
Add Cocoapods to Tests Target Too
Rotation Only in One Viewcontroller
How to Get the Front Camera in Swift
Dyld: Library Not Loaded: @Rpath/Libswiftswiftononesupport.Dylib
Path Extension and Mime Type of File in Swift
Accessing Variables from Another Viewcontroller in Swift
iOS Controlling Uiview Alpha Behaviour for Subviews