【问题标题】:How can I present a native UIViewController in React Native? (Can't use just a UIView)如何在 React Native 中呈现原生 UIViewController? (不能只使用 UIView)
【发布时间】:2016-08-02 10:00:05
【问题描述】:

我正在尝试在我的 React Native 应用程序中使用 ABNewPersonViewController。这就是它在 Objective-C 中的使用方式:

ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init];
picker.newPersonViewDelegate = self;

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

我将如何在 React Native 中做到这一点?我无法编写桥接 UI 组件,因为它是 UIViewController,而不是 UIView。

请不要告诉我重新实现它????

【问题讨论】:

    标签: javascript ios objective-c reactjs react-native


    【解决方案1】:

    您实际上可以做的是将 UIViewController 作为子视图添加到关键窗口视图控制器,但仍将 UIViewController 的视图作为子视图添加到本机模块视图,因此它包含在本机的框架和生命周期中模块视图而不在本机模块视图的生命周期之外呈现 UIViewController 本身 ->

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

    这是完整的实现 ->

    在你的 MyViewManager.m 添加

    #import "RCTUIManager.h"
    
    @interface MyViewManager ()
    
    @end
    
    @implementation MyViewManager
    
    RCT_EXPORT_MODULE()
    
    - (UIView*)view
    {
        return [[MyView alloc] init];
    }
    
    @end
    

    并在您的 MyView.h 中添加

    #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
    

    并在您的 MyView.m 中添加

    #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];
    }
    

    【讨论】:

      【解决方案2】:

      这就是最终对我有用的东西。

      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
      

      本教程有更多细节:http://moduscreate.com/leverage-existing-ios-views-react-native-app/

      我将在实施将信息传达回 RN 的最佳方式时进行更新。

      【讨论】:

      • 完全符合它的需要!感谢您的帮助。
      • #import 是这个故事板吗?你能详细解释一下吗
      【解决方案3】:

      您希望实现一个桥接 UI 组件,该组件安装一个空的 UIView 并主要负责呈现您的 UIViewController。这种技术最简单的例子是在 RCTModalHostView; check out the source code.

      值得注意的是,React Native 在 UIView 上定义了一个类别,该类别添加了一个名为 reactViewController 的属性,该属性会爬上视图层次结构以找到最近的 UIViewController。使用这个 UIViewController 来展示你的自定义视图控制器:

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

      【讨论】:

      • 所以我们有 UIViews 负责呈现 UIViewControllers。这对我来说似乎不对。
      • 比第一个答案更直接。谢谢。
      • @PostCodeism 这当然看起来不对,但 RN 的严格界面迫使我们采用这样的解决方法。如果 RN 直接与 UIViewController 合作,那就太好了。
      • 自从上面的答案已经有一段时间了,React Native 已经改进了很多......我想知道现在是否有更好的方法来做到这一点......ty
      猜你喜欢
      • 2021-12-29
      • 1970-01-01
      • 2015-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 2016-11-03
      • 1970-01-01
      相关资源
      最近更新 更多