实现IOS原生平台与RN双向的事件调用和数据传递

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33721382/article/details/88716562

最近在要把IOS原生端的百度人脸离线采集SDK移植到React-Native上,就学习了IOS原生平台与RN之间的通信机制。做了一个Demo,现在把知识点梳理了一下,主要有以下两个:

(1)RN调用IOS原生平台的方法,并传递参数。

(2)IOS原生平台向RN发送事件,并传递参数。

Demo主要业务流程如下:

(1)RN端跳转到IOS原生页面(我们假设这个是人脸识别页面)。这一步就是实现RN调用IOS原生平台的方法,并传递参数。

(2)IOS原生页面主动向RN发送事件,模拟页面获取到人脸的数据,并传递到RN端。这一步就是实现IOS原生平台向RN发送事件,并传递参数。

一、在Xcode创建一个负责IOS原生平台与RN通信的类

这个通信类有两个作用:一是导出IOS原生平台的方法给RN调用;二是用于IOS原生页面主动向RN发送事件,传递数据。

创建这个通信类,有以下两个步骤:

1、与RN通信的类继承RCTEventEmitter(RCTEventEmitter用于向RN发送事件),实现RCTBridgeModule协议(这个协议使得RN可以调用IOS原生平台的方法)。

IOSFaceDetection.h代码

扫描二维码关注公众号,回复: 5843791 查看本文章
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

NS_ASSUME_NONNULL_BEGIN

// IOS原生端和RN端通信的接口
// 继承RCTEventEmitter,原生模块也可以给 JavaScript 发送事件通知。
// 最好的方法是继承RCTEventEmitter,实现suppportEvents方法并调用self sendEventWithName:
@interface IOSFaceDetection : RCTEventEmitter<RCTBridgeModule>

@end

NS_ASSUME_NONNULL_END

2、实现suppportEvents方法,这个方法返回一个RN回调事件的名称数组。有多少个事件,就写多少个名称。RN通过这些监听事件,从而实现IOS原生平台向RN发送事件。

RCT_EXPORT_METHOD导出一个方法给RN端调用,RN端调用的形式为:NativeModules.IOSFaceDetection.presentfFaceDetectionViewController([参数])。

IOSFaceDetection.m代码

#import "IOSFaceDetection.h"
#import "IOSFaceDetectionViewController.h"
#import "AppDelegate.h"

@implementation IOSFaceDetection

// RN的回调事件名称列表
-(NSArray<NSString *> *)supportedEvents{
    return @[
             @"onFaceLogin",
             @"onFaceCollection",
             @"onCloseFaceDetection"
             ];
}

// 为了实现RCTBridgeModule协议,你的类需要包含RCT_EXPORT_MODULE()宏。
RCT_EXPORT_MODULE();

-(dispatch_queue_t)methodQueue{
    //因为是显示页面,所以让原生接口运行在主线程
    return dispatch_get_main_queue();
}

// 导出方法给RN调用
RCT_EXPORT_METHOD(presentfFaceDetectionViewController:(int)type){
    //创建人脸识别页面ViewController
    IOSFaceDetectionViewController *faceDetectionViewController = [IOSFaceDetectionViewController new];
    //参数赋值
    faceDetectionViewController.type = type;
    //记录faceDetection,用于向RN发送事件
    faceDetectionViewController.faceDetection = self;
    AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    //  UIViewController *rootViewController = (UIViewController *)[[app.window] rootViewController];
    //获取根视图控制器
    UIViewController *rootViewController = app.window.rootViewController;
    //跳转页面
    [rootViewController presentViewController:faceDetectionViewController animated:YES completion:nil];
}

@end

二、IOS原生页面向RN端发生事件,传递参数

在第一步,创建了一个负责IOS原生平台与RN通信的类。接下来,我们需要在IOS原生页面获得这个通信类的实例对象,然后通过[sendEventWithName]向RN发送事件。具体看一下代码:

IOSFaceDetectionViewController.h 头文件代码:

