(一)iOS日历模块封装演示
下面开始演示如何封装一个iOS日历原生模块,让JavaScript可以进行访问到iOS平台日历的功能。
在React Native中,原生模块就是一个Objective-C类,并且应该实现了RCTBridgeModule协议。其中RCT是ReaCT的缩写。我们看一下CalendarManger.h官方的代码:
// CalendarManager.h
#import "RCTBridgeModule.h"
@interface CalendarManager : NSObject <RCTBridgeModule>
@end
为了实现RCTBridgeModule协议,你的CalendarManger类.m文件需要实现RCT_EXPORT_MODULE()宏,该宏有一个可选的参数可以设置指定在JavaScript端进行访问该模块的名称(更多细节可以往后面继续看)。如果你这边没有设置该名称,那么JavaScript会默认去使用Objective-C类名称。
到这一步CalendarManager.m文件的配置如下:
#import "CalendarManager.h"
@implementation CalendarManager
RCT_EXPORT_MODULE()
@end
下面就是注入提供给JavaScript调用的方法,该方法必须要使用RCT_EXPORT_METHOD()宏来声明,不然这些方法JavaScript前端是无法进行调用的。看下面官方的代码:
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
NSLog(@"Pretending to create an event %@ at %@", name, location);
}
完成以上的步骤,我们就可以从JavaScript端进行访问这个方法了。具体调用方式如下:
import { NativeModules } from 'react-native';
var CalendarManager = NativeModules.CalendarManager;
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');
来最终JavaScript中的代码如下:
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import React,{
AppRegistry,
Component,
StyleSheet,
Text,
View,
TouchableHighlight,
} from 'react-native';
///进行导入NativeModules中的CalendarManger模块
import { NativeModules } from 'react-native';
var CalendarManager = NativeModules.CalendarManager;
class CustomButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#a5a5a5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
class ModulesDemo extends Component {
render() {
return (
<View style={{marginTop:20}}>
<Text style={styles.welcome}>
封装iOS原生模块实例
</Text>
<CustomButton text="点击调用原生模块addEvent方法"
onPress={()=>CalendarManager.addEvent('生日聚会', '江苏南通 中天路')}
/>
</View>
);
}
}
const styles = StyleSheet.create({
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
button: {
margin:5,
backgroundColor: 'white',
padding: 10,
borderWidth:1,
borderColor: '#cdcdcd',
},
});
AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
点击按钮,会去调用原生模块方法,并且传入数据打印在控制台,效果截图如下:
22.jpg
[特别注意].JavaScript调用方法名称
封装的原生模块方法给JavaScript进行调用,通过该方法的名字进行调用。有时候可能出现同名冲突的问题,在React Native中,还好给我们定义了RCT_REMAP_METHOD()宏来进行重置JavaScript调用的方法名称。该宏在多个原生模块中可能封装了同样的方法名称非常有用,可以解决同名冲突问题。
CalendarManager模块在Objective-C中必须要使用[CalendarManager new]进行初始化,并且该用于桥接调用的方法的返回值始终为void。React Native桥接的方法是异步的,所以传递一个返回值给JavaScript只能通过回调或者发送事件解决(具体我们会在后面的文章中讲解)。
(二)参数类型
RCT_EXPORT_METHOD支持所有的标准的JSON对象类型,如下:
-
string(NSString )
-
number(NSInteger,float,double,CGFloat,NSNumber)
-
boolean(BOOL,NSNumber)
-
array(NSArray) 任何类型的集合
-
map(NSDictionary) 字典类型,包括任何类型键值对的集合
-
function(RCTResponseSenderBlock) block回调对象方法
除此之外,支持RCTConvert类支持的类型也都支持(大家可以查看RCTConvert来了解详情)。RCTConvert还提供了一些方法可以把输入接收的JSON数据转换成原生的Objective-C类型或者类。
例如在我们本个CalendarManger例子中,我们需要通过桥接把JavaScript中的事件的时间发送给原生封装的方法,但是我是不能直接通过桥接进行传递JavaScript的时间对象的,所以我们这边需要把时间转换成一个字符串或者数字进行传递。下面我们来看一下实例的方法:
//对外提供调用方法,为了演示事件时间格式化
RCT_EXPORT_METHOD(addEventMore:(NSString *)name location:(NSString *)location data:(NSNumber *)secondsSinceUnixEpoch){
NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
}
或者像以下的写法:
RCT_EXPORT_METHOD(addEventMoreTwo:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
{
NSDate *date = [RCTConvert NSDate:ISO8601DateString];
}
其实我们还可以依靠类型自动转换的原理,跳过手动类型转换的步骤,直接像如下这样写,也是可以的
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSDate *)date)
{
// Date is ready to use!
}
接下来我们就可以在JavaScript文件中使用如下两种方法的一种进行调用了
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey', date.getTime()); //把日期以unix时间戳形式传递
或者:
CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey', date.toISOString()); // // 把日期以ISO-8601的字符串形式传递
以上两种值传递的方法都可以正确的转换成原生平台的NSDate类型,如果传值错误的话,例如:传递Array类型值,会出现红屏幕的错误。
随着业务的发展CalendarManager.addEvent的方法会变得越来越复杂,同样参数也会越来越多。很多时候这些参数可能是可选的,在这种情况下面我们需要修改我们的API,可以接受一个事件属性的字典类型,如下代码:
#import "RCTConvert.h"
RCT_EXPORT_METHOD(addEvent:(NSString *)name details:(NSDictionary *)details)
{
NSString *location = [RCTConvert NSString:details[@"location"]];
NSDate *time = [RCTConvert NSDate:details[@"time"]];
...
}
接下来我们在JavaScript中如下进行调用即可:
CalendarManager.addEvent('Birthday Party', {
location: '4 Privet Drive, Surrey',
time: date.getTime(),
description: '...'
})
(三)实战实例
上面讲了那么多,基本都是概念和流程,现在我们直接演示一个实例让大家看一下,首先看一下封装的原生代码:
//
// CalendarManager.m
// ModulesDemo
//
// Created by 江清清 on 16/5/22.
// Copyright © 2016年 Facebook. All rights reserved.
//
#import "CalendarManager.h"
#import "RCTConvert.h"
@implementation CalendarManager
//默认名称
RCT_EXPORT_MODULE()
//对外提供调用方法
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location){
NSLog(@"Pretending to create an event %@ at %@", name, location);
}
//对外提供调用方法,为了演示事件时间格式化 secondsSinceUnixEpoch
RCT_EXPORT_METHOD(addEventMore:(NSString *)name location:(NSString *)location data:(NSNumber*)secondsSinceUnixEpoch){
NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];
}
//对外提供调用方法,为了演示事件时间格式化 ISO8601DateString
RCT_EXPORT_METHOD(addEventMoreTwo:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)
{
NSDate *date = [RCTConvert NSDate:ISO8601DateString];
}
//对外提供调用方法,为了演示事件时间格式化 自动类型转换
RCT_EXPORT_METHOD(addEventMoreDate:(NSString *)name location:(NSString *)location date:(NSDate *)date)
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
[formatter setDateFormat:@"yyyy-MM-dd"];
NSLog(@"获取的事件信息:%@,地点:%@,时间:%@",name,location,[formatter stringFromDate:date]);
}
//对外提供调用方法,为了演示事件时间格式化 传入属性字段
RCT_EXPORT_METHOD(addEventMoreDetails:(NSString *)name details:(NSDictionary *) dictionary)
{
NSString *location = [RCTConvert NSString:dictionary[@"location"]];
NSDate *time = [RCTConvert NSDate:dictionary[@"time"]];
NSString *description=[RCTConvert NSString:dictionary[@"description"]];
NSLog(@"获取的事件信息:%@,地点:%@,时间:%@,备注信息:%@",name,location,time,description);
}
@end
前端JS调用方法如下:
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import React,{
AppRegistry,
Component,
StyleSheet,
Text,
View,
TouchableHighlight,
} from 'react-native';
///进行导入NativeModules中的CalendarManger模块
import { NativeModules } from 'react-native';
var CalendarManager = NativeModules.CalendarManager;
class CustomButton extends React.Component {
render() {
return (
<TouchableHighlight
style={styles.button}
underlayColor="#a5a5a5"
onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.props.text}</Text>
</TouchableHighlight>
);
}
}
class ModulesDemo extends Component {
render() {
return (
<View style={{marginTop:20}}>
<Text style={styles.welcome}>
封装iOS原生模块实例
</Text>
<CustomButton text="点击调用原生模块addEvent方法"
onPress={()=>CalendarManager.addEvent('生日聚会', '江苏南通 中天路')}
/>
<CustomButton text="点击调用原生模块addEvent方法"
onPress={()=>CalendarManager.addEventMoreDate('生日聚会', '江苏南通 中天路',1463987752)}
/>
<CustomButton text="调用原生模块addEvent方法-传入字段格式"
onPress={()=>CalendarManager.addEventMoreDetails('生日聚会', {
location:'江苏 南通市 中天路',
time:1463987752,
description:'请一定准时来哦~'
})}
/>
</View>
);
}
}
const styles = StyleSheet.create({
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
button: {
margin:5,
backgroundColor: 'white',
padding: 10,
borderWidth:1,
borderColor: '#cdcdcd',
},
});
AppRegistry.registerComponent('ModulesDemo', () => ModulesDemo);
网友评论