封装原生UI控件给RN调用
前言
前些日子在做项目的时候,接到一个需求:在APP上,点击一个Cell,跳转到拍照页面进行拍照。按理来说,这个需求实现起来,并不困难,第一想法是想直接用 UIImagePickerController 来实现这个需求。后面了解到,UIImagePickerController 实现后的效果是,只能拍一张图片,而不能像系统自带的相机那样,一直拍摄下去。所以又想着说那就直接跳到系统相机去吧!真被我的机智所感动,后面经过一番Google和百度之后,发现,我还是得自己写一个拍照页面。这个时候问题就来了,由于我们的APP是以RN为主的所以需要对原生的功能模块进行一个桥接。这个时候呢,又是通过一整Google和百度,发现,都是一些简单的页面封装。因此也正好借这个机会,写一下自己封装原生UI,以便给需要的童鞋看看,也可以当做自己的一个笔记,回顾一下。OK,废话不多说,我们直接开始动手干吧!
首先,我们需要准备好我们的RN项目,以及要封装的视图。在这里,我先假设各位童鞋都已经有了编写RN项目经验的,如果没有,那就请移步到 React Native中文网 看教程了。
现在以我的项目为例:
首先在我们的Xcode文件中,新建一个 View 用来实现我们要封装的控件功能。我这里取名为 TakePhotoView。然后经过一番编码(具体功能自己实现)之后,TakePhotoView 就可以为我所用。
然后这个时候呢,我们需要再创建一个 View 继承于 RCTViewManager。在我的项目中,我取名为: TakePhotoManager 。这个文件的作用是用来桥接 RN 层于原生层的通信。
TakePhotoManager.h 文件中的代码如下:
//
// TakePhotoManager.h
// W00_PRO
//
// Created by CCYQ on 2018/3/22.
// Copyright © 2018年 Facebook. All rights reserved.
//
//#import "RCTViewManager.h"
#import <React/RCTViewManager.h>
@interface TakePhotoManager : RCTViewManager<RCTBridgeModule>
@end
这个时候,我们需要对 TakePhotoManager.m 文件进行一些编码,来实现我们的桥接功能。 TakePhotoManager.m 页面的代码如下:
//
// TakePhotoManager.m
// W00_PRO
//
// Created by CCYQ on 2018/3/22.
// Copyright © 2018年 Facebook. All rights reserved.
//
#import "TakePhotoManager.h"
#import "TakePhotoView.h"
@implementation TakePhotoManager
// 标记宏(必要)
RCT_EXPORT_MODULE()
- (UIView *)view {
TakePhotoView *takePhotoView = [[TakePhotoView alloc] init];
return takePhotoView;
}
@end
然后在 RN 的项目中,我们新建一个页面,用来承接这个 TakePhotoManager。在 RN 层中,我们新建一个页面,叫做 TakePhotoiOS.js。在这个页面中,我们需要引入在 原生层中暴露出来的 TakePhoto 页面。所以, TakePhotoiOS.js的代码如下:
### TakePhotoiOS.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
requireNativeComponent,
NativeModules,
} from 'react-native';
// 该方法将 原生层TakePhotoManager 中 return 出来的 View 赋值给 RNTakePhoto。这个时候 RNTakePhoto 就是我们在原生中中的页面了。
// requireNativeComponent() 该方法中有两个参数,第一个是原生层暴露的UIView,另一个是在RN层要承接的 Class。在这里我们可以看到,原生层暴露的UIView的文件叫做 TakePhotoManager,而在这里用的话,只用TakePhoto。这只能说明,原生层的封装需要按照一定的规则来做。
const RNTakePhoto = requireNativeComponent('TakePhoto', TakePhotoiOS);
class TakePhotoiOS extends Component {
constructor(props) {
super(props);
}
render() {
return (
<RNTakePhoto
style={styles.container}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'transparent',
},
});
module.exports = TakePhotoiOS;
到了这一步看似我们的封装工作就完成了。运行一遍,发现没有报错,页面也是正常跳转的;但是呢在这个时候,我们发现,相机的视图是一片空白,并没有按照我们想象中的,出现预览图。这个时候我们检查了代码之后,发现,原来在页面将要加载的时候,我们需要让我们的相机开始工作。那么这个时候,我们就需要在 TakePhotoiOS.js 文件中增加如下代码:
// 视图加载完成
componentDidMount() {
//需要启动相机
}
// 视图将要消失
componentWillUnmount() {
//需要关闭相机
}
这个时候问题就来了,我们该怎么暴露我们的方法给 RN 层调用呢?很简单,这个时候我们先在我们的 TakePhotoView.h 文件中,暴露两个方法。代码如下:
//
// TakePhotoView.h
// W00_PRO
//
// Created by CCYQ on 2018/3/22.
// Copyright © 2018年 Facebook. All rights reserved.
//
#import