#import <UIKit/UIKit.h>
#import "IOSFaceDetection.h"

#define TYPE_FACE_LOGIN 100       //人脸登录操作标识
#define TYPE_FACE_COLLECTION 200  //人脸采集操作标识

NS_ASSUME_NONNULL_BEGIN

@interface IOSFaceDetectionViewController : UIViewController

//在这里定义React Native传递过来的参数对象

// type操作类型
@property int type;

// 用于发送事件RN
@property(nonatomic,assign) IOSFaceDetection *faceDetection;

@end

NS_ASSUME_NONNULL_END

注意:在这个人脸识别原生页面类当中定义了一个IOSFaceDetection的属性,为什么要定义这个呢?答案是为了获取负责IOS与RN通信类的实例对象,在RN调用IOS原生平台方法跳转到人脸识别页面的时候,把这个通信类的实例对象存放到页面的属性当中(导出给RN调用的方法中赋值代码:faceDetectionViewController.faceDetection = self;),这样人脸识别页面就能通过这个实例对象向RN发送事件了。因为不能页面当中创建IOSFaceDetection类的实例来向RN发送事件,这样会导致APP闪退。

IOSFaceDetectionViewController.m代码:

#import "IOSFaceDetectionViewController.h"
#import "IOSFaceDetection.h"

@interface IOSFaceDetectionViewController ()

@end

// IOS原生人脸识别页面
@implementation IOSFaceDetectionViewController

//viewDidLoad方法是我们最常用的方法的,类中成员对象和变量的初始化我们都会放在这个方法中,在类创建后,无论视图的展现或消失,这个方法也是只会在将要布局时调用一次。
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"viewDidLoad");
    self.view.backgroundColor = UIColor.whiteColor;
    // Do any additional setup after loading the view.
    
    //创建一个文本组件
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20,20,200,60)];
    //设置文字
    textView.text = @"IOS原生人脸识别页面";
    //设置字体大小
    [textView setFont:[UIFont systemFontOfSize:20]];
    //添加文件组件到页面当中
    [self.view addSubview:textView];
    
    //创建一个按钮组件
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(20, 80, 60, 40);
    [button setTitle:@"返回" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(closeAction) forControlEvents:UIControlEventTouchUpInside];
    //添加按钮组件到页面当中
    [self.view addSubview:button];
    
    
    //创建一个按钮组件
    UIButton *sendRNEventButton = [UIButton buttonWithType:UIButtonTypeSystem];
    sendRNEventButton.frame = CGRectMake(20, 150, 100, 40);
    [sendRNEventButton setTitle:@"回调RN事件" forState:UIControlStateNormal];
    [sendRNEventButton addTarget:self action:@selector(sendEventToReactNative) forControlEvents:UIControlEventTouchUpInside];
    //添加按钮组件到页面当中
    [self.view addSubview:sendRNEventButton];
    
}

- (void)viewDidAppear:(BOOL)animated{
    //[super viewDidAppear:animated];
    //    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"操作类型标识:" message:[NSString stringWithFormat:@"%d",self.type] preferredStyle:UIAlertControllerStyleAlert];
    
    NSString *message = self.type == TYPE_FACE_LOGIN ? @"人脸登录":@"人脸采集";
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"操作类型标识:" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
    [alertController addAction:cancelAction];
    //弹出对话框
    [self presentViewController:alertController animated:YES completion:nil];
}

// viewWillDisappare   在视图变换时,当前视图在即将被移除、或者被覆盖时,会调用这个方法进行一些善后的处理和设置。
- (void)viewWillDisappear:(BOOL)animated{
    [self.faceDetection sendEventWithName:@"onCloseFaceDetection" body:@{}];
}

- (void)closeAction {
    //关闭页面
    [self dismissViewControllerAnimated:YES completion:nil];
}

