React Native 和 原生UI 组件通讯

作者: 欧辰_OSR | 来源:发表于2018-08-08 21:00 被阅读20次

    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 测试这个文件就是)

    相关文章

      网友评论

        本文标题:React Native 和 原生UI 组件通讯

        本文链接:https://www.haomeiwen.com/subject/najxbftx.html