react-native与IOS原生之间的通信——基于真实的项目

最近遇到一个RN项目,需要继承第三方公司的安全键盘,键盘代码用oc实现的,并且没有现成的RN组件,最后只能自己封装了。
RN版本 0.58.4

一、分析

1、第三方键盘是用oc代码实现的,可能牵扯到商业机密,所以只贴部分代码,具体如下:(直接上图了)
react-native与IOS原生之间的通信——基于真实的项目_第1张图片
上图实现的输入框是用IOS原生的UITextField
react-native与IOS原生之间的通信——基于真实的项目_第2张图片
接下来 keyboard (软键盘)是封在SDK里面的,初始化以后 通过setTextField设置键盘输入源。
在这里插入图片描述
输入以后 完成键 点击的时候执行handleSafekeyboardOkButtonAction函数。
其他都是设置属性的代码,就不贴了。

2、由此确定 输入源是原生 UITextField实现的,那么我就可以用RN的TextInput代替了。然后完成键是用原生触发的,所以要实现IOS调用RN的函数(只要IOS执行了,就会调用RN中的某个函数执行)。

二、代码实现

1、首先创建一个 继承于NSObject的类。

//  RFTSafeKeyModule.h

#import 
#import 

NS_ASSUME_NONNULL_BEGIN

@interface RFTSafeKeyModule : NSObject 

@end

NS_ASSUME_NONNULL_END

接下来是.m文件(因为代码中c++的语法,所以我把后缀改成了.mm)

// RFTSafeKeyModule.mm

#import "RFTSafeKeyModule.h"
#import 
...  // 省略部分引入

@implementation RFTSafeKeyModule

/*这个一定要添加*/
@synthesize bridge = _bridge;

RCT_EXPORT_MODULE(FTSafeKeyboard)

RCT_EXPORT_METHOD(initSafeKeyboard:(nonnull NSDictionary *)msg){
  dispatch_async(dispatch_get_main_queue(), ^{
  	//  获取传过来的数据
    NSNumber *reactTag = [msg objectForKey:@"reactTag"];
    NSString *keyType = [msg objectForKey:@"keyType"];
    
    
    // 这里是坑我好久的地方, 就是通过reactTag获取元素。 而且这里的  self.bridge也是后来oc调用rn函数的坑点。
    
    if(reactTag){
      UITextField *textInput = (UITextField *)(((RCTBaseTextInputView*)[self->_bridge.uiManager viewForReactTag:reactTag]).backedTextInputView);
    }
    
   // 初始化软键盘
    FTSafeKeyboard *keyboard = [[FTSafeKeyboard alloc] init];
    
    //设置键盘输入源
    [self.keyboard setTextField:self.textInput withView:keyType];
    
  	...  // 省略部分代码
    
    [self.keyboard setOKActionOnTarget:self OKAction:@selector(handleSafekeyboardOkButtonAction:)];
  });
}

RN端调用如下:

import { findNodeHandle,NativeAppEventEmitter,NativeModules,TextInput } from "react-native"
const { FTSafeKeyboard } = NativeModules;
const { initSafeKeyboard } = FTSafeKeyboard;

// 初始化安全键盘
initSafeKeyboard({
      reactTag: findNodeHandle(this.textInput),
      keyType: "number",  // alphabet 字母   number 数字    symbol 符号
      ...
    });

// render里面实现如下
render(){
    return(
      
         this.textInput = textInput}  style={styles.textInput} placeholder={"自定义键盘"} />
        加密结果:{this.state.pwd}
        加密结果:{this.state.res}
      
    );
  }

上面遇到的坑就是怎么通过传过来的reactTag来获取 UITextField类型的输入框。实现代码是UITextField *textInput = (UITextField *)(((RCTBaseTextInputView*)[self->_bridge.uiManager viewForReactTag:reactTag]).backedTextInputView);

2、接下来就是怎么通过oc调用RN函数(巨坑啊)。
根据官方的代码使用RCTEventEmitter,我直接照操作了,代码如下:

//  RFTSafeKeyModule.h     将 NSObject 换成了 RCTEventEmitter
@interface RFTSafeKeyModule : RCTEventEmitter 

.mm 的代码就不贴了,说重点,这里 通过 reactTag 获取 UITextField类型的输入框的地方,_bridge变为null了(我真无语了)。

(这段是废话,可略过)百度,谷歌,SO,github 能找的地方能找遍了,资料少之又少,无奈,翻源码后惊奇的发现有个 enqueueJSCall方法可以调用js的方法,具体怎么怎么写,在RN端怎么监听?网上搜索出来的结果有两种,一是把官网的文档翻译了一遍,另外一个就是讲RN的原理。但就是没有说在RN端怎么使用。(我不懂原生开发,所有的代码都是边用边学,这对现在的我已经是绝境了),然后慢慢翻源码,各种试,最后绝惊喜来了。。。。。

三、解决问题

_bridge 有个 sendAppEventWithName 方法,这个好像已经被弃用了,但是幸好还能用。

[self->_bridge.eventDispatcher sendAppEventWithName:@"KeyResult" body:@{@"pwd":@"", @"res":@"" }];
// body 后面是向RN传递的参数。

然后RN代码:

this.subscription = NativeAppEventEmitter.addListener(
      'KeyResult',
      result => this.setState({
        pwd: result.pwd,
        res: result.res
      })
    );
// 清除订阅
this.subscription && this.subscription.remove();

到这里问题总算是解决了,如果哪位大神还有更好的方法,请多多赐教。

你可能感兴趣的:(技术分享)