How can I present a native UIViewController in React Native? (Can't use just a UIView)

12,330

Solution 1

Here's what ended up working for me.

CreateContact.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
#import "RCTBridgeModule.h"

@interface CreateContact : NSObject <ABNewPersonViewControllerDelegate, RCTBridgeModule>

@end

CreateContact.m:

#import "CreateContact.h"
#import "AppDelegate.h"

@implementation CreateContact

RCT_EXPORT_MODULE(CreateContact);


RCT_EXPORT_METHOD(presentContact) {

    dispatch_async(dispatch_get_main_queue(), ^{
        ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init];
        picker.newPersonViewDelegate = self;
        UINavigationController* contactNavigator = [[UINavigationController alloc] initWithRootViewController:picker];
        AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        [delegate.window.rootViewController presentViewController:contactNavigator animated:NO completion:nil];
    });
}

- (void)newPersonViewController:(ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person
{
    [newPersonViewController dismissViewControllerAnimated:YES completion:nil];
}
@end

This tutorial has more detail: http://moduscreate.com/leverage-existing-ios-views-react-native-app/

I'll update as I implement the best way to communicate information back to RN.

Solution 2

You want to implement a bridged UI component that mounts an empty UIView and is responsible primarily for presenting your UIViewController. The simplest example of this technique is in RCTModalHostView; check out the source code.

Notably, React Native defines a category on UIView that adds a property called reactViewController which climbs the view hierarchy to find the closest UIViewController. Use this UIViewController to present your custom view controller:

- (void)didMoveToWindow
{
  [super didMoveToWindow];

  if (!_isPresented && self.window) {
    [self.reactViewController presentViewController:_customViewController
                                           animated:NO
                                         completion:nil];
    _isPresented = YES;
  }
}

Solution 3

What you can actually do is have the UIViewController added as a child to the key window view controller but still have the view of the UIViewController added as a subview to the native module view so it's contained in the frame and lifecycle of the native module view without presenting the UIViewController itself outside the life of the native module view as so ->

UIWindow *window = (UIWindow*)[[UIApplication sharedApplication] keyWindow];
[window.rootViewController addChildViewController:_myViewController];
_myViewController.view.frame = self.superview.frame;
[self addSubview:_myViewController.view];
[_myViewController didMoveToParentViewController:window.rootViewController];

Here is the full implementation ->

In your MyViewManager.m add

#import "RCTUIManager.h"

@interface MyViewManager ()

@end

@implementation MyViewManager

RCT_EXPORT_MODULE()

- (UIView*)view
{
    return [[MyView alloc] init];
}

@end

and in your MyView.h add

#if __has_include("React/RCTViewManager.h")
#import "React/RCTViewManager.h"
#else
#import "RCTViewManager.h"
#endif

#import <UIKit/UIKit.h>
#import <AVKit/AVKit.h>

@interface MyView : UIView

@property(nonatomic, strong)UIViewController* myViewController;

@end

and in your MyView.m add

#import "MyView.h"
#import <AVFoundation/AVFoundation.h>
#import <AVKit/AVKit.h>

@implementation MyView

- (instancetype)init
{
    self = [super init];
    return self;
}

- (void)dealloc
{

}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (_myViewController != nil) {
        _myViewController.view.frame = self.frame;
    }
}

- (void)removeFromSuperview {
    if (_myViewController != nil) {
         [_myViewController willMoveToParentViewController:nil];
         [_myViewController.view removeFromSuperview];
         [_myViewController removeFromParentViewController];
         _myViewController = nil;
         [super removeFromSuperview];
    }
}

-(void)addViewControllerAsSubView
{
    _myViewController = [UIViewController new];
    UIWindow *window = (UIWindow*)[[UIApplication sharedApplication] keyWindow];
    [window.rootViewController addChildViewController:_myViewController];
    _myViewController.view.frame = self.superview.frame;
    [self addSubview:_myViewController.view];
    [_myViewController didMoveToParentViewController:window.rootViewController];
}
Share:
12,330

Related videos on Youtube

Mark Estefanos
Author by

Mark Estefanos

Updated on June 06, 2022

Comments

  • Mark Estefanos
    Mark Estefanos almost 2 years

    I'm trying to use ABNewPersonViewController in my React Native app. This is how it's used in Objective-C:

    ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init];
    picker.newPersonViewDelegate = self;
    
    UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:picker];
    [self presentViewController:navigation animated:NO completion:nil];
    

    How would I do this in React Native? I can't write a bridged UI component since it's a UIViewController, not a UIView.

    Please don't tell me to reimplement it 😟

  • ngoue
    ngoue about 5 years
    Does exactly what it needs to! Thanks for the help.
  • PostCodeism
    PostCodeism about 5 years
    So we have UIViews responsible for presenting UIViewControllers. This doesn't seem right to me.
  • Ilia Sidorenko
    Ilia Sidorenko almost 5 years
    MUCH more straightforward than the first answer. Thank you.
  • Nader Ghanbari
    Nader Ghanbari over 4 years
    @PostCodeism It certainly does not seem right but RN's rigid interface forces us towards workarounds like this. Would be great if RN worked directly with a UIViewController.
  • Ephrim Daniel
    Ephrim Daniel about 3 years
    #import <AddressBookUI/AddressBookUI.h> is this storyboard ?? could you please explain this in detail
  • Wils
    Wils almost 3 years
    It has been a while since the answer above, React Native has improved a lot...I'm wondering if is there a better way to do this nowadays...ty
  • jspizziri
    jspizziri about 2 years
    Thanks for this answer! Are there any open-source libs you can point me to that use this approach?
  • Chaim Paneth
    Chaim Paneth about 2 years