// 发送事件到RN
- (void)sendEventToReactNative{
    // 不能通过实例化新的对象来发送事件到RN,这样会出错。
    //IOSFaceDetection *faceDetection = [IOSFaceDetection new];
    //[faceDetection sendEventWithName:@"onFaceLogin" body:@{@"faceBase64":@"人脸数据"}];
    
    if(self.type == TYPE_FACE_LOGIN){
        // 人脸登录回调
        [self.faceDetection sendEventWithName:@"onFaceLogin" body:@{@"faceBase64":@"人脸登录-人脸数据"}];
    }else if(self.type == TYPE_FACE_COLLECTION){
        [self.faceDetection sendEventWithName:@"onFaceCollection" body:@{@"faceBase64":@"人脸采集-人脸数据"}];
    }
}

/*
 #pragma mark - Navigation
 
 // In a storyboard-based application, you will often want to do a little preparation before navigation
 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 // Get the new view controller using [segue destinationViewController].
 // Pass the selected object to the new view controller.
 }
 */

@end

在IOS原生人脸识别页面当中,通过按钮点击事件模拟了人脸识别回调事件,实现了IOS原生平台向RN发送事件,并传递参数。

三、RN端调用IOS原生平台的方法,并监听IOS原生平台的回调事件

IOS原生平台与RN的通信细节封装在一个模块里面,只提供业务方法给外部调用。

代码如下:

import {
    NativeModules,
    NativeEventEmitter
} from 'react-native';

const IOSFaceDetectionEmitter = new NativeEventEmitter(NativeModules.IOSFaceDetection);

/**
 * Created by chenlw on 2019/03/21.
 * React Native调用原生IOS平台的人脸识别模块。
 */
export default class IOSFaceDetection {

    static TYPE_FACE_LOGIN = 100;         //人脸登录标识
    static TYPE_FACE_COLLECTION = 200;    //人脸采集标识

    /**
     * 进入人脸登录页面
     * @param callback
     */
    static jumpFaceLoginPage = (callback) => {
        IOSFaceDetectionListener.addFaceLoginListener(callback);
        IOSFaceDetectionListener.addCloseFaceDetectionListener();
        NativeModules.IOSFaceDetection.presentfFaceDetectionViewController(IOSFaceDetection.TYPE_FACE_LOGIN);
    };

    /**
     * 进入人脸采集页面
     * @param callback
     */
    static jumpFaceCollectionPage = (callback) => {
        IOSFaceDetectionListener.addFaceCollectionListener(callback);
        IOSFaceDetectionListener.addCloseFaceDetectionListener();
        NativeModules.IOSFaceDetection.presentfFaceDetectionViewController(IOSFaceDetection.TYPE_FACE_COLLECTION);
    };
}

/**
 * RN与IOS原生平台的通信细节封装在内部,只提供业务方法给外部调用。
 */
class IOSFaceDetectionListener {

    static FACE_LOGIN_CALLBACK_NAME = 'onFaceLogin';                //人脸登录回调方法名称
    static FACE_COLLECTION_CALLBACK_NAME = 'onFaceCollection';      //人脸采集回调方法名称
    static CLOSE_FACE_DETECTION_CALLBACK_NAME = 'onCloseFaceDetection';      //关闭IOS人脸识别模块回调方法名称

    /**
     * 不赋值null,则static成员的初始值是undefine
     * @type {null}
     */
    static faceLoginCallback;
    static faceCollectionCallback;

    static faceLoginSubscription;
    static faceCollectionSubscription;
    static closeFaceDetectionSubscription;

    ///人脸登录
    static addFaceLoginListener = (callback) => {
        if (!IOSFaceDetectionListener.faceLoginCallback) {
            console.log('');
            console.log('addFaceLoginListener');
            IOSFaceDetectionListener.faceLoginCallback = callback;
            //监听IOS原生端发送的事件
            IOSFaceDetectionListener.faceLoginSubscription = IOSFaceDetectionEmitter.addListener(
                IOSFaceDetectionListener.FACE_LOGIN_CALLBACK_NAME,
                IOSFaceDetectionListener.onFaceLogin
            );
        }
    };

