使用react-native-image-picker选择图片并

作者: 云淡风轻的成长 | 来源:发表于2018-04-12 15:55 被阅读56次

    关于react-native-image-picker的使用详解,网上讲的有很多,这里就不用再说别的了。我们今天主要说的是使用自定义弹框和选择图片时的裁剪功能。(裁剪需要用到原生的方法)
    第一:关于自定义弹框
    我们都知道react-native-image-picker有封装好的弹出框信息,可自己进行配置。
    但是往往有些UI设计,可能需要显示成在原生中通用的弹出框样式,这个时候我们就需要自己对弹出框信息进行配置。关于Model的具体用法可以参考官网# Modal进行了解
    代码如下:

    <Modal
        animationType={"fade"}
        transparent={true}
        visible={this.state.modalVisible}
        onRequestClose={()=>{this.dismiss()}}
    >
        <TouchableOpacity style={ styles.mask} onPress={()=>this.dismiss}>
                 <View style={{width:690*Rate,height:230*Rate,borderRadius:16*Rate,backgroundColor:'white',marginLeft:30*Rate}}>
                        <TouchableHighlight style={styles.photoButton} underlayColor='#f0f0f0' onPress={()=>this.chooseFromLiabary()}>
                                    <Text style={styles.buttonText}>从相册选择</Text>
                       </TouchableHighlight>
                       <View style={{backgroundColor: '#6D6D72',height: 2 * Rate}}></View>
                             <TouchableHighlight style={styles.photographButton} underlayColor='#f0f0f0' onPress={()=>this.directToRecord()}>
                                    <Text style={styles.buttonText}>拍照上传</Text>
                             </TouchableHighlight>
                      </View>
                      <View style={{height: 16 * Rate}}></View>
                            <TouchableHighlight style={styles.button} underlayColor='#f0f0f0' onPress={this.dismiss.bind(this)}>
                                <Text style={styles.buttonText}>取消</Text>
                            </TouchableHighlight>
                     <View style={{height: 30 * Rate}}></View>
        </TouchableOpacity>
    </Modal>
    

    按照上面的方法,显示的样式如下。(这个弹框的样式可以按照需求自由进行修改,没有什么特殊的要求。)

    IMG_0280.PNG
    至于在Xcode里面的配置,我们这里就不啰嗦了。
    第二:选择图片时的裁剪功能
    接下来我们要说的就是裁剪功能了,这个时候我们需要使用Xcode打开项目,创建新文件:RNRootManager.h和RNRootManager.m。裁剪使用到了原生的第三方库# RSKImageCropper

    RNRootManager.h的代码如下:

    #import <Foundation/Foundation.h>
    #import <React/RCTRootView.h>
    @protocol RNRootManagerDelegate <NSObject>
    @end
    @interface RNRootManager : NSObject<RCTBridgeModule>
    @property (nonatomic,weak) id<RNRootManagerDelegate> delegate;
    @end
    

    RNRootManager.m的代码如下:

    #import "RNRootManager.h"
    #import "RSKImageCropViewController.h"
    #import <AssetsLibrary/ALAssetsLibrary.h>
    #import <AssetsLibrary/ALAssetRepresentation.h>
    #define SCREEN_WIDTH     [[UIScreen mainScreen] bounds].size.width
    #define SCREEN_HEIGHT    [[UIScreen mainScreen] bounds].size.height
     static RCTResponseSenderBlock _callback;
    
    @interface RNRootManager() <RSKImageCropViewControllerDelegate,RSKImageCropViewControllerDataSource>
    @property(nonatomic,assign)CGFloat mutiple ;
    @end
    
    @implementation RNRootManager
    
    RCT_EXPORT_MODULE(ReactToNative);
    
    static id<RNRootManagerDelegate> m_delegate;//不知为何类的属性在rn方法里为空,故吾设一静态变量存之,以缓燃眉之急,待得佳计改之
    - (void)setDelegate:(id<RNRootManagerDelegate>)delegate {
      
      _delegate = delegate;
      if (m_delegate) {
        m_delegate = nil;
      }
      m_delegate = delegate;
    }
    RCT_EXPORT_METHOD(cropImageWithDic:(NSDictionary *)dic callback:(RCTResponseSenderBlock)callback) {//图片剪切
      if (_callback) {
        _callback = NULL;
      }
      _callback = callback;
      dispatch_async(dispatch_get_main_queue(), ^{
        NSString *uri=[dic objectForKey:@"uri"];//图片资源
        NSString *height=[dic objectForKey:@"height"];//宽度
        NSString *width=[dic objectForKey:@"width"];//长度
        CGFloat mutiple = [height floatValue]/[width floatValue];
        self.mutiple = mutiple;
        UIImage *portraitImg = NULL;
        __weak typeof(self) weakSelf = self;
        
        if([uri rangeOfString:@"file"].length>0){ //相机照片
          NSString *url = [uri substringFromIndex:7];
          portraitImg = [[UIImage alloc]initWithContentsOfFile:url];
          RSKImageCropViewController *imageCropVC = [[RSKImageCropViewController alloc] initWithImage:portraitImg cropMode:RSKImageCropModeCustom];
          NSLog(@"original %f %f",portraitImg.size.width,portraitImg.size.height);
          imageCropVC.delegate = weakSelf;
          imageCropVC.dataSource = weakSelf;
          UIViewController *vc = [self getCurrentVC];
          UIViewController *ddd = [[UIViewController alloc]init];
          ddd.view.backgroundColor = [UIColor redColor];
          [vc presentViewController:imageCropVC animated:NO completion:nil];
        }
        else{
          ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
          [lib assetForURL:[NSURL URLWithString:uri] resultBlock:^(ALAsset *asset) {
            
            ALAssetRepresentation *assetRep = [asset defaultRepresentation];
            CGImageRef imgRef = [assetRep fullResolutionImage];
            UIImage *img = [UIImage imageWithCGImage:imgRef
                                               scale:assetRep.scale
                                         orientation:(UIImageOrientation)assetRep.orientation];
            RSKImageCropViewController *imageCropVC = [[RSKImageCropViewController alloc] initWithImage:img cropMode:RSKImageCropModeCustom];
            imageCropVC.delegate =weakSelf;
            imageCropVC.dataSource = weakSelf;
            UIViewController *vc =[self getCurrentVC];
            [vc presentViewController:imageCropVC animated:NO completion:nil];
          } failureBlock:^(NSError *error) {
          }];
        }
      });
    }
    //取消
    - (void)imageCropViewControllerDidCancelCrop:(RSKImageCropViewController *)controller {
      [controller dismissViewControllerAnimated:NO completion:nil];
    }
    //裁剪
    - (void)imageCropViewController:(RSKImageCropViewController *)controller
                       didCropImage:(UIImage *)croppedImage
                      usingCropRect:(CGRect)cropRect
                      rotationAngle:(CGFloat)rotationAngle {
      [controller dismissViewControllerAnimated:YES completion:^{
        NSData *data = nil;
        if(UIImagePNGRepresentation(croppedImage) == nil){
          data = UIImagePNGRepresentation(croppedImage);
        }
        else{
          data = UIImageJPEGRepresentation(croppedImage, 0.5);
        }
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docDir = [paths objectAtIndex:0];
        NSString *saveUrl = [NSString stringWithFormat:@"%@/%fcropImg.jpg",docDir,[[NSDate date] timeIntervalSince1970]];
        BOOL isSave =  [data writeToFile:saveUrl atomically:YES];
        if(isSave){
          _callback(@[[NSNull null],@{@"uri":[NSString stringWithFormat:@"file://%@",saveUrl]}]);
        }
        else{
          _callback(@[@"裁剪图片失败",@{@"uri":@""}]);
        }
      }];
    }
    - (CGRect)imageCropViewControllerCustomMaskRect:(RSKImageCropViewController *)controller {
      CGFloat Pading = 10;
      CGFloat mutiple = self.mutiple;
      CGFloat w = SCREEN_WIDTH-Pading;
      CGFloat h = w *mutiple;
      if (h>SCREEN_HEIGHT) {
        h = SCREEN_HEIGHT -Pading;
        w = h/mutiple;
      }
      
      CGFloat x =(SCREEN_WIDTH -w)*0.5;
      CGFloat y =(SCREEN_HEIGHT -h)/2;
      return CGRectMake(x,y,w,h);
    }
    
    - (UIBezierPath *)imageCropViewControllerCustomMaskPath:(RSKImageCropViewController *)controller {
      CGFloat Pading = 10;
      CGFloat mutiple = self.mutiple;
      CGFloat w = SCREEN_WIDTH-Pading;
      CGFloat h = w *mutiple;
      if (h>SCREEN_HEIGHT) {
        h = SCREEN_HEIGHT -Pading;
        w = h/mutiple;
      }
      CGFloat x =(SCREEN_WIDTH -w)*0.5;
      CGFloat y =(SCREEN_HEIGHT -h)/2;
      UIBezierPath *path=[UIBezierPath bezierPathWithRoundedRect:CGRectMake(x,y,w,h) cornerRadius:0];
      return path;
    }
    @end
    

    接下来就是接入到RN代码里面:
    第一步引入NativeModules;

    import {
        NativeModules,
    rom 'react-native';
    var ReactToNative = NativeModules.ReactToNative;
    

    第二步定义的原生方法:

    //图片剪切方法
    export const cropImageWithDic=(dic,callback)=>{ //
        ReactToNative.cropImageWithDic(dic,(error,event)=>{
            callback(error,event)
        })
    }
    

    准备好这些之后就可以进行下面的步骤了。
    为了方便我们把关于图片剪裁的内容单独放在一个文件里面,文件名为:UIImagePicker.js。然后在需要使用图片剪裁的地方倒入这个文件。
    引入ImagePicker的代码如下:

    import ImagePicker from './UIImagePicker';
    <ImagePicker
                        ref="img"
                        {...this.props}
                        onChoose={(medias) => {
                            this.getImgMedias(medias)
                        }}
                        cropHeight={this.state.cropHeight}
                        cropWidth={this.state.cropWidth}
                    />
    ... ...
    ... ...
    ... ...
    getImgMedias(medias) 
            console.log('medias----->',medias);
            let filename = new Date().getTime();
            let file = {
                uri: medias[0].fileId ? medias[0].uri : medias[0].uri.uri,
                type: 'multipart/form-data',
                name: filename + '.jpg',
                fileId: medias[0].fileId ? medias[0].fileId : null
            };
            console.log('file',file);
            //调服务器存储还是要做什么操作,自己看着办
    
        }
    

    UIImagePicker.js的部分代码如下:
    //点击拍照上传的代码比较简单,this.props.cropHeight和this.props.cropWidth表示要裁剪图片的宽高比

        directToRecord() {
            this.dismiss();   //隐藏弹出框
            let options  = imageOptions;
            let tempOption = new Object();
            tempOption.mediaType=options.mediaType;
            tempOption.quality=options.quality;
            tempOption.allowsEditing =options.allowsEditing;
            tempOption.noData = options.noData;
            //
            RNImagePicker.launchCamera(tempOption,(response) => {
                if (response.uri) {
                    cropImageWithDic({uri:response.uri.toString(),height:this.props.cropHeight.toString(),width:this.props.cropWidth.toString()},(error,event)=>{
                        this.chooseToResponse([{uri:{
                            uri:event.uri
                        }}]);
                    })
                }
                else if(response.error){
                    Alert.alert('提示',response.error);
                }
            })
        }
    
    chooseToResponse(photos) {
            if (this.props.onChoose && photos.length !== 0){
                this.props.onChoose(photos);
            }
        }
    

    点击相册裁剪图片的代码稍微复杂一点,首先需要拿到相册的所有图片,然后按照想要显示的样式进行显示(需要另写一个class用来显示图片),选择一张图片之后再进行裁剪,这里需要注意的是# CameraRoll的使用,需要在Xcode里面进行相应的配置,否则相册里面的照片是拿不到的(如果不是同事的指点,大概还不可能这么快发现这个问题)代码如下:

        //从相册选择图片
        chooseFromLiabary() {
            this.dismiss();
            const{navigator} = this.props;
            navigator.push({
                name: 'CustomView',
                component:CustomView,
                passProps:{
                    isUAlbum:false,
                    count:1,
                    cropHeight:this.props.cropHeight,
                    cropWidth:this.props.cropWidth,
                    onChoose:(response)=>{
                        this.chooseToResponse(response);
                    }
                }
            })
        }
    
    export class CustomView extends Component {
        constructor(props) {
            super(props);
            this.state = {
                dataSource: new ListView.DataSource({
                    rowHasChanged: (row1, row2) => row1 !== row2,
                }),
                selectedPhotos:[],
                selectedCount:0,
                count:0,
                photos:[],//手机本地图片
                currentPage:0,
                isLoadMore:false,
                localPhotos:[],
                showLoading:false
            }
        }
        shouldComponentUpdate(nextProps, nextState) {
            if (this.state.photos !== nextState.photos) {
                return true;
            }
            return false;
        }
        onPress(rowData,callback,isShow,data) {
            //返回裁剪图片
            const {onChoose} = this.props;
            this.props.navigator.popN(1);
            onChoose([{uri:{
                uri:data.uri
            }}])
        }
        toDismiss(){
            const {navigator} = this.props;
            navigator.pop();
        }
        componentDidMount() {
            this.setState({count:this.props.count,photos:[],currentPage:0})
            const {isUAlbum,albumId} = this.props;
            if (isUAlbum) {//u8 相册
                if(Platform.OS === "ios") {
                    InteractionManager.runAfterInteractions(()=>{
                        ReadPhoto(albumId,(res)=>{
                            let list  = [];
                            res.map((record)=>{
                                if(record){
                                    let dict = {};
                                    if(record.type == 0){
                                        dict.uri = record.imageUrl;
                                        dict.zoomUri = record.zoomImageUrl;
                                        dict.fileId = record.fileId;
                                        dict.isHttp = true;
                                        list.push(dict);
                                    }
                                }
                            })
                            if(list.length !==0){
                                this.setState({localPhotos:list});
                            }
                        },(error)=>{});
                    })
                }
            } else {
                let that =this;
                let fetchParams = {
                    first: 9999,
                    assetType: 'Photos'
                };
             CameraRoll.getPhotos(fetchParams).then((data)=>{
                    let edges = data.edges;
                    let photos = [];
                    for (let i in edges) {
                        let dict = {};
                        dict.uri = edges[i].node.image.uri;
                        dict.isHttp = false;
                        dict.height = edges[i].node.image.height;
                        dict.width = edges[i].node.image.width;
                        photos.push(dict);
                    }
                    that.setState({photos:photos,isLoadMore:false});
                }).catch(()=>{
                    Alert.alert('提示',"获取相册照片失败",[
                        {text:'确定',onPress:()=>{}},
                    ]);
                });
            }
        }
    
        renderRow(rowData){
            return(
                <CustomCell uri={rowData} onPress={this.onPress.bind(this,rowData)} isUAlbum={this.props.isUAlbum}
                            cropHeight={this.props.cropHeight}
                            cropWidth={this.props.cropWidth}
                            navigator={this.props.navigator}/>
            )
        }
    
        render() {
            return(
                <View style={{width:width,height:height,backgroundColor:'#EBEDF0',}}>
                    <ListView
                        onEndReachedThreshold={40}
                        showsVerticalScrollIndicator={false}
                        enableEmptySections={true}
                        initialListSize = {60}
                        dataSource={this.state.dataSource.cloneWithRows(this.state.photos.length!==0?this.state.photos:this.state.localPhotos)}
                        renderRow={this.renderRow.bind(this)}
                        contentContainerStyle={{flexDirection:'row',flexWrap:'wrap',paddingTop:Rate*10,paddingBottom:Rate*30}}
                    />
                </View>
            )
        }
    }
    
    class CustomCell extends Component {
        constructor(props) {
            super(props);
            this.state = {
                isShow: false,
            }
        }
        render() {
            return (
                <TouchableWithoutFeedback onPress={this.onPress.bind(this)} >
                    <ImageBackground resizeMode='cover' source={this.props.isUAlbum?{uri:this.props.uri.zoomUri}:{uri:this.props.uri.uri}}
                           style={{flexDirection:'row-reverse',marginRight:Rate*5,marginTop:Rate*5,width:(width-Rate*25)/4,height:(width-Rate*25)/4}}>
                        <View style={{justifyContent:'space-between',padding:Rate*5}}>
                            <Image source={require('../../../app/resource/audioRecordImg/selected.png')} style={{height:Rate*40,width:Rate*40,opacity:this.state.isShow?1:0}}/>
                        </View>
                    </ImageBackground>
                </TouchableWithoutFeedback>
            );
        }
        onPress() {
            cropImageWithDic({uri:this.props.isUAlbum?this.props.uri.zoomUri.toString():this.props.uri.uri.toString(),height:'1',width:'1'},(error,event)=>{
                this.props.onPress();
            })
        }
    }
    

    以上基本就是全部的代码逻辑了,个人写的比较的繁琐,有可能哪些地方写的不合适,欢迎评论给我建议。

    相关文章

      网友评论

      本文标题:使用react-native-image-picker选择图片并

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