美文网首页RN iOS开发相关iOS DeveloperiOS 开发
iOS 原生项目集成 React-Native 中遇到的问题(

iOS 原生项目集成 React-Native 中遇到的问题(

作者: 小梁同学 | 来源:发表于2016-04-10 21:39 被阅读2461次

    没有开场闲话, 直接开始啦

    一. 原生项目集成 React-Native, JS 中调用iOS原生开发组件遇到的问题:

    在进行项目集成 React-Native 的过程中, 会遇到很多界面, 比较复杂, 需要使用原生组件实现, 而在 React-Native 中写逻辑, 所以这个时候我们就需要在 JS 文件中使用调用原生的组件, 从文档中我们发现, 关于原生有两种集成方法: 1. Native Module(原生组件, 以下简称 NM) 2. Native UI Component (原生 UI 组件, 以下简称 NUC)

    就他们之间的区别, 首先 NM 是之间实现RCTBridgeModule协议, 而 NUC 是继承自RCTViewManager, RCTViewManager同样实现了RCTBridgeModule协议, 所有这两者应该没有区别, 导出组件都是通过 RCT_EXPORT_MODULE('指定导出组件的名称,默认类名');宏导出, 导出属性通过RCT_EXPORT_MODULE(属性名称, 属性类型, viewClass(Native 组件的类型));宏 导出方法通过 RCT_EXPORT_METHOD()在 NM 中导出方法直接在()中写原生方法即可如下:

    // 这是一个 NM 中的导出方法, 用于在 JS 令 Native push到下一界面
    RCT_EXPORT_METHOD(pushNative:(NSString *)storyName controllerId:(NSString *)controllerId params:(NSDictionary *)json)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIStoryboard *secondStoryBoard = [UIStoryboard storyboardWithName:storyName bundle:nil];
            UIViewController *suitViewControllers = [secondStoryBoard instantiateViewControllerWithIdentifier:controllerId];
            [suitViewControllers setHidesBottomBarWhenPushed:YES];
            [self setproperties:suitViewControllers andParams:json];
            [(UINavigationController *)[WN_GlobalUtil getCurrentNavigationController] pushViewController:suitViewControllers animated:YES];
        });
    }
    
    // JS 中使用, 将箭头方法声明为属性, 自动绑定 this 为 class
    avatorHandle = (event: Event) => {
        console.log('event');
        ReactTools.pushNative('UserCenter','Usercenters',{});
    };
    

    这里我要说的是, 我觉得 NM 适合导出原生逻辑导出原生 View 时应该使用 NUM
    所以我要将原生的一个 HeaderView 导出到 RN 中使用

    假设我们的 HeaderView 在项目总是这样定义的

     #import <UIKit/UIKit.h>
    @class MineHeaderView;
    @protocol MineHeaderViewDelegate <NSObject>
    // 点击头像触发的操作
    - (void)mineHeaderViewDidClickAvatar:(MineHeaderView *)mineHeaderView;
    // 另一项操作
    - (void)clickProducting:(MineHeaderView *)mineHeaderView;
    
    @end
    
    @interface MineHeaderView : UIView
    @property (nonatomic, weak) id<MineHeaderViewDelegate> delegate;
    // 从 xib 中加载视图的工厂方法
    + (instancetype)mineHeaderView;
    // 显示是刷新信息
    - (void)show;
    @end
    

    首先创建 RCTxxxManager 类, 在这里是 RCTMineHeaderViewManager, RN 自动去除前缀 RCT, 后缀 Manager

    #import "RCTViewManager.h"
    
    @interface RCTMineHeaderViewManager : RCTViewManager
    @end
    
    #import "RCTMineHeaderViewManager.h"
    #import "RCTEventDispatcher.h"
    #import "MineHeaderView.h"
    #import "RCTUIManager.h"
    #import "RCTBridge.h"
    #import "UIView+React.h"
    
    @interface RCTMineHeaderViewManager () <MineHeaderViewDelegate>
    
    @end
    
    @implementation RCTMineHeaderViewManager
    // 导出组件
    RCT_EXPORT_MODULE();
    // 1.
    RCT_EXPORT_METHOD(showHeaderView:(nonnull NSNumber *)reactTag) {
        // 2.
        [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
            // 3.
            MineHeaderView *view = (MineHeaderView *) viewRegistry[reactTag];
            if (!view || ![view isKindOfClass:[MineHeaderView class]]) {
                RCTLogError(@"Cannot find Component with tag #%@", reactTag);
                return;
            }
            // 4
            [view show];
        }];
    }
    
    - (UIView *)view {
        // 指定关联的 View
        MineHeaderView *mineHeaderView = [MineHeaderView mineHeaderView];
        mineHeaderView.delegate = self;
        return mineHeaderView;
    }
    
    // 绑定事件
    - (void)mineHeaderViewDidClickAvatar:(MineHeaderView *)mineHeaderView {
        // 5.
        [self.bridge.eventDispatcher sendInputEventWithName:@"handleAvatorEvent"
                                                       body:@{
                                                              @"target": mineHeaderView.reactTag
                                                              }];
    }
    // 绑定事件
    - (void)clickProducting:(MineHeaderView *)mineHeaderView {
        // 6.
        [self.bridge.eventDispatcher sendInputEventWithName:@"doingProductProgress"
                                                       body:@{
                                                              @"target": mineHeaderView.reactTag
                                                              
                                                            }];
        
    }
    
    // 6.
    - (NSArray<NSString *> *)customBubblingEventTypes {
        return @[
                 @"handleAvatorEvent",
                 @"doingProductProgress"
                 ];
    }
    
    @end
    

    1. 指定导出方法, 注意 reactTag参数, 在 RN 中, 是通过一个 reactTag 的参数将原生组件与 JS 中的调用连接, reactTag 是 JS 中传递过来的, 注意添加 nonull 关键字,否则 debug 模式会报 Android不兼容错误, 注意; 一开始我用的__nonull, 不行, 查看 OC 新特性, 在变量时使用__nonull, 应该没错, nonull 在 property 使用, 不知道此处为什么不行, 如果遇到的请指教, 谢谢

    2. 导入 RCTBridge.h, 通过 uiManager, 在 addUIBlock 中添加需要实现的逻辑, 我们可以使用 RCTUIManager 维护的 ViewRegistry 通过 reactTag 获得调用方法的组件

    3. 我们可以使用 RCTUIManager 维护的 ViewRegistry 通过 reactTag 获得调用方法的组件

    4. 获得原生组件后, 调用原生的方法

    5. 因为我们在 HeaderView 要通过代理将点击头像的事件传递, 所以我们同样将点击事件传入到 JS 中处理, 在传递事件时通过 eventDispatch(事件分发) 处理, 在 RCTViewManager 中默认处理了 press, change, focus, blur, submitEditing.. 事件, 在 JS 中调用时只需要添加 on 前缀即可监听例如 onPress, onChange,.. 就默认指定了事件, 如果想要自定义事件名称, 请重写 customBubblingEventTypes, 返回数组, 在这个例子中我们重定义了handleAvatorEvent(点击头像),doingProductProgress(物品进度) 两个事件名称

    6. 通过 sendInputEventWithName:body: 对事件与 JS 进行绑定, 第一个参数为事件名称, 第二个参数为一个字典, 这个字典中一定要包含一个 key-value @"target": reactTag, reactTag 是在 UIView+React.h 分类中(默认所有的 View都有 reactTag 属性)

    JS 中调用:

    // MineHeaderView.js
    
    'use strict';
    import React,{
        ListView,
        Text,
        View,
        Image,
        TouchableHighlight,
        NativeModules,
        Platform
    } from 'react-native';
    const RCT_UI_REF = 'MineHeaderView';
    //
    const { requireNativeComponent } = React;
    // Native Modules
    const ReactModules = React.NativeModules;
    // MineHeaderView Native UI Component
    const MineHeaderViewManager = ReactModules.MineHeaderViewManager;
    // Native Modules
    const ReactTools = ReactModules.WN_ReactTool;
    
    class MineHeaderView extends React.Component {
      constructor() {
        super();
      }
      // 点击头像事件
      avatorHandle = (event: Event) => {
        console.log('event');
        ReactTools.pushNative('UserCenter','Usercenters',{});
      };
    
      // 其他点击事件
      doingProProgress = (event: Event) => {
        ReactTools.pushNative('DoingProduct', 'DoingProduct',{});
      };
        
      // 组件加载完成后, 执行操作
      componentDidMount() {
        Platform.OS == 'ios' &&
        MineHeaderViewManager.showHeaderView(React.findNodeHandle(this.refs[RCT_UI_REF]));
      }
    
      render() {
        return (
          <RCTMineHeaderView
            ref = {RCT_UI_REF}          {/* 指定组件的 ref */}
            onHandleAvatorEvent = {this.avatorHandle}
            onDoingProductProgress = {this.doingProProgress}
            {...this.props}
          />
        );
      }
    }
    
    MineHeaderView.propTypes = {
    
    };
    
    // 第一个参数为指定的原生类名 RCTMineHeaderViewManager 类型, 默认去除 Manager, MineHeaderView关联的 JS 中组件(这个地方如果不明白参加 React-Native 文档), RCTMineHeaderView 为一个变量, 在 MineHeaderView 中使用
    var RCTMineHeaderView = requireNativeComponent('RCTMineHeaderView', MineHeaderView);
    // 将封装的原生组件供其他 JS 调用
    module.exports = MineHeaderView;
    
    
    // UserCenter.js
    
    'use strict';
    
    import React,{
        ListView,
        Text,
        View,
        Image,
        TouchableHighlight,
    } from 'react-native';
    
    import MineHeaderView from './MineHeader';
    
    class UserCenter extends React.Component {
    
      constructor() {
        super();
      }
    
      render() {
        return (
          <View>
            <MineHeaderView />
          </View>
        );
      }
    }
    
    React.AppRegistry.registerComponent('UserCenter', () => UserCenter);
    

    以上就是第一次总结, 可能存在疏误, 请指出, 共同学习进步, 谢谢, 以后会不定期更新

    相关文章

      网友评论

      • d0ab8ef28fa2:好文 mark
      • 小失:hi~ iOS中eventDispatch有个函数`RCTNormalizeInputEventName`
        作用是给eventName加上前缀"top".
        然而我们如果给eventName定为"press", 为何在JS端对应是"onPress"呢?
        求解, 请问一下相应的代码在哪?
        找了好久没找到....
      • 446d65f6773f:请问一下[WN_GlobalUtil getCurrentNavigationController]这个你是怎么做的?
        小梁同学:这个不难吧,你可以先获得当前的控制器,之后在获得NavController
        殴打小熊猫:@macroswang 哥们 我也遇到这个问题了 解决了吗?
      • Tmac50:你好,我遇到一个问题,就是从原生界面进入RN后,ios无法调用方法退出RN,请问有解决方法吗
        小梁同学:@Tmac50 具体点,不大明白
      • 一wei渡江:能贴出源码吗

      本文标题: iOS 原生项目集成 React-Native 中遇到的问题(

      本文链接:https://www.haomeiwen.com/subject/aiablttx.html