最近遇到一个RN项目,需要继承第三方公司的安全键盘,键盘代码用oc实现的,并且没有现成的RN组件,最后只能自己封装了。
RN版本 0.58.4
。
1、第三方键盘是用oc代码实现的,可能牵扯到商业机密,所以只贴部分代码,具体如下:(直接上图了)
上图实现的输入框是用IOS原生的UITextField
。
接下来 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();
到这里问题总算是解决了,如果哪位大神还有更好的方法,请多多赐教。