    static onFaceLogin = (params) => {
        console.log('');
        console.log('onFaceLogin');
        console.log(params);
        IOSFaceDetectionListener.faceLoginCallback && IOSFaceDetectionListener.faceLoginCallback(params.faceBase64);
    };

    ///人脸采集
    static addFaceCollectionListener = (callback) => {
        if (!IOSFaceDetectionListener.faceCollectionCallback) {
            console.log('');
            console.log('addFaceCollectionListener');
            IOSFaceDetectionListener.faceCollectionCallback = callback;
            //监听IOS原生端发送的事件
            IOSFaceDetectionListener.faceCollectionSubscription = IOSFaceDetectionEmitter.addListener(
                IOSFaceDetectionListener.FACE_COLLECTION_CALLBACK_NAME,
                IOSFaceDetectionListener.onFaceCollection
            );
        }
    };

    static onFaceCollection = (params) => {
        console.log('');
        console.log('onFaceCollection');
        console.log(params);
        IOSFaceDetectionListener.faceCollectionCallback && IOSFaceDetectionListener.faceCollectionCallback(params.faceBase64);
    };


    static addCloseFaceDetectionListener = () => {
        if (!IOSFaceDetectionListener.closeFaceDetectionSubscription) {
            console.log('');
            console.log('addCloseFaceDetectionListener');
            //监听IOS原生端发送的事件
            IOSFaceDetectionListener.closeFaceDetectionSubscription = IOSFaceDetectionEmitter.addListener(
                IOSFaceDetectionListener.CLOSE_FACE_DETECTION_CALLBACK_NAME,
                IOSFaceDetectionListener.onCloseFaceDetection
            );
        }
    };
    /**
     * 从IOS原生端关闭人脸识别界面时,要移除相关监听。这个通过IOS原生页面生命周期方法实现。
     */
    static onCloseFaceDetection = (params) => {
        console.log('');
        console.log('onCloseFaceDetection');
        console.log(params);
        IOSFaceDetectionListener.removeAllListener();
    };

    static removeAllListener = () => {
        if (IOSFaceDetectionListener.faceLoginSubscription) {
            IOSFaceDetectionListener.faceLoginSubscription.remove();
            IOSFaceDetectionListener.faceLoginSubscription = null;
            IOSFaceDetectionListener.faceLoginCallback = null;
        }
        if (IOSFaceDetectionListener.faceCollectionSubscription) {
            IOSFaceDetectionListener.faceCollectionSubscription.remove();
            IOSFaceDetectionListener.faceCollectionSubscription = null;
            IOSFaceDetectionListener.faceCollectionCallback = null;
        }
        if (IOSFaceDetectionListener.closeFaceDetectionSubscription) {
            IOSFaceDetectionListener.closeFaceDetectionSubscription.remove();
            IOSFaceDetectionListener.closeFaceDetectionSubscription = null;
        }
    };
}

RN测试页面代码:

import React, {Component} from 'react';
import {StyleSheet, Text, View, Button} from 'react-native';

import IOSFaceDetection from './IOSFaceDetection';

export default class IOSFaceDetectionExample extends Component {


    static navigationOptions = {
        title: 'IOS人脸离线采集',
    };

    constructor(props, context) {
        super(props, context);
    }

    render() {
        return (
            <View style={styles.container}>
                <Button title={'人脸登录'} onPress={() => {
                    IOSFaceDetection.jumpFaceLoginPage((faceBase64) => {
                        alert(faceBase64);
                    });
                }}/>
                <Button title={'人脸采集'} onPress={() => {
                    IOSFaceDetection.jumpFaceCollectionPage((faceBase64) => {
                        alert(faceBase64);
                    });
                }}/>
            </View>
        )
    }


}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    }
});

到这里,就成功实现IOS原生平台与RN双向的事件调用和数据传递了。

猜你喜欢

转载自blog.csdn.net/qq_33721382/article/details/88716562
今日推荐