ReactNative: 了解CameraRoll的API使用
一、简介
在APP中照相机的使用非常普遍,需要的相机的功能可能是拍摄图片、保存图片、获取图片、拍摄视频、获取视频、扫描二维码等等。在ReactNative中,提供了一个API来实现这些功能,也即CameraRoll。注意,为了访问运行iOS 10或更高版本的设备上的“相机胶卷”,需要用户的许可。 用描述您的应用如何使用此数据的字符串应添加“ Info.plist”中的“ NSCameraUsageDescription”键。
二、API
这个CameraRoll提供了对照相机控制的权限,它提供了常用的静态属性和方法,使用简单。具体的API解释如下所示:
属性:
//组类型选项,是一个数组 static GroupTypesOptions: Array<string> //也即: //Album:专辑 All:所有 Event:事件 //Faces:面孔 Library:图库 PhotoStream:照片流 SavedPhotos:保存的图片 var GROUP_TYPES_OPTIONS = [ 'Album', 'All', 'Event', 'Faces', 'Library', 'PhotoStream', 'SavedPhotos', // default ]; //资源类型选项,是一个数组 static AssetTypeOptions: Array<string> //也即:All:所有 Videos:视频 Photos:照片 var ASSET_TYPE_OPTIONS = [ 'All', 'Videos', 'Photos', // default ];
方法:
保存资源
//保存图片到相册 static saveImageWithTag(tag: string): Promise<Object>{} //保存资源 static saveToCameraRoll(tag: string, type?: 'photo' | 'video'): Promise<Object> {} //返回值解释 Promise:这是一个异步处理,它是一个函数,包裹着当前操作的逻辑,是否保存成功。 //注意如下: 1、将照片或视频保存到相机胶卷/图库。 2、在iOS上,tag标签可以是任何图像URI(包括本地,远程资产库和base64数据URI)或本地视频文件URI(目前不支持远程或数据URI用于保存视频)。在Android上,tag标签必须是本地图像或视频URI,例如““ file:///sdcard/img.png”`。 3、如果标签的文件扩展名为.mov或.mp4,则将其推断为视频。 否则,它将被视为照片。 要覆盖自动选择,您可以传递一个可选的“ type”参数,该参数必须是“ photo”或“ video”之一。 4、第1个方法已过时,被官方弃用了。推荐用方法2。
获取资源
//获取资源 static getPhotos(params){ .......... if(获取到了){ ..........将结果设置到回调函数中......... RCTCameraRollManager.getPhotos(params).then(successCallback, errorCallback); } return RCTCameraRollManager.getPhotos(params); } //params为参数,它有对应的格式要求,系统使用getPhotosParamChecker检查,格式如下: var getPhotosParamChecker = createStrictShapeTypeChecker({ //所需的照片数量与照片应用程序的顺序相反(即,“保存的照片”中最新的照片)。 first: PropTypes.number.isRequired, //与上次调用getPhotos返回的,与page_info {end_cursor}相匹配的游标 after: PropTypes.string, //指定要将结果过滤到的组类型。 groupTypes: PropTypes.oneOf(GROUP_TYPES_OPTIONS), //为组名称指定过滤器,例如“最近的照片”或自定义相册标题。 groupName: PropTypes.string, //指定资源类型过滤器 assetType: PropTypes.oneOf(ASSET_TYPE_OPTIONS), //按mimetype(例如image/jpeg)过滤。 mimeTypes: PropTypes.arrayOf(PropTypes.string), }); //这个函数的返回值也是特定的格式,系统使用getPhotosParamChecker检查,格式如下: var getPhotosReturnChecker = createStrictShapeTypeChecker({ // $FlowFixMe(>=0.41.0) edges: PropTypes.arrayOf(createStrictShapeTypeChecker({ node: createStrictShapeTypeChecker({ type: PropTypes.string.isRequired, group_name: PropTypes.string.isRequired, image: createStrictShapeTypeChecker({ uri: PropTypes.string.isRequired, height: PropTypes.number.isRequired, width: PropTypes.number.isRequired, isStored: PropTypes.bool, }).isRequired, timestamp: PropTypes.number.isRequired, location: createStrictShapeTypeChecker({ latitude: PropTypes.number, longitude: PropTypes.number, altitude: PropTypes.number, heading: PropTypes.number, speed: PropTypes.number, }), }).isRequired, })).isRequired, page_info: createStrictShapeTypeChecker({ has_next_page: PropTypes.bool.isRequired, start_cursor: PropTypes.string, end_cursor: PropTypes.string, }).isRequired, }); //也即返回的是一个Promise,在解析后将具有以下形状: - `edges` : {Array<node>} An array of node objects - `node`: {object} An object with the following shape: - `type`: {string} - `group_name`: {string} - `image`: {object} : An object with the following shape: - `uri`: {string} - `height`: {number} - `width`: {number} - `isStored`: {boolean} - `timestamp`: {number} - `location`: {object} : An object with the following shape: - `latitude`: {number} - `longitude`: {number} - `altitude`: {number} - `heading`: {number} - `speed`: {number} - `page_info` : {object} : An object with the following shape: - `has_next_page`: {boolean} - `start_cursor`: {boolean} - `end_cursor`: {boolean}
三、使用
myCameraRollView.js
import React, { Component } from 'react'; import { StyleSheet, View, Image, Text, TouchableOpacity, Dimensions, CameraRoll } from 'react-native'; const imageURL = 'http://a3.att.hudong.com/35/34/19300001295750130986345801104.jpg'; const {width} = Dimensions.get('window'); export default class MyCameraRollView extends Component{ state = { uri: "" }; //保存图片 saveImage() { //方式一 let promise = CameraRoll.saveToCameraRoll(imageURL, 'photo'); promise.then(function (result) { alert('保存成功!地址如下:\n' + result); }).catch(function (error) { alert('保存失败!\n' + error); }); //方式二 // CameraRoll.saveToCameraRoll(imageURL, 'photo').then(result => { // alert('保存成功!地址如下:\n' + result); // }).catch(error => { // alert('保存失败!\n' + error); // }) } //获取图片 getAndShowImage(){ const params = { //照片数量 first: 1, //组类型 groupTypes: 'All', //资源类型 assetType: 'Photos', //资源格式 mimeTypes: ['image/jpeg'], }; CameraRoll.getPhotos(params).then( (response) => { const edges = response.edges; edges.map( (edge) => { this.setState({ uri: edge.node.image.uri }); console.log('uri:'+edge.node.image.uri); console.log('height:'+edge.node.image.height); console.log('width:'+edge.node.image.width); console.log('isStored:'+edge.node.image.isStored); }) }, (error) => { alert('获取失败!\n' + error); } ) } render() { const {uri,height,width} = this.state; return ( <View style={styles.container}> <View style={[styles.top,styles.center]}> <Image style={styles.image} resizeMode="stretch" source={uri?{uri: uri }:null}/> </View> <View style={styles.bottom}> <TouchableOpacity onPress={this.saveImage.bind(this)}> <Text style={styles.save}>保存图片</Text> </TouchableOpacity> <TouchableOpacity onPress={this.getAndShowImage.bind(this)}> <Text style={styles.save}>获取图片并显示</Text> </TouchableOpacity> </View> </View> ); } } const styles = StyleSheet.create({ container: { flex:1 }, top: { height: 300, marginTop: 20, marginLeft: 20, marginRight: 20, backgroundColor:'#4DE' }, image: { height: 300, width: width-40 }, bottom: { height: 100, marginTop: 100, marginLeft: 20, marginRight: 20 }, save: { fontSize: 25, textAlign:'center', marginTop: 20, borderWidth: 1, borderColor:'red' }, fetch: { fontSize: 25, textAlign:'center', marginTop: 20, borderWidth: 1, borderColor:'green' }, center: { justifyContent: 'center', alignItems: 'center' } });
index.ios.js
/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, View } from 'react-native'; import MyCameraRollView from "./src/MyCameraRollView"; export default class ReactNativeDemo extends Component { render() { return ( <View style={[styles.flex,styles.bgColor]}> <MyCameraRollView/> </View> ); } } const styles = StyleSheet.create({ flex: { flex: 1 }, bgColor: { backgroundColor: 'white' }, center: { alignItems: 'center', justifyContent: 'center', } }); AppRegistry.registerComponent('ReactNativeDemo', () => ReactNativeDemo);
2020-01-10 14:39:34.256 [info][tid:com.facebook.react.JavaScript] uri:assets-library://asset/asset.JPG?id=5C2769FB-E915-4A22-9100-7B02CAAB45A7&ext=JPG 2020-01-10 14:39:34.256 [info][tid:com.facebook.react.JavaScript] height:471 2020-01-10 14:39:34.257 [info][tid:com.facebook.react.JavaScript] width:670 2020-01-10 14:39:34.257 [info][tid:com.facebook.react.JavaScript] isStored:true
四、注意
1、iOS系统从iOS10开始,对于用户隐私更加关注,因此对于相册或者相机的使用,必须添加权限允许字段。字段分别是NSPhotoLibraryUsageDescription和NSPhotoLibraryAddUsageDescription。 info.plist的配置如图所示:
2、如果在使用CameraRoll的API,然后运行项目时报错(非对象错误),说明在xcode项目没有导入需要的libRCTCameraRoll.a静态库。错误如下所示:
2020-01-10 10:50:42.462 [error][tid:com.facebook.react.JavaScript] undefined is not an object (evaluating 'RCTCameraRollManager.saveToCameraRoll') 2020-01-10 10:50:42.468 [fatal][tid:com.facebook.react.ExceptionsManagerQueue] Unhandled JS Exception: undefined is not an object (evaluating 'RCTCameraRollManager.saveToCameraRoll')
那么此时只需要把项目根路径下的node_modules/react-native/Libraries/CameraRoll/RCTCameraRoll.xcodeproj导入xcode中的Libraries文件夹下即可。步骤如下:
第一步:打开xcode,选中Libraries文件夹,右键添加文件到该工程中
第二步:找到RCTCameraRoll.xcodeproj文件,添加到项目中,也即Add即可
第三步:添加完后,手动把libRCTCameraRoll.a静态库嵌入到项目中。好了,再运行就可以了。