编者按: 跨平台开发在某些特殊情况下需要原生这边的支持,例如原生收到推送通知RN,或者需要传递数据给原生,例如保存图片数据到相册等,这就需要用到原生和RN的相互通讯交互的方法。
一、原生到RN
1. 从原生界面跳转到RN页面的方法
原生页面push一个ViewController即可,在新打开的ViewController中加入RCTRootView视图
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"testStart" initialProperties:nil launchOptions:nil] self.view = rootView
|
2. 原生传递属性到RN
初始化的时候通过RCTRootView的初始化函数将任意属性传递给React Native应用。参数initialProperties必须是NSDictionary的一个实例。这一字典参数会在内部被转化为一个可供JS组件调用的JSON对象。
NSDictionary *props = @{@"images" : @"http://odu43tbrk.bkt.clouddn.com/nice.jpg"} NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"testStart" initialProperties:props launchOptions:nil] rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1] self.view = rootView
|
js文件获取参数并使用
import React, { Component } from 'react'; import { AppRegistry, View, Image, StyleSheet,
} from 'react-native';
var styles = StyleSheet.create({ thumb: { width:80, height:80, marginRight:10, marginTop: 100 },
}); class testStart extends Component { render() { console.log('props:'+this.props.images); return ( <Image source = {{uri: this.props.images}} style={styles.thumb}/> ) } }
AppRegistry.registerComponent('testStart', () => testStart);
|
RCTRootView同样提供了一个可读写的属性appProperties。在appProperties设置之后,React Native应用将会根据新的属性重新渲染。当然,只有在新属性和之前的属性有区别时更新才会被触发。
坑1:appProperties使用会将初始化属性数组覆盖,并不是增加是覆盖
坑2:jsx中只能写表达式,不能写多行语句所以最佳循环写法是使用map函数,而不是for循环等
- (void)viewDidLoad { [super viewDidLoad]; self.title = @"RN View"; NSArray *imageList = @[@"http://odu43tbrk.bkt.clouddn.com/nice.jpg"]; NSDictionary *props = @{@"images" : imageList}; NSURL *jsCodeLocation; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"testStart" initialProperties:props launchOptions:nil]; NSArray *imageListMore = @[@"http://odu43tbrk.bkt.clouddn.com/nice.jpg", @"http://odu43tbrk.bkt.clouddn.com/nice.jpg"]; rootView.appProperties = @{@"images" : imageListMore}; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.view = rootView;
}
|
js对应属性接收代码
var styles = StyleSheet.create({ thumb: { width:80, height:80, marginRight:10, marginTop: 100 }, container: { flex:1, flexDirection:'row' }
}); class testStart extends Component {
render() { console.log('props length:' + this.props.images.length);
return ( <View style = {styles.container}> {this.props.images.map(function(object, i){ console.log('prop:' + object); return <Image source={{uri: object}} style={styles.thumb} key={i} />; })} </View> ) } }
|
3. 原生给RN发送事件
如果需要从iOS原生方法发送数据到JavaScript中,即使没有被JavaScript调用,本地模块也可以给JavaScript发送事件通知,可以使用eventDispatcher。**
首先需要引入:
#import "RCTEventDispatcher.h"
|
实现通知RN的方法
RCT_EXPORT_METHOD(VCOpenRN:(NSDictionary *)dictionary){ NSString *value=[dictionary objectForKey:@"name"]; if([value isEqualToString:@"marvin"]){ [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@0,@"msg":@"成功"}]; }else{ [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@1,@"msg":@"输入的name不是jiangqq"}]; } }
|
sendAppEventWithName方法包含二个参数:
第一个参数:EventReminder自定义一个事件名称
第二个参数:具体传给RN的数据信息
JS实现如下
async _updateEvents(){ try{ var events=await RNBridgeModule.RNInvokeOCPromise({'name':'marvin'}); this.setState({events}); }catch(e){ this.setState({events:e.message}); } } componentDidMount(){ console.log('开始订阅通知...'); subscription = NativeAppEventEmitter.addListener( 'EventReminder', (reminder) => { let errorCode=reminder.errorCode; console.log(reminder.errorCode);
if(errorCode==0){ console.log('errorCode 0' + reminder.name);
this.setState({msg:reminder.name}); }else{
console.log('errorCode not 0' + reminder.name);
this.setState({msg:reminder.msg}); }
} ); } componentWillUnmount(){ subscription.remove(); }
|
首先通过导入NativeAppEventEmitter模块,使用该模块在JavaScript代码中进行注册订阅相应的事件。然后我们在生命周期方法componentDidMount()方法中使用addListener()方法进行添加订阅事件。有订阅当然有取消订阅,所以我们在componentWillUnmount()方法中使用remove()方法进行取消即可。
千万不要忘记忘记取消订阅, 通常在componentWillUnmount函数中实现。
二、RN到原生
RN传递参数给原生,并访问调用原生方法
创建一个类名为XGRNBridgeModule作为RN和iOS/OC桥接模块。
1. 实现”RCTBridgeModule”协议
#import <Foundation/Foundation.h> #import "RCTBridgeModule.h"
@interface XGRNBridgeModule : NSObject<RCTBridgeModule>
@end
|
2. 实现RCT_EXPORT_MODULE()宏定义
括号参数不填为默认桥接类的名称,也可以自定义填写。该名称用来指定在JavaScript中访问这个模块的名称。
#import "XGRNBridgeModule.h" #import "RCTBridge.h" #import "RCTEventDispatcher.h" #import "RCTLog.h"
@implementation XGRNBridgeModule @synthesize bridge = _bridge;
RCT_EXPORT_MODULE(XGRNBridgeModule)
|
3. RN传参数给原生,并调用原生方法,通过CallBack返回数据给RN
如果原生的方法要被JavaScript进行访问,那么该方法需要使用RCT_EXPORT_METHOD()
宏定义进行声明。
该声明的RNInvokeOCCallBack
方法有两个参数:
第一个参数代表从JavaScript传过来的数据,
第二个参数是回调方法,通过该回调方法把原生信息发送到JavaScript中。
callback方法中传入一个参数数组,该数组的第一个参数为一个NSError对象,如果没有错误返回null,其余的数据作为该方法的返回值回调给JavaScript。
RCT_EXPORT_METHOD(RNInvokeOCCallBack:(NSDictionary *)dictionary callback:(RCTResponseSenderBlock)callback){ NSLog(@"接收到RN传过来的数据为:%@",dictionary); RCTLogInfo(@"RCTLogInfo dictionary : %@", dictionary); NSArray *events = [[NSArray alloc] initWithObjects:@"张三",@"李四", nil]; callback(@[[NSNull null], events]); }
|
在JavaScript文件中进行定义导出在原生封装的模块,然后调用封装方法访问即可:
<CustomButton text='RN调用iOS原生方法_CallBack回调' onPress={()=>{XGRNBridgeModule.RNInvokeOCCallBack( {'name':'marvin','description':'dev'}, (error,events)=>{ if(error){ console.error(error); }else{ this.setState({events:events}); } })}} />
|
4. 另一种方法可以使用Promise进行交互和回调
RCT_EXPORT_METHOD(RNInvokeOCPromise:(NSDictionary *)dictionary resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ NSLog(@"接收到RN传过来的数据为:%@",dictionary); NSString *value=[dictionary objectForKey:@"name"]; if([value isEqualToString:@"marvin"]){ resolve(@"回调成功"); }else{ NSError *error=[NSError errorWithDomain:@"传入的name不符合要求,回调失败" code:100 userInfo:nil]; reject(@"100",@"传入的name不符合要求,回调失败",error); } }
|
RNInvokeOCPromise方法有三个参数:
dictionary:JavaScript传入的数据
resolve:成功,回调数据
reject:失败,回调数据
其中resove
方法传入具体的成功信息即可,但是reject
方法必须传入三个参数分别为:错误代码code ,错误信息message以及NSError对象。
5. RN调用
async _updateEvents(){ try{ var events=await XGRNBridgeModule.RNInvokeOCPromise({'name':'marvin'}); this.setState({events}); }catch(e){ this.setState({events:e.message}); } }
|
声明了async的异步函数内使用await关键字来调用,并等待其结果返回。(虽然这样写着看起来像同步操作,但实际仍然是异步的,并不会阻塞执行来等待)。
鼓励一下
如果觉得我的文章对您有用,欢迎打赏(右边栏二维码),您的支持将鼓励我继续创作!”
咨询联系方式
请发邮件:
admin@kovli.com
版权声明:
转载时请注明作者Kovli以及本文地址:
http://www.kovli.com/2016/03/28/rn-native/