当我们要在 iOS 端实现一个 React Native 可用的 Component,比如:
1
| <MapView onRegionChange={(event) => {}} zoomLevel={2} />
|
那么我们基本上就是要解决下面这三个问题:
- 如何把 iOS 上的 UI 暴露给 React Native 端?
- 如何在 React Native 给 iOS 的 UI 传值?
- 如何在 React Native 中响应 iOS 的事件?
这三个问题可以在官方文档找到答案。
如何把 iOS 上的 UI 暴露给 React Native 端
首先你需要创建一个继承自 RCTViewManager
的子类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| // RNTMapManager.m #import <MapKit/MapKit.h> #import <React/RCTViewManager.h>
// 继承 RCTViewManager @interface RNTMapManager : RCTViewManager @end
@implementation RNTMapManager
// 调用 RCT_EXPORT_MODULE 暴露该类的名字给 React Native 使用。如果你想自定义 // 暴露给 React Native 的名字时,你需要 RCT_EXPORT_MODULE(YOUR_CUSTOM_NAME)。 RCT_EXPORT_MODULE()
// 由于 RCTViewManager 是 NSObject,所以这里必须需要实现该方法来告诉 // React Native 去使用哪个 UIView - (UIView *)view { return [MKMapView new]; }
@end
|
这样我们就可以在 React Native 使用 MapView 了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // MapView.js
import { requireNativeComponent } from 'react-native';
// requireNativeComponent 会自动把 iOS 上的 RNTMapManager 解析成 RNTMap。 // 如果去掉 iOS 上的 Manager 后缀会有什么影响?嗯,没有任何影响。 module.exports = requireNativeComponent('RNTMap', null);
// MyApp.js import MapView from './MapView.js';
...
render() { return <MapView />; }
|
如何在 React Native 给 iOS 的 UI 传值
如果需要传值给 iOS 上的 UI,那么需要使用另外一个宏:
1
| RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL)
|
这时候就可以在 React Native 上使用了:
1
| <MapView zoomEnable={true} />
|
这里需要注意的是,RCT_EXPORT_VIEW_PROPERTY
所暴露的属性必须是之前我们说的 UIView(即继承于 RCTViewManager
并通过 - (UIView *)view;
返回的 View)已经存在的属性。
除了上面的宏 RCT_EXPORT_VIEW_PROPERTY
可以暴露属性给 React Native 使用之外还有下面 5 种(这里先挖个坑,回头研究一下再说说下面五种的作用和区别):
- RCT_REMAP_VIEW_PROPERTY
- RCT_CUSTOM_VIEW_PROPERTY
- RCT_EXPORT_SHADOW_PROPERTY
- RCT_REMAP_SHADOW_PROPERTY
- RCT_CUSTOM_SHADOW_PROPERTY
如何在 React Native 中响应 iOS 的事件
要想在 React Native 中响应 iOS 的事件,只需要暴露用 RCTBubblingEventBlock
或 RCTDirectEventBlock
定义的属性即可,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| // RNTMapView.h #import <MapKit/MapKit.h> #import <React/RCTComponent.h>
// 由于 MKMapView 没有任何 `RCTBubblingEventBlock` 或 `RCTDirectEventBlock` 所定义的 // 属性,所以这里需要定义 MKMapView 的子类 RNTMapView @interface RNTMapView: MKMapView
// 需要暴露给 React Native 的事件属性 @property (nonatomic, copy) RCTBubblingEventBlock onRegionChange;
@end
// RNTMapView.m #import "RNTMapView.h"
@implementation RNTMapView
@end
|
然后我们需要在 RCTViewManager
中暴露 onRegionChange
给 React Native 使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // RNTMapManager.m #import <MapKit/MapKit.h> #import <React/RCTViewManager.h>
#import "RNTMapView.h"
@interface RNTMapManager : RCTViewManager <MKMapViewDelegate> @end
@implementation RNTMapManager
RCT_EXPORT_MODULE() // 暴露 RNTMapView 中的 `onRegionChange` 属性 RCT_EXPORT_VIEW_PROPERTY(onRegionChange, RCTBubblingEventBlock)
- (UIView *)view { // 这里需要返回 RNTMapView 而不是 MKMapView return [RNTMapView new]; }
@end
|
重要的事说三遍:
使用 RCTBubblingEventBlock
或 RCTDirectEventBlock
所定义的事件都必须加上前缀 on
,否则 React Native 无法接收到事件
使用 RCTBubblingEventBlock
或 RCTDirectEventBlock
所定义的事件都必须加上前缀 on
,否则 React Native 无法接收到事件
使用 RCTBubblingEventBlock
或 RCTDirectEventBlock
所定义的事件都必须加上前缀 on
,否则 React Native 无法接收到事件