RN与IOS原生双向通信以及UI绑定通信的使用方式
首页 专栏 javascript 文章详情
0

RN与IOS原生双向通信以及UI绑定通信的使用方式

乍的 发布于 3 月 9 日

react-native提交给IOS客户端数据来实现通信或者交互有2种方式:

1、双向通信的方式:

RN to IOS

原生端:

#import <React/RCTBridgeModule.h>

@implementation RNToNativeEmitter

RCT_EXPORT_MODULE(RNToNativeEmitter);

RCT_EXPORT_METHOD(showText:(NSString *)textValue) textHeight:(CGFloat)height
{
    dispatch_async(dispatch_get_main_queue(), ^{
        //在主线程种接受并处理数据
        ...
    });
}

RN端:

import { NativeModules } from 'react-native';

//RNToNativeEmitter为OC的类名称,showText是OC宏注册的方法名称
NativeModules.RNToNativeEmitter.showText('显示的文本', 200);

IOS to RN

原生端:

NativeToRNEmitter.h:

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface NativeToRNEmitter : RCTEventEmitter <RCTBridgeModule>

NativeToRNEmitter.m:

- (NSArray<NSString *> *)supportedEvents {
    return @[@"EmitterMsg"];//注册的RN的方法名称
}

//注册本地通知来实现在其他模块中调用sendEventWithName的方法
-(void)beginObserving
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(emitEventInternal:)
                                                 name:@"EmitterMsg"
                                               object:nil];
}

-(void)endObserving
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

-(void)emitEventInternal:(NSNotification *)notification
{
    [self sendEventWithName:@"EmitterMsg"
                       body:notification.userInfo];
}

OC在需要的地方调用:

 [[NSNotificationCenter defaultCenter] postNotificationName:@"EmitterMsg"
                                                        object:self
                                                      userInfo:@{}];

RN接收端:

import { NativeEventEmitter } from 'react-native';
var messager = new NativeEventEmitter(NativeToRNEmitter);
messager.addListener("EmitterMsg", (result) => {
    //result为上个代码快中userInfo参数的字典对象
});

2、UI绑定通信的方式

原生端:

首先创建一个继承于UIView的CategoryView(举栗子)类,这个类有如下功能:
1、有一个成员方法-(void)showCategoryName:(NSString*)textValue
2、内部渲染了一个RCTView的RN对象。

CategoryViewManager.h(类名为目标对象的UIView对象名称+Manager)

#import <React/RCTViewManager.h>

@interface CategoryViewManager: RCTViewManager

CategoryViewManager.m

@interface CategoryViewManager()

@property (nonatomic, strong) CategoryView *cView;

@end

@implementation CategoryViewManager

RCT_EXPORT_MODULE()

//创建目标UIView的实例
- (UIView *)view {
    self.cView = [[CategoryView alloc] initWithFrame:CGRectZero];
    return self.cView;
}

RCT_EXPORT_METHOD(showCategoryName:(nonnull NSNumber *)reactTag textValue:(nonnull NSString*)textValue) {
    [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
        CategoryView *view = viewRegistry[reactTag];//从注册的CategoryView集合中获取实例
        if(view&&[view isKindOfClass:[CategoryView class]]){
            [view showCategoryName:textValue];
        }
    }];
}

@end

RN端:

首先一个组件类用于映射到原生组件

import { requireNativeComponent, findNodeHandle, UIManager } from 'react-native';

const CategoryView = requireNativeComponent('CategoryView', Category);//CategoryView与IOS端类名一致

class Category extends PureComponent {
    sendTextValue2Native(textValue) {
        //CategoryView与IOS端类名一致,showCategoryName方法名与RCT_EXPORT_METHOD注册的方法名称保持一致
        UIManager.dispatchViewManagerCommand(
            findNodeHandle(this),
            UIManager.getViewManagerConfig('CategoryView').Commands.showCategoryName,
            [textValue]//此处参数对应原生showCategoryName方法的参数,从第二个开始
        );
    }

    render() {
        return <CategoryView>
            <View>
                {this.props.children}
            </View>
        </CategoryView>;
    }
}

export default Category;

如何使用:

import { Category } from 'local/Category';

<Category ref={ref => { this.categoryRef = ref }}>
    <TouchableWithoutFeedback onPress={() => {
        this.categoryRef.sendTextValue2Native('测试文本')
    }}>
        ...
    </TouchableWithoutFeedback>;
<Category />

这样在Category组件时,原生同样就会创建一个原生CategoryView的实例对象。这样在RN端调用sendTextValue2Native就可以将内容发送到原生的showCategoryName方法,然后在原生处理相关的逻辑。文章中的调用还是较为简单,涉及到属性等更复杂的使用可以参考文章:https://reactnative.cn/docs/n...

总结:
2种通信方式应用的场景是不一样的,第一种双向通信使用相对更加普遍,因为通信编写方式与2端不会有太多代码上的耦合度,第二种方式相对来说就和UI的使用绑定结合的更紧密,限制也更多一些~

