iOS开发中自定义相册功能性能改善

大多数项目中都会用到相册浏览和选择功能,如果需要使用到自定义相册浏览器,那么,性能优化将是一个很重要的课题。毕竟操作对象是图片这样相对较大写数据单位。今天就针自定义相册浏览选择器四个优化点进行剖析:

 

  • 缩略图页面加载速度优化

  • 缩略图页面滑动流畅度优化

  • 大图浏览滑动流畅度优化

  • 内存优化

 

先看看自定义相册的两个主要界面:

1.缩略图页面加载速度优化

 

如果本地相册有200张以上的照片,那么缩略图页面的加载速度就显得尤为重要。


首先,要保证缩略图界面的控制器在没有加载照片的时候,从viewDidLoad到viewDidAppear的时间不能太长。之前用的MWPhotoBrowser,从viewDidLoad开始到viewDidAppear完成的耗时是700ms,这个时间就太长。

 

其次,是从本地读取asset的过程不能太长。获取一个group的所有asset方法如下:

 

- (void) getGroupPhotosWithGroup : (ZLPhotoPickerGroup *) pickerGroup finished : (groupCallBackBlock ) callBack{  
     NSMutableArray *assets = [NSMutableArray array];  
     ALAssetsGroupEnumerationResultsBlock result = ^(ALAsset *asset , NSUInteger index , BOOLBOOL *stop){  
         if  (asset) {  
             [assets addObject:asset];  
         } else {  
             callBack(assets);  
         }  
     };  
     [pickerGroup.group enumerateAssetsUsingBlock:result];  
}

 

每取一张照片,就要执行一次代码块result。如果本地相册中有1000张照片,那么这个代码块就要执行1000次,所有这个代码块绝对不能臃肿,尽量简洁!!!

 

2. 缩略图页面滑动流畅度优化

 

1)cell重用

 

缩略图页面的使用collectionView展示,每一个cell上实际有两个子视图,一个是里面有对勾的小圆圈,另一个是透明的按钮。所以在cell重用的时候就要注意了,这两个子视图也要加入到重用的行列中,千万不要每加载一个cell就删除所有子视图然后再重新安装。试验证明,重用机制发挥好了,可以很大程度改善流畅度。

 

代码如下:

 

/** 
  *  每个cell右上角的选择按钮 
  */  
- (void)setupTickButtonOnCell:(ZLPhotoPickerCollectionViewCell *)cell  
                       AtIndex:(NSIndexPath *)indexPath  
{  
     UIButton *tickButton = nil;  
     if  (cell.contentView.subviews.count == 2 && [cell.contentView.subviews[1] isKindOfClass:[UIButton class]]) { //如果是重用cell,则不用再添加button  
         tickButton = cell.contentView.subviews[1];  
     else  {  
         tickButton = [[UIButton alloc] init];  
         tickButton.frame = CGRectMake(cell.frame.size.width - 40, 0, 40, 40);  
         [tickButton setBackgroundColor:[UIColor clearColor]];  
         [cell.contentView addSubview:tickButton];  
         [tickButton addTarget:self action:@selector(tickBtnTouched:) forControlEvents:UIControlEventTouchUpInside];  
     }  
     //runtime 关联对象  
     objc_setAssociatedObject(tickButton, @ "tickBtn" , indexPath, OBJC_ASSOCIATION_ASSIGN);  
}

 

2)获取缩略图

 

在ALAsset类中有两种获取缩略图的方法:thumbnail和aspectRatioThumbnail,第一个是普通缩略图,第二个是按比例的缩略图,实验证明前者比后者快。实现方法如下:

 

 

- (UIImage*)thumbImage{      
     return  [UIImage imageWithCGImage:[self.assetaspectRatioThumbnail]];  
}

 

 

- (UIImage*)thumbImage{     
     return  [UIImage imageWithCGImage:[self.assetthumbnail]];  
}

 

thumbnail方法要比aspectRatioThumbnail快很多。

 

但是,在ios9上,用thumbnail方法取得的缩略图显示出来不清晰。

 

所以,在代码中可以加判断,在iOS9上使用前者,其他使用后者。这样可以在一定程度上提高流畅度。

 

3.大图浏览滑动流畅度优化

 

大图浏览界面使用collectionView展示,一张图占据一个cell。一般情况下,collectionView需要加载cell时,就用数据源方法中取一个cell。在数据源方法中需要给cell获取ALAsset对象的fullResolutionImage,获取方法如下:

 

- (UIImage*)originImage{  
     UIImage *image = [UIImage imageWithCGImage:[[self.assetdefaultRepresentation]fullResolutionImage]];      
     return  image;  
}

 

那么问题来了,执行这个方法需要花费大概80ms,这就会产生卡顿感。

 

那怎么办?用异步加载?如果用异步的话,在originImage方法执行完之前,需要加载的cell已经暴露在视野中,是背景色,等fullResolutionImage执行完了,图片会突然蹦出。这样的体验效果当然不行啦。

 

