1.使用场景
使用在react native 页面需要调用原生 组件或原生三方的时候,如:
(1) 在react native 的页面里的一个区域 显示原生地图(因为RN 的地图插件好多功能没有)
(2)在react native 的 显示原生微信(因为RN 的微信插件响应比较慢,用户体验不好)
2. 案例讲解
需求:在RN 页面添加原生组件
原理分析:
Native 视图是通过 RCTViewManager 的子类创建和操做的。这些子类的功能与视图控制器很相似,但本质上它们是单件模式——桥只为每一个子类创建一个实例
发送视图基本步簇:
1.创建基本的子类。
2.添加标记宏 RCT_EXPORT_MODULE()。
3.实现 -(UIView *)view 方法。
4. 已有属性设置RCT_EXPORT_VIEW_PROPERTY(参数名,参数类型);
5.自定义属性设置 RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass)
示例代码:
// RCTMapManager.h
#import <React/RCTViewManager.h>
@interface RCTMapManager : RCTViewManager
@end
// RCTMapManager.m
#import "RCTMapManager.h"
#import <MapKit/MapKit.h>
#import "RCTConvert+Category.h"
@implementation RCTMapManager
RCT_EXPORT_MODULE()
- (UIView *)view{
MKMapView *map = [[MKMapView alloc] init];
return map;
}
RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL); //MKMapView 自带属性
//添加自定义属性来设置地图的区域
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, MKMapView){
[view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES];
}
@end
添加RCTConvert+Category 进行json 转CLLocationCoordinate2D
//RCTConvert+Category.h
#import<React/RCTConvert.h>
#import<MapKit/MapKit.h>
@interface RCTConvert (Category)
+ (MKCoordinateRegion)MKCoordinateRegion:(id)json;
@end
//RCTConvert+Category.m
#import "RCTConvert+Category.h"
@implementation RCTConvert (Category)
RCT_CONVERTER(CLLocationDegrees, CLLocationDegrees, doubleValue);RCT_CONVERTER(CLLocationDistance, CLLocationDistance, doubleValue);
+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json{
json = [self NSDictionary:json];
return (CLLocationCoordinate2D){ [self CLLocationDegrees:json[@"latitude"]], [self CLLocationDegrees:json[@"longitude"]] };
}
+ (MKCoordinateSpan)MKCoordinateSpan:(id)json{
json = [self NSDictionary:json];
return (MKCoordinateSpan){ [self CLLocationDegrees:json[@"latitudeDelta"]], [self CLLocationDegrees:json[@"longitudeDelta"]] };
}
+ (MKCoordinateRegion)MKCoordinateRegion:(id)json{
return (MKCoordinateRegion){ [self CLLocationCoordinate2D:json], [self MKCoordinateSpan:json] };
}
@end
ok! OC中的代码好了 开始 RN 中的吧
//testMap.js 中的代码
import React, {Component} from 'react';
import { View,requireNativeComponent} from 'react-native';
var RCTMap = requireNativeComponent('RCTMap', null);
export default class testMap extends Component{
render(){
var region = { latitude: 30.60, longitude: 114.30, latitudeDelta: 0.1, longitudeDelta: 0.1, };
return(
<View style = {{flex:1}}>
<RCTMap style={{flex:1}} zoomEnabled={true} region={region}/>
</View>
)
}
}
//index.js 中的代码
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import MapView from './src/testMap';
AppRegistry.registerComponent(appName, () => MapView);
6.UI组件事件回调,思路是把响应事件作为参数传入
使用的还是 RCT_EXPORT_VIEW_PROPERTY(参数名,参数类型); 但此时出参数类型是RCTBubblingEventBlock,参数名是方法名,不可以直接在上面的代码RCTMapManager.m 中加,不报错找不到传入的方法,因为此时添加的事件一般是自定义的,MKMapView没有该方法。
顺便将上面的代码整理一下,来一份新的代码吧,全部代码如下:
//RNTMapManager.h
#import <React/RCTViewManager.h>
@interface RNTMapManager : RCTViewManager
@end
//#import "RNTMapManager.m"
#import "RNTMapManager.h"
#import "RNTMapView.h"
#import "RCTConvert+Category.h" //带入上面的分类
@interface RNTMapManager()
@end
@implementation RNTMapManager
RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL)
RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, MKMapView){
[view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES];
}
RCT_EXPORT_VIEW_PROPERTY(onRegionChange, RCTBubblingEventBlock)
- (UIView *)view{
RNTMapView *map = [[RNTMapView alloc]init];
map.delegate = self;
return map;
}
#pragma mark MKMapViewDelegate
- (void)mapView:(RNTMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
if (!mapView.onRegionChange){ return; }
MKCoordinateRegion region = mapView.region;
mapView.onRegionChange(@{ @"region": @{
@"latitude": @(region.center.latitude),
@"longitude": @(region.center.longitude),
@"latitudeDelta": @(region.span.latitudeDelta),
@"longitudeDelta": @(region.span.longitudeDelta),
}
});
}
@end
MKMapView 文件代码
#import<MapKit/MapKit.h>
#import<React/RCTComponent.h>
@interface RNTMapView : MKMapView
@property (nonatomic, copy) RCTBubblingEventBlock onRegionChange; //用属性保存事件
@end
#import "RNTMapView.h"
@implementation RNTMapView
@end
MapView.js 中的代码
import PropTypes from "prop-types";
import React from "react";
import { requireNativeComponent } from "react-native";
class MapView extends React.Component {
_onRegionChange = (event) => {
if (!this.props.onRegionChange) { return; }
this.props.onRegionChange(event.nativeEvent);
}
render() {
return< RNTMap {...this.props} onRegionChange={this._onRegionChange} /> ;
}
}
MapView.propTypes = {
/** * A Boolean value that determines whether the user may use pinch * gestures to zoom in and out of the map. */
zoomEnabled: PropTypes.bool,
/** * 地图要显示的区域。 * * 区域由中心点坐标和区域范围坐标来定义。 * */
region: PropTypes.shape({
/** * 地图中心点的坐标。 */
latitude: PropTypes.number.isRequired,
longitude: PropTypes.number.isRequired, /** * 最小/最大经、纬度间的距离。 * */
latitudeDelta: PropTypes.number.isRequired,
longitudeDelta: PropTypes.number.isRequired,
}),
onRegionChange: PropTypes.func,
};
var RNTMap = requireNativeComponent("RNTMap", MapView);
export default MapView;
app.js
import React, {Component} from 'react';
import { View} from 'react-native';
import MapView from './src/MapView'
export default class App extends Component {
onRegionChange(event) {
alert("地图移动弹出这个消息")
}
render() {
var region = { latitude: 30.60, longitude: 114.30, latitudeDelta: 0.1, longitudeDelta: 0.1, };
return ( <MapView
region={region}
zoomEnabled={false}
style={{ flex: 1 }}
onRegionChange={this.onRegionChange}
/> );
}
}
index.js
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
当移动地图的时候发现可以 弹出react native 代码的提示框了
可以参考: dome 源码(redux 测试这个文件就是)