React Native开发经验集React-Native学习指南我爱编程

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

2018-04-12  本文已影响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();
        })
    }
}

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

上一篇下一篇

猜你喜欢

热点阅读