我的解决思路是:提前获取image。概括的来说呢,就是在恰当的时机,提前在后台取到将要显示的fullResolutionImage。

 

  • 在viewDidLoad中获取当前图片的前一张image和后一个image,这时程序中就有三张准备好的image,如果向左边滑动,直接拿取右边的图片给cell。

  • 这一步是重点。滑动过程中,异步加载下一张image,这个下一张是需要判断滑动方向的。从本次滑动动作结束到下次滑动开始,中间这个时间段异步加载一张图片,时间是充裕的。那么,在什么时候开始异步加载呢?你可以在scrollView的这几个代理方法中实现:

 

 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;  
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;    // called on finger up as we are moving  
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;       // called when   
// called on finger up if the user dragged. velocity is in points/millisecond. targetContentOffset may be changed to adjust where the scroll view comes to rest  
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset NS_AVAILABLE_IOS(5_0);

 

我选择最后一个,它有targetContentOffset,根据这个可以判断手离开屏幕后,collectionView是否滑到下一张。因为用户有时候会在滑动的时候犹豫一下,结果没滑到下一张,这个时候在代码里就不要做多余的操作了。功能代码如下:

 

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {  
     //如果滑动松开手后回滚动到下一个页面  
     if  (targetContentOffset->x != _beginDraggingContentOffsetX) {  
         DraggingDirect direct = [self getDraggingDirect];  
         //获得currentPage  
         if  (direct == LEFT) {  
             self.currentPage = (NSInteger)(scrollView.contentOffset.x / (scrollView.frame.size.width) + 0.9);  
             if  (self.currentPage > self.photos.count - 1) {  
                 self.currentPage --;  
             }  
         else  if  (direct == RIGHT) {  
             self.currentPage = (NSInteger)(scrollView.contentOffset.x / (scrollView.frame.size.width));  
         }  
           
         //获得image  
         dispatch_queue_t queue = dispatch_queue_create( "BeginDecelerating" , DISPATCH_QUEUE_SERIAL);  
         dispatch_async(queue, ^{  
             //获取下一张image  
             [self loadNextImageWithDirect:direct];  
         });  
     }  
}

 

- (DraggingDirect)getDraggingDirect  
{  
     DraggingDirect direct;  
     if  (self.beginDraggingContentOffsetX == self.collectionView.contentOffset.x  
         ) {  
         direct = MIDDLE;  
     else  if  (self.beginDraggingContentOffsetX > self.collectionView.contentOffset.x) {  
         direct = RIGHT;  
     else  {  
         direct = LEFT;  
     }  
     return  direct;  
}

 

/** 
  *  根据上一次的滑动方向,加载下一张图 
  */  
- (void)loadNextImageWithDirect:(DraggingDirect)direct {  
     if  (direct == LEFT && self.currentPage < self.photos.count - 1){  
         if  (!((LGPhotoPickerBrowserPhoto *)self.photos[self.currentPage + 1]).photoImage) {  
             LGPhotoAssets *asset = ((LGPhotoPickerBrowserPhoto *)self.photos[self.currentPage + 1]).asset;  
             ((LGPhotoPickerBrowserPhoto *)self.photos[self.currentPage + 1]).photoImage = [asset originImage];  
         }  
     else  if (direct == RIGHT && self.currentPage > 0){  
         if  (!((LGPhotoPickerBrowserPhoto *)self.photos[self.currentPage - 1]).photoImage) {  
             LGPhotoAssets *asset = ((LGPhotoPickerBrowserPhoto *)self.photos[self.currentPage - 1]).asset;  
             ((LGPhotoPickerBrowserPhoto *)self.photos[self.currentPage - 1]).photoImage = [asset originImage];  
         }  
     else  ;  
}

 

4. 内存优化

 

如果选中9张原图,点击发送后内存瞬间暴增到100M以上。原因是点击发送之后,程序会连续循环执行9次

 

UIImage*image = [UIImage imageWithCGImage:[[self.assetdefaultRepresentation]fullResolutionImage]];

 

这句代码是解压缩原分辨率照片的。

 

这句代码是很耗内存的,但是没办法,这是系统的API。

 

解决办法1:让这九次解压过程分开执行,一张图片发送完成之后再解压下一张图片。这样可以改善内存问题。

 

解决办法2:利用NSData,如果项目中只需要得到原图的原始数据,那么就没必要再把CGImage解压成UIImage,避开解压代码。附上获取NSData代码:

 

- (NSData *)imageData:(ALAsset*)asset{  
       
     ALAssetRepresentation *assetRep = [asset defaultRepresentation];  
     NSUInteger size = [assetRep size];  
     uint8_t *buff = malloc(size);  
       
     NSError *err = nil;  
     NSUInteger gotByteCount = [assetRep getBytes:buff fromOffset:0 length:size error:&err];  
       
     if  (gotByteCount) {  
         if  (err) {  
             free(buff);  
             return  nil;  
         }  
     }  
       
     return  [NSData dataWithBytesNoCopy:buff length:size freeWhenDone:YES];  
}

 

转载于:https://www.cnblogs.com/daiyazhou/p/5164977.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值