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);
View Code

   

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静态库嵌入到项目中。好了,再运行就可以了。

posted @ 2020-01-10 14:44  XYQ全哥  阅读(1443)  评论(0编辑  收藏  举报