背景
注:这个方法有坑。。【戳--填坑版】
最近一个半路接手的项目需要修改一个功能。图片上传。项目中有很多地方用到了图片上传这个功能,有的是单张的,有的是批量上传的。现在需要全部改为批量上传。还有新增图片上传的状态:未上传、上传中、上传失败、上传成功。上传失败需要有一个点击重传的功能。
没有将代码抽离出来,就不放代码了。涉及具体业务的部分为了保密性也不放了。。主要就是介绍一下思想,捋一下思路
动手1
因为对别人写的代码不太熟悉,粗略的看过一遍后,决定先改好改的部分。然后在熟悉的过程中看看别人的思路以及牵扯到的需要修改的地方。从结果往前推。首先给图片部分加上显示的状态。找到图片显示控件,新增控件需要的状态。
//上传中的半透明遮罩
@property (nonatomic, strong) UIImageView *maskView;
//点击重传的按钮
@property (nonatomic, strong) UILabel *reUpload;
//上传中的转动图片
@property (nonatomic, strong) UIImageView *uploading;
加号之后我们需要一个变量对这些控件进行显示与隐藏。所以我们要知道这些状态。再来看看现有的这个类中提供了哪些外部更新的方法。
- (void)setImage:(UIImage *)image;
- (void)setImageUrl:(NSString *)imgUrl;
- (void)clearImage;
其中 imgUrl是上传成功后接口返回的图片地址,当上传成功后接口会按上传顺序返回图片的一串URL,失败的那张图片会返回空信息。拿到结果后我们会调用这个方法去刷新。这个时候我们可以拿到 【成功】和【失败】 的状态。拿另外两个【未上传】以及【上传中】就可以通过
- (void)setImage:(UIImage *)image
这个方法来判断。image==nil 未上传
动手2
接下来是图片来源的问题。
上传的图片通过
- 拍照
- 相册多张选择
- 相册单张选择(【上传失败】需要重传照片)
主要是修改相册作为图片源的部分。让点击添加的时候为多张,重传的时候为一张。批量上传使用了别人开源的控件ZYQAssetPickerController,使用懒加载的方式初始化它。
if (_addImgIndex == _imageArray.count) {
//这个部分是新加的,_addImgIndex指的是点击添加图片的index,_imageArray存储照片的数组,用于未上传时候的展示
[self presentViewController:self.pickerController animated:YES completion:nil];
} else {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.allowsEditing = NO;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:imagePicker animated:YES completion:nil];
}
动手3
照片选择完成后的回调,包括图片的显示,图片的
上传。
原先是点一张上传一张,不存在未上传时候的显示问题。所以我们需要添加一些全局变量来存储一些东西。
@property (nonatomic, strong) NSMutableArray *imageArray; //存储图片
@property (nonatomic, assign) NSInteger addImgIndex; //添加照片的index;
@property (nonatomic, assign) NSInteger imageCount; //与上一个结合起来
当前的controller实现ZYQAssetPickerControllerDelegate。开辟一个子线程拿到图片后,调用存储的方法,将图片存入内存。mutaFile用来存储图片文件用来上传。
- (void)assetPickerController:(ZYQAssetPickerController *)picker didFinishPickingAssets:(NSArray *)assets
{
_isCamera = NO;
_imageCount = assets.count + _imageArray.count;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableArray *mutaFile = [NSMutableArray new];
for (int i=0; i
当所有的图片存储完毕后我们切回主线程,刷行UI,显示图片。并将图片至于【上传中】的状态,调用上传文件的方法上传文件。
- (void)saveImage:(UIImage *)image fileArr:(NSMutableArray *)filePathArr
{
//省略了图片的压缩,存储,以及文件的初始化
if (!_imageArray) {
_imageArray = [NSMutableArray array];
}
if (_addImgIndex == _imageArray.count) {
[_imageArray addObject:image];
[self.viewModel.showImgUrlArr addObject:@"**"];
[self.viewModel.imgUrlArr addObject:@"**"];
} else {
[_imageArray replaceObjectAtIndex:_addImgIndex withObject:image];
[self.viewModel.showImgUrlArr replaceObjectAtIndex:_addImgIndex withObject:@"**"];
[self.viewModel.imgUrlArr replaceObjectAtIndex:_addImgIndex withObject:@"**"];
}
[filePathArr addObject:file];
if (_isCamera) {
_isCamera = NO;
dispatch_async(dispatch_get_main_queue(),^{
[self.tableView reloadData];
});
[self uploadImage:filePathArr];
}
else
{
if (_imageCount == _imageArray.count) {
dispatch_async(dispatch_get_main_queue(),^{
[self.tableView reloadData];
});
[self uploadImage:filePathArr];
}
}
}
其中showImgUrlArr和imgUrlArr都是接口返回的,用**代表上传中,##代表上传失败。在拿到上传结果后给他replaceObjectAtIndex。这样在用户点击下一步的时候,能弹框告诉用户图片的上传状态,拦截当前页面的保存操作。
思考时间做多的地方
当图片分次批量上传的时候,怎么处理这个URL对应的替换操作。接口并没有返回当前这个URL指向的图片的下标。后来我修改了上传的文件数组,原来这个图片文件数组是全局变量,保存了所有要显示的图片,包括已经上传成功后的图片。将它改为当前需要上传图片,作用域只在相册及相机回调 方法内,然后将文件命名为 0,1,2...
刷新方法里面根据showImgUrl来判断展示什么状态。
AddImgView *addView = [[AddImgView alloc] init];
if ([mulArr[i] isEqualToString:@"**"] || [mulArr[i] isEqualToString:@"##"]) {
//上传中图片 + 上传失败
[addView setImage:imageArr[i] reload:[mulArr[i] isEqualToString:@"##"]];
} else if ([mulArr[i] isEqualToString:@""]) {
} else {
[addView setImageUrl:imgUrlArr[i]];
}
总结
改别人的代码总觉得很难受,中间发生过很多意外的情况,没有自己写的顺手。因为不清楚之前的具体需求,会忽略掉某些边界以及特殊情况。只能自己一次次的debug。不过看别人的代码学习的一种吧。总不可能每个项目都是从0开始的。。但是涉及到很多文件的修改真的想吐血啊。