javascript ios react-native
阅读 36 发布于 3 月 9 日
收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
avatar
乍的
1 声望
0 粉丝
关注作者
0 条评论
得票 时间
提交评论
avatar
乍的
1 声望
0 粉丝
关注作者
宣传栏
目录

react-native提交给IOS客户端数据来实现通信或者交互有2种方式:

1、双向通信的方式:

RN to IOS

原生端:

#import <React/RCTBridgeModule.h>

@implementation RNToNativeEmitter

RCT_EXPORT_MODULE(RNToNativeEmitter);

RCT_EXPORT_METHOD(showText:(NSString *)textValue) textHeight:(CGFloat)height
{
    dispatch_async(dispatch_get_main_queue(), ^{
        //在主线程种接受并处理数据
        ...
    });
}

RN端:

import { NativeModules } from 'react-native';

//RNToNativeEmitter为OC的类名称,showText是OC宏注册的方法名称
NativeModules.RNToNativeEmitter.showText('显示的文本', 200);

IOS to RN

原生端:

NativeToRNEmitter.h:

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface NativeToRNEmitter : RCTEventEmitter <RCTBridgeModule>

NativeToRNEmitter.m:

- (NSArray<NSString *> *)supportedEvents {
    return @[@"EmitterMsg"];//注册的RN的方法名称
}

//注册本地通知来实现在其他模块中调用sendEventWithName的方法
-(void)beginObserving
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(emitEventInternal:)
                                                 name:@"EmitterMsg"
                                               object:nil];
}

-(void)endObserving
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

-(void)emitEventInternal:(NSNotification *)notification
{
    [self sendEventWithName:@"EmitterMsg"
                       body:notification.userInfo];
}

OC在需要的地方调用:

 [[NSNotificationCenter defaultCenter] postNotificationName:@"EmitterMsg"
                                                        object:self
                                                      userInfo:@{}];

RN接收端:

import { NativeEventEmitter } from 'react-native';
var messager = new NativeEventEmitter(NativeToRNEmitter);
messager.addListener("EmitterMsg", (result) => {
    //result为上个代码快中userInfo参数的字典对象
});

2、UI绑定通信的方式

原生端:

首先创建一个继承于UIView的CategoryView(举栗子)类,这个类有如下功能:
1、有一个成员方法-(void)showCategoryName:(NSString*)textValue
2、内部渲染了一个RCTView的RN对象。

CategoryViewManager.h(类名为目标对象的UIView对象名称+Manager)

#import <React/RCTViewManager.h>

@interface CategoryViewManager: RCTViewManager

CategoryViewManager.m

@interface CategoryViewManager()

@property (nonatomic, strong) CategoryView *cView;

@end

@implementation CategoryViewManager

RCT_EXPORT_MODULE()

//创建目标UIView的实例
- (UIView *)view {
    self.cView = [[CategoryView alloc] initWithFrame:CGRectZero];
    return self.cView;
}

RCT_EXPORT_METHOD(showCategoryName:(nonnull NSNumber *)reactTag textValue:(nonnull NSString*)textValue) {
    [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
        CategoryView *view = viewRegistry[reactTag];//从注册的CategoryView集合中获取实例
        if(view&&[view isKindOfClass:[CategoryView class]]){
            [view showCategoryName:textValue];
        }
    }];
}

@end

RN端:

首先一个组件类用于映射到原生组件

import { requireNativeComponent, findNodeHandle, UIManager } from 'react-native';

const CategoryView = requireNativeComponent('CategoryView', Category);//CategoryView与IOS端类名一致

class Category extends PureComponent {
    sendTextValue2Native(textValue) {
        //CategoryView与IOS端类名一致,showCategoryName方法名与RCT_EXPORT_METHOD注册的方法名称保持一致
        UIManager.dispatchViewManagerCommand(
            findNodeHandle(this),
            UIManager.getViewManagerConfig('CategoryView').Commands.showCategoryName,
            [textValue]//此处参数对应原生showCategoryName方法的参数,从第二个开始
        );
    }

    render() {
        return <CategoryView>
            <View>
                {this.props.children}
            </View>
        </CategoryView>;
    }
}

export default Category;

如何使用:

import { Category } from 'local/Category';

<Category ref={ref => { this.categoryRef = ref }}>
    <TouchableWithoutFeedback onPress={() => {
        this.categoryRef.sendTextValue2Native('测试文本')
    }}>
        ...
    </TouchableWithoutFeedback>;
<Category />

这样在Category组件时,原生同样就会创建一个原生CategoryView的实例对象。这样在RN端调用sendTextValue2Native就可以将内容发送到原生的showCategoryName方法,然后在原生处理相关的逻辑。文章中的调用还是较为简单,涉及到属性等更复杂的使用可以参考文章:https://reactnative.cn/docs/n...

总结:
2种通信方式应用的场景是不一样的,第一种双向通信使用相对更加普遍,因为通信编写方式与2端不会有太多代码上的耦合度,第二种方式相对来说就和UI的使用绑定结合的更紧密,限制也更多一些~