React-Native原生通信

一、Android 通信

在使用通信时分为自定义控件和功能组件两个部分。但是两者都离不开以下几个类:

  • ReactPackage: 总管理包
  • ReactContextBaseJavaModule: 管理模块
  • SimpleViewManager: 管理控件

1.1 ReactPackage

1.1.1 初始化Package

为了将Android功能接入ReactNative,首先需要定义ReactPackage。实现功能,实现的功能分为ReactModuleViewManager
如下面代码:

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;

import androidx.annotation.NonNull;

public class GAMapPackage implements ReactPackage {

@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
return Arrays.asList();
}

@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Arrays.asList();
}
}

完成后就做好了接入RN层的功能入口,如果需要手动加入则需要找到Application中添加该功能库。需要注意,上的两个接口函数一定要有返回值,如果返回为null,则无法运行。
public class MainApplication extends Application implements ReactApplication {
...
List<ReactPackage> packages = new PackageList(this).getPackages();
...

上面代码会进行对所有添加的package进行初始化操作,而我们要做的就是进入getPackages中,手动添加。
package com.facebook.react;

public class PackageList {
...
public ArrayList<ReactPackage> getPackages() {
return new ArrayList<>(Arrays.<ReactPackage>asList(
new MainReactPackage(mConfig),
new RNCMaskedViewPackage(),
new RNGestureHandlerPackage(),
// 这里添加该功能库即可,如果是使用Library的方式添加,就不需要手动添加
));
}
}

如上,但是要注意,如果是在原有Android项目下添加package就需要手动添加,但是如果使用RN的Library方式添加的话,就不需要进行手动处理操作。另外需要注意最好在自定义添加package的时候,在Application中添加,因为package的创建会在每次编译后重置掉。

1.1.2 package的组成

实现后可以看到两个接口函数:

public List createNativeModules(@NonNull ReactApplicationContext reactContext)
public List createViewManagers(@NonNull ReactApplicationContext reactContext)

根据函数所需要的泛型不难看出重要的就是NativeModuleViewManager两个类型。前者用来实现功能方法,后者可以用来实现UI控件。
唯一参数ReactApplicationContext则是该库所在的上下文对象。其内部封装了很多实用的函数,如:getJSModule()、getCurrentActivity(),用来完成js层通信和与主要的activity的沟通功能。

1.1.3 Js 对应 Java 的所有类型映射

JavaScript Java
Bool Boolean
Number Integer
Number Float
Number Double
String String
ReadableMap、WritableMap Object
ReadableArray、WritableArray Array

1.2 ReactModule

1.2.1 初始化ReactModule

这里继承ReactContextBaseJavaModule,该库内部实现NativeModule

public class GAOfflineMapModule extends ReactContextBaseJavaModule {

private ReactApplicationContext context;

public GAOfflineMapModule(@NonNull ReactApplicationContext reactContext) {
// 注意这里需要调用super,将Context传递过去
super(reactContext);
this.context = reactContext;
}

@NonNull
@Override
public String getName() {
// 该Module在js层定义的名称
return "GaoModule";
}

...
}

1.2.2 在JS层找到该库

完成初始化java层库后,就需要在js层找到该库,这样我们就可以完成中间穿透,进行函数调用。

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

const { GaoModule } = NativeModules;

export default GaoModule;// 使用该类直接调用java层函数

这样就完成了最简单的双向初始化操作,后面只需要添加指定的函数,就可以进行完整的函数调用、函数回调操作。

1.2.3 从 JS 到 Java

如果需要将函数暴露给JS层,则需要使用注解@ReactMethod

...
@ReactMethod
public void test(int a) {
...
}

@ReactMethod
public void test(float a) {
...
}

@ReactMethod
public void test(ReadableArray a, ReadableMap b) {
...
a.getString(0);
a.getInt(1);

b.getString('key');
}

完成函数定义和注解后,就可以在JS层找到该函数了
GaoModule.test(1);
GaoModule.test(1.1);
GaoModule.test(['1', 2], { 'key': 'value' });

注意:整个过程在代码编写后需要编译操作,否则会出现意想不到的问题。多 clean 多 rebuild 。

1.2.4 从 JS 到 Java 并回调数据给 JS

数据回调是异步的过程。回调也提供了多种手段。

Callback

重新定义一个函数,并在这个函数最后使用Callback对象进行处理回调方式invoke

import com.facebook.react.bridge.Callback;
...

@ReactMethod
public void test(int a, int b, Callback callback) {
callback.invoke((a + b));
}

上面的invoke函数接收的是多个参数,因此可以在后面继续添加。
然后在Js层可以这么做:
GaoModule.test(1, 2, (result) => {
this.setState({ data: result });
});

但是由于多数情况在异步操作随时可能出现异常情况,在异常处理后返回的数据无法在js层正常获取,因此在多数情况下会使用两个Callback来管理回调预测的完整性。
// ---------JAVA
import com.facebook.react.bridge.Callback;
...

@ReactMethod
public void test(int a, int b, Callback successCallback, Callback failCallback) {
try {
successCallback.invoke((a + b));
} catch(Exception e) {
failCallback.invoke('err');
}
}

// ----------JS
GaoModule.test(1, 2, (result) => {
this.setState({ data: result });
}, (err) => {
console.log(err)
});

Promise

es6提供了promise来完成异步操作,同样也可以适用于原生调用:

import com.facebook.react.bridge.Promise;
...

@ReactMethod
public void test(int a, int b, Promise promise) {
promise.resolve((a + b));
// promise.reject("404", "error");
}

熟悉promise可以知道具有两个回调方式,但是只能使用一种方式来完成回调。
GaoModule.test(1, 2)
.then(data => {
this.setState({ data: result });
})
.catch(err => {
console.log(err)
});

相对于Callback,promise更具有完整的异步模式。

1.2.5 从 Java 到 JS

流程如下:

1.注册监听回调
2.监听回调下完成java层内容到js层的所有数据
3.取消注册监听

代码实例

首先在java层需要做一些准备,需要准备emit函数来完成到js层的函数入口。

public class GAOfflineMapModule extends ReactContextBaseJavaModule {

private ReactApplicationContext context;

public GAOfflineMapModule(@NonNull ReactApplicationContext reactContext) {
// 注意这里需要调用super,将Context传递过去
super(reactContext);
this.context = reactContext;
}

@NonNull
@Override
public String getName() {
// 该Module在js层定义的名称
return "GaoModule";
}

public void emit(String key, Object value) {
try {
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(key, value);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

可以看到emit函数所需要的两个参数解释:

key: 监听对象
value: 数据回调,注意格式必须是之前表格的js对应格式。

首先需要找到该监听器对象:

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

const { GaoModule } = NativeModules;
const listenerGaoModule = new NativeEventEmitter(ImSdkModule);
GaoModule.listenerGaoModule = listenerGaoModule;

export default GaoModule;

进行注册:
import GaoModule from '...';

...
componentDidMount() {
// 注册
this.listener = GaoModule.listener.addListener(key, (value) => {
// 逻辑处理
});
}

componentWillUnmount() {
// 取消注册
GaoModule.listener.removeListener(
this.listener
);
}

1.3 ViewManager

用于完成UI组件部分功能。
说到组件Android就会想到ViewGroupView,而这里也可以有迹可循。

SimpleViewManager

1.创建java层viewManager
public class DemoViewManager extends SimpleViewManager<TextView> {
@NonNull
@Override
public String getName() {
return "DemoView";
}

@NonNull
@Override
protected TextView createViewInstance(@NonNull ThemedReactContext reactContext) {
return new TextView(reactContext);
}
}

看到这里需要如下几个:

泛型UI:表明该viewmanager管理那个控件。
getName:返回该控件在JS端上的命名。
createViewInstance:返回该控件的一个函数,这里返回的类型就是指定的泛型。

2.注册

最后别忘了将这个ViewManager添加到package中:

public class DemoPackage implements ReactPackage {
...
@NonNull
@Override
public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
return Arrays.asList(new DemoViewManager());
}
}

3.JS层找到该控件

完成java层后,看看js层如何操作:

import { requireNativeComponent } from 'react-native';
module.exports = requireNativeComponent('DemoView');

4.设置属性

通过@ReactProp@ReactPropGroup注解函数来进行属性设置,定义好名称和类型后,就可以直接使用了。

& js
public class DemoViewManager extends SimpleViewManager<TextView> {
...
@ReactProp(name = "text")
public void setText(TextView view, String text) {
if (text != null)
view.setText(text);
}

<DemoView
style={{ width: 100, height: 100, backgroundColor: '#0f0' }}
text={'好的'}
/>

当我们在初始化、修改属性时都会调用到java层面上。

5.点击事件

通过一系列的注册来完成该功能:

public class DemoViewManager extends SimpleViewManager<TextView> {
...
// @Nullable
// @Override
// public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
// return MapBuilder.<String, Object>builder()
// .put("callback", MapBuilder.of("registrationName", "onCallback"))
// .build();
// }

@Nullable
@Override
public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
return MapBuilder.<String, Object>builder()
.put("callback", MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", "onCallback")))
.build();
}

首先需要注册java层和js层的函数映射及名称,上面的注册前者是java层名称、后者是js层函数名称。上面两个可以使用任意一个,都可以完成注册任务,但是依旧推荐未被注释的方式。
注册好函数后,就可以开始调用并传递参数:
@Override
public void onClick(View v) {
Log.e("----->", "click");
WritableMap map = Arguments.createMap();
map.putString("name", "click");
emit("callback", map);
}

public void emit(String key, WritableMap map) {
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(textView.getId(), key, map);
}

注意传递的参数格式,以及最重要的view.getId(),表明这个函数可以在自定义控件中添加,也可以在viewManager中使用,但是一定是指定的view的id。
最后就可以在js层面上使用了:
<DemoView
style={{ width: 100, height: 100, backgroundColor: '#0f0' }}
text={text}
onCallback={(data) => {
console.log('-----> callback:', data);
}}
/>

6.refs函数调用

需要注意新老函数的调用区别。

  • 新函数

    public void receiveCommand(@NonNull TextView root, String commandId, @Nullable ReadableArray args)
    只需要在这里处理好 commandId 的功能即可,无需要注册。

    & js
    public class DemoViewManager extends SimpleViewManager<TextView> implements View.OnClickListener {
    ...
    @Override
    public void receiveCommand(@NonNull TextView root, String commandId, @Nullable ReadableArray args) {
    super.receiveCommand(root, commandId, args);
    }


    import { UIManager, findNodeHandle } from 'react-native';
    ...
    sendCommand = (viewName, funcName, data = []) => {
    UIManager.dispatchViewManagerCommand(
    findNodeHandle(this),
    funcName,
    data,
    );
    };
  • 老函数

    public Map<String, Integer> getCommandsMap()
    public void receiveCommand(@NonNull TextView root, int commandId, @Nullable ReadableArray args)
    首先需要通过 getCommandsMap 函数进行注册函数名称和 commandId 进行绑定,然后在 receiveCommand 函数中处理 commandId 的功能即可。
    在JS层找到该控件名称、函数名称、需要的参数数组即可。

    & js
    public class DemoViewManager extends SimpleViewManager<TextView> implements View.OnClickListener {
    ...
    @Nullable
    @Override
    public Map<String, Integer> getCommandsMap() {
    return MapBuilder.<String, Integer>builder()
    .put("funcName", 1)
    .build();
    }


    @Override
    public void receiveCommand(@NonNull TextView root, int commandId, @Nullable ReadableArray args) {
    super.receiveCommand(root, commandId, args);
    }


    import { UIManager, findNodeHandle } from 'react-native';
    ...
    sendCommand = (viewName, funcName, data = []) => {
    UIManager.dispatchViewManagerCommand(
    findNodeHandle(this),
    UIManager.getViewManagerConfig(viewName).Commands[funcName],
    data
    )
    };

    这里传入的this就是组件的refs,这里在使用sendCommand函数时对自定义控件进行封装后的结果。

ViewGroupManager

与ViewManager的区别就在于 ViewGroupManager 可以对内部组件进行管理,例如地图内部的标记效果。

二、IOS

2.1 Module

2.1.1 RCT_EXPORT_MODULE():创建Module初始化

使用RCT_EXPORT_MODULE()来创建需要的Module,与Android对应。
准备好.h文件,这里的文件名称、类名称都无需在意。

#ifndef DemoModule_h
#define DemoModule_h

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

@interface DemoModule : NSObject <RCTBridgeModule>

@end

#endif /* DemoModule_h */

RCT_EXPORT_MODULE()中添加的参数既是定义在JS层中的类名称,注意这里不要加引号,否在在JS层的类名称也会有引号。

#import "DemoModule.h"

@implementation DemoModule
RCT_EXPORT_MODULE(DemoModule)
@end

2.1.2 JS中找到该库

需要注意的是使用过程中如果出现问题,请先重新build后在找问题。

import { NativeModules } from 'react-native';
const { DemoModule } = NativeModules;

2.1.3 从 JS 到 ios

使用RCT_EXPORT_METHOD()来创建我们需要的功能函数:

@implementation DemoModule

RCT_EXPORT_METHOD(test1) {
NSLog(@"-----> test1()");
}

RCT_EXPORT_METHOD(test2:(int)a) {
NSLog(@"-----> test2(%d)", a);
}

整个过程只需要将函数的定义放到该函数中即可。

2.1.4 从 JS 到 ios 并回调数据给 JS

Callback
// ----------JS
DemoModule.test(2, (result) => {
this.setState({ data: result });
});

此处代码的js层与android代码一模一样,不同只在于原生

@implementation DemoModule

RCT_EXPORT_METHOD(test3:(int)a:(RCTResponseSenderBlock)callback) {

NSLog(@"-----> test3(%d)", a);
callback(@[[NSNumber numberWithInt:a]]);
}

注意返回的是一个NSObject数组,并且内部的每项数据都应该是NSObject类型,因此基本类型int需要进行封箱成对象。

Promise
GaoModule.test(1, 2)
.then(data => {
this.setState({ data: result });
})
.catch(err => {
console.log(err)
});

同上,来看看ios如果处理

@implementation DemoModule

RCT_EXPORT_METHOD(test4:(int)a:(RCTPromiseResolveBlock)resolve:(RCTPromiseRejectBlock)reject) {
NSLog(@"-----> test4(%d)", a);
if (a == 11) {
resolve(@[[NSNumber numberWithInt:a]]);
} else {
NSError *error = [NSError errorWithDomain:@"Test" code:0 userInfo:nil];
reject(@"CODE", @"MESSAGE", error);
}
}

2.1.5 从 ios 到 JS

通android一样,ios也可以直接访问到Js层面上,方式也有迹可循,在js监听字段,然后ios通过该映射字段进行数据传递。
JS端:

import { NativeModules, NativeEventEmitter } from 'react-native';
...
const emitter = new NativeEventEmitter(NativeModules.TestEmitterModule);
// 注册
emitter.addListener('EventName', (name) => {
// 回调
console.log('----->', 'callback:'+name.name);
});
// 模拟发送消息
NativeModules.TestEmitterModule.send('test');

// 取消注册
emitter.remove();

上面的代码大概回顾之前的功能。

1.需要继承RCTEventEmitter。

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

@interface DemoModule : RCTEventEmitter <RCTBridgeModule>

+ (instancetype)instance;

@end

#endif /* DemoModule_h */

2.初始化工作。
... 
static DemoModule * _sharedInstance = nil;
+ (instancetype)instance {
return _sharedInstance;
}

- (NSArray<NSString*> *)supportedEvents
{
// 定义的监听字段
return @[@"callback"];
}

- (void)startObserving {
if (_sharedInstance == nil) {
_sharedInstance = self;
}
[super startObserving];
}

- (void)stopObserving {
_sharedInstance = nil;
[super stopObserving];
}

3.编写类似emit的代码作为传递入口。
// 功能回调封装
- (void)emit:(NSString *) key :(NSObject *)value {
[self sendEventWithName:key body:value];
}

RCT_EXPORT_METHOD(send) {
NSLog(@"-----> send()");
[self emit:@"callback" :@{
@"a": @"a",
@"b": [NSNumber numberWithInt:2]
}];
}

4.js层面注册及解绑

2.2 View

2.2.1 RCTViewManager

这里使用通过RCTViewManager来管理ios中的ui控件。

2.2.2 创建自定义View

自定义一个TextView

#ifndef DemoView_h
#define DemoView_h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface DemoView : UITextView
@end

#endif /* DemoView_h */

然后完成这个View,并且让他实现一个自己的本文设置功能函数:
#import "DemoView.h"

@implementation DemoView
// 添加一个设置字符串功能函数
- (void)setTexts:(NSString *)text {
[self setText:text];
}
@end

2.2.3 创建ViewManager

需要继承RCTViewManager:

#import <React/RCTViewManager.h>
#import "DemoView.h"

@interface DemoViewManager : RCTViewManager
@end

@implementation DemoViewManager
RCT_EXPORT_MODULE(DemoView)

// RCT_EXPORT_VIEW_PROPERTY(texts, NSString)

// 找到我们定义的UI
- (UIView *)view {
return [[DemoView alloc] init];
}
@end

然后在view函数找返回我们定义的UI控件。
同module一样,通过RCT_EXPORT_MODULE来定义控件在JS层的命名,注意别加引号

2.2.4 JS 中找到该View

官方文档说明:渲染时,请不要忘记拉伸UI即给UI大小,否则将无法在屏幕上看到。

import { requireNativeComponent } from 'react-native';
module.exports = requireNativeComponent('DemoView');

...

<Demo.DemoView
ref={(refs) => (this.demoView = refs)}
style={{ width: 100, height: 100, backgroundColor: '#0f0' }}
/>

2.2.5 设置属性

使用RCT_EXPORT_VIEW_PROPERTY()来标注属性命名和属性类型。

@implementation DemoViewManager
RCT_EXPORT_MODULE(DemoView)
// 标注属性
RCT_EXPORT_VIEW_PROPERTY(texts, NSString)

然后在控件中定义这个属性

#import "DemoView.h"

@implementation DemoView
// 添加一个设置字符串功能函数
- (void)setTexts:(NSString *)text {
[self setText:text];
}
@end

2.2.6 设置函数

添加相应的Delegate协议,这里使用的是UITextView,使用的就是UITextViewDelegate。

```



#### 2.2.7 refs函数调用
















重要函数有如下几个
* `RCT_EXPORT_MODULE`: 描述当前类为通信类。
* `RCT_EXPORT_METHOD`: 描述该函数为单向通信函数。
* `RCT_REMAP_METHOD`: 解决函数重载问题。

#### 带参数的原生调用

这里在JS端使用类似上面Android端的方式放置代码,来完善ios端的代码。
```java
NativeModules.TestModule.testTwo('a', 'b', 'c');

与Android端一致,需要Module名称,函数名称,参数,及回调。

  • 模块名称:
    TestModule.m
    #import <Foundation/Foundation.h>
    #import <React/RCTBridgeModule.h>

    @interface TestModule : NSObject <RCTBridgeModule>

    @end

TestModule.m

mport "TestModule.h"

@implementation TestModule
// 用于标记为指定RCTBridgeModule协议类
RCT_EXPORT_MODULE();

// 实现函数
RCT_EXPORT_METHOD(testTwo:(NSString*) text1:(NSString*) text2:(NSString*) text3) {
NSLog(@"-----> IOS%@ - %@ - %@", text1, text2, text3);
}

@end

  • 模块名称就是实现类名称
  • 这里在实现TestModule接口后,需要在类中添加RCT_EXPORT_MODULE()宏。
  • 实现的函数必须使用RCT_EXPORT_METHOD()来定义。

函数参数:
RCT_EXPORT_METHOD(函数名称:(参数类型)参数名称: (参数类型)参数名称)

这样就完成了单向函数调用,由于调用过程异步,所以回调时也和Android端类似。

原生回调

Callback

使用Callback完成函数回调。
IOS端:

RCT_EXPORT_METHOD(testFour:(NSString*)text:(RCTPromiseResolveBlock)resolve:(RCTPromiseRejectBlock)reject) {
resolve([@"Callback:" stringByAppendingString:text]);
}

JS端:
this.test();
...
test = () => {
NativeModules.TestModule.testFour('msg')
.then((msg) => {
console.log('----->', 'over:'+msg);
});
}
// async函数解析与Promise类似
test = async () => {
var a = await NativeModules.TestModule.testFour('msg');
console.log('----->', 'over'+a);
}

上面代码实例看到Callback的回调方式,但是这种方式必须在JS端触发,才能进行反响回调。

Promise

Callback类似,Promise也具有异步的效果。
Ios端:

	RCT_EXPORT_METHOD(testFour:(NSString*)text:(RCTPromiseResolveBlock)resolve:(RCTPromiseRejectBlock)reject) {
resolve([@"Callback:" stringByAppendingString:text]);
}

JS端:
 
NativeModules.TestModule.testThree(test1, test2)
.then((...) => {
...
}).catch(e => {
...
});

但是上面两种方式都无法需要在JS端触发。

RCTEventEmitter

该功能类似广播监听。不同于上面两者函数回调,一次回调,此接口用于多次回调,长期监听。

IOS端:
TestEmitterModule.h

@interface TestEmitterModule : RCTEventEmitter <RCTBridgeModule>
- (NSArray<NSString*> *) supportedEvents;
- (void)register: (NSString*)string;
@end

TestEmitterModule.m
@implementation TestEmitterModule
RCT_EXPORT_MODULE();

- (NSArray<NSString*> *)supportedEvents {
return @[@"EventName"];

}
// 注册通信
- (void)register: (NSString*) string {
[self sendEventWithName:@"EventName" body:@{@"name": string}];

}
// 发送信息提示回调,这里仅用于测试用
RCT_EXPORT_METHOD(send:(NSString*)text) {
[self register:@"callback"];

}
@end

JS端:

import { NativeModules, NativeEventEmitter } from 'react-native';
...
const emitter = new NativeEventEmitter(NativeModules.TestEmitterModule);
// 注册
emitter.addListener('EventName', (name) => {
// 回调
console.log('----->', 'callback:'+name.name);
});
// 模拟发送消息
NativeModules.TestEmitterModule.send('test');

// 取消注册
emitter.remove();

注意,如果在未注册监听的情况下发送消息,则会发送一个警告Sending XXX with no listeners registered,告知未找到注册该字段的监听器。

在需要判断该问题时,可以使用startObservingstopObserving方法。

@implementation TestEmitterModule
bool hasListener;
RCT_EXPORT_MODULE();
- (NSArray<NSString*> *)supportedEvents {
return @[@"EventName"];
}
- (void)startObserving {
hasListener = YES;
}
- (void)stopObserving {
hasListener = NO;
}
- (void)register: (NSString*) string {
if (hasListener) {
[self sendEventWithName:@"EventName" body:@{@"name": string}];
}
}

类型转换

这里时RCTConvert.h文件。
https://github.com/facebook/react-native/blob/master/React/Base/RCTConvert.h
当在定义一组常量时,我们需要对RCTConvert类进行扩展。
如,需要使用如下:

#import <React/RCTConvert.h>

@implementation RCTConvert (AMap3DView)

// 类型
RCT_ENUM_CONVERTER(MAMapType, (@{
@"normal": @(MAMapTypeStandard),
@"satellite": @(MAMapTypeSatellite),
@"night": @(MAMapTypeStandardNight),
@"navi": @(MAMapTypeNavi),
@"bus": @(MAMapTypeBus)
}), MAMapTypeStandard, integerValue);

@end

如上,我们将在JS中需要的字符串,在转换到ios时会自动根据这个内容进行转换到相应枚举类型中。

第一个参数为ios中枚举类型
第二个参数为转换对应表
第三个参数为默认数值
第四个参数为转换器工具

自定义类型

当我们在js层建立一个对象,需要在ios层读取这个对象时,需要对自定义类型进行解析处理,在android中,使用的ReadableMapReadableArray来解决,在ios上的处理也很像。

当在定义一组常量时,我们需要对RCTConvert类进行扩展;这里的自定义类型也需要。

@implementation RCTConvert (AMap3DView)
...
// 配置自定义对象
+ (LocationStyle *)LocationStyle:(id)json {
LocationStyle * locationStyle = [LocationStyle new];

locationStyle.locationIcon = [UIImage imageNamed:[self NSString:json[@"locationIcon"]]];
locationStyle.strokeColor = [self NSString:json[@"strokeColor"]];
locationStyle.strokeWidth = [self CGFloat:json[@"strokeWidth"]];
locationStyle.showsAccuracyRing = [self BOOL:json[@"showsAccuracyRing"]];

return locationStyle;
}
...

代码中可以看到,这里完成了一个对json格式数据到ios对象数据的转换过程,也是RN在对象转换的一个转换表,所有自定义的对象类型都会像这样进行转换对照。

自定义控件


#import <Foundation/Foundation.h>
#import <React/RCTViewManager.h>

@interface AMap3DViewManager : RCTViewManager
@end

@implementation AMap3DViewManager
RCT_EXPORT_MODULE();
RCT_EXPORT_VIEW_PROPERTY(mapType, MAMapType);

// 创建view组件
- (UIView *)view
{
AMap3DView *mapView = [AMap3DView new];
return mapView;
}
@end

这里创建了一个地图控件管理类,用与管理我们自定义的AMap3DView

  • 继承RCTViewManager
  • 实现的函数必须使用RCT_EXPORT_METHOD()来定义。
  • 需要的属性必须使用RCT_EXPORT_VIEW_PROPERTY()来定义,前者为属性名称,后面则是属性的类型,如果需要自定义类型,则必须要在RCTConvert的类中去添加这个自定义类。然后如果我们需要设置这个属性到View中的话,还必须要在自定义的View中添加指定函数设置这个属性。必须以setXxx格式设置属性函数。
    - (void)setLocationTypeIos:(MAUserTrackingMode)mode
    {
    ...
    }
  • 最后通过view()函数添加这个控件。