1

I want to display a native view controller (ABPersonViewController) within a react native application.

I can present the view but I cannot push the view. This code fragment works and presents the view but there is no back button to my app:

let personViewController = ABPersonViewController()
let rootViewController:UIViewController? 
        = UIApplication.shared.delegate?.window??.rootViewController!

personViewController.displayedPerson = record!

// this works
rootViewController!.present(personViewController, animated: true)

If I try to push the view rather than present the view, nothing happens because navigationController is nil:

if (rootViewController!.navigationController != nil) {
      rootViewController!.navigationController!.pushViewController(personViewController, animated: true)
}

How can I push a native view within react native iOS?

In React native android, view contacts can easily be done using this code in a custom module:

final Activity activity = getCurrentActivity();
Intent contactIntent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id);

contactIntent.setData(uri);
activity.startActivity(contactIntent);

I wish it was that easy in react native iOS!

I tried @Artal's solution for iOS and it works up to a point.

I allocated a UINavigationController that used the existing RN UIViewController as its root and set it as the rootViewController of the window. Here is the code:

UIViewController *rootViewController = [UIViewController new];
UINavigationController *navigationController = [[UINavigationController alloc]initWithRootViewController:rootViewController];

navigationController.navigationBarHidden = YES;   
rootViewController.view = rootView;
self.window.rootViewController = navigationController;

In my swift code I then cast the rootViewController to UINavigationController, set isNavigationBarHidden to false and pushed the personViewController

let navigationController:UINavigationController = rootViewController as! UINavigationController;

DispatchQueue.main.async {
   navigationController.isNavigationBarHidden = false;
   navigationController.pushViewController(personViewController, animated: true)

}

The problem came when I overrode the UINavigationController ShouldPopOnBackButton in order to set navigationBarHidden to true when I returned to RN. I followed the approach in https://stackoverflow.com/a/19132881/826435

@implementation UINavigationController (ShouldPopOnBackButton)

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
  if([self.viewControllers count] < [navigationBar.items count]) {
    return YES;
  }

  dispatch_async(dispatch_get_main_queue(), ^{
    self.navigationBarHidden = YES;
    [self popViewControllerAnimated:YES];
  });

  return NO;
}

@end

AftershouldPopItem is called, the RN view is restored but the RN keyboard is then subsequently disabled for all input fields when running on the simulator; it works fine on a device though.

Any ideas why the RN keyboard on the simulator is disabled?

James

jmc42
  • 359
  • 5
  • 22
  • From what i could find, it seems what you're doing can't be done directly through the Swift-React bridging code. Refer to this, perhaps you'll find a solution. https://github.com/facebook/react-native/issues/1148 . Also, look to familiarize yourself with custom transitions in iOS, it's usually the simplest hack to getting unique transitions you might want. https://www.raywenderlich.com/146692/ios-animation-tutorial-custom-view-controller-presentation-transitions-2 – jlmurph Jul 19 '17 at 18:02
  • Out of curiosity, is your first code block in AppDelegate?.. – jlmurph Jul 19 '17 at 18:03
  • No the code is in a separate swift module that is called via the react native bridge. Thanks for the other references; I will take a look. – jmc42 Jul 19 '17 at 20:03

2 Answers2

0

You can't push a view controller because the default rootViewController of a RN app is a UIViewController and not a UINavigationController, and that's also why you see that it's nil.

If you want to use a native navigation stack and push another native screen you have two options:

  1. Use a native navigation solution such as RNN. The downside of this approach is that it will require changing the way you currently handle navigation completely. Note: although RN comes with an out-of-the-box solution for native navigation (NavigatorIOS), it won't allow you to push other native screens. You might be able to hack it by accessing some private properties but i'm not sure that you want to go there.
  2. Change the root of your app in your AppDelegate. You can allocate a UINavigationController that will use the existing RN UIViewController as its root and set it as the rootViewController of your window. Note that this kind of change might have other implications on your app; for example: you will get the UINavigationController native navigation-bar and possible some other side effects.
Artal
  • 8,933
  • 2
  • 27
  • 30
  • On (1) thanks for the link to react native navigation (which replaces react native controllers mentioned in https://github.com/facebook/react-native/issues/1148) and for clarifying that you cannot push a view. Any examples of (2)? I also thought of using React Native Linking but on iOS there is no equivalent of addressbook://{ABRecrord uniqueId}. – jmc42 Jul 20 '17 at 10:23
0

Maybe you can pass Objective-C navigationController to Swift method.

(UINavigationController *)[UIApplication sharedApplication].keyWindow.rootViewController can convert to navigationController in React Native.

BUT

UIApplication.shared.keyWindow?.rootViewController as? UINavigationController

Amira Bedhiafi
  • 8,088
  • 6
  • 24
  • 60
jinyang li
  • 11
  • 1