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];  

}

 

时间: 2024-12-24 23:01:44

iOS开发中自定义相册功能性能改善的相关文章

iOS开发中自定义字体的方法

http://www.cnblogs.com/iyou/archive/2014/05/25/3751669.html 1. 首先下载你想要设置的字体库,例如设置方正启体简体 2. 添加到工程,一定要注意勾选红色框框处,默认是不勾选的  添加以后 3.在plist文件中添加 4.现在已经添加成功了,但是要使用就必须知道FontName,用以下代码可查到 NSArray *familyNames = [[NSArray alloc] initWithArray:[UIFont familyName

【Swift】IOS开发中自定义转场动画

在IOS开发中,我们model另外一个控制器的时候,一般都使用的自定义的转场动画. 其实我们可以自定义一些转场动画.达到不同的转场效果. 步骤如下:(photoBrowser是目标控制器) 1.在源控制器中,设置目标控制器的转场代理为 self 1 //设置Model转场代理 2  photoBrowser.transitioningDelegate = self 2.同时设置目标控制器的model类型 1 //设置Model类型 2 photoBrowser.modalPresentation

深入理解iOS开发中的BitCode功能

前言 做iOS开发的朋友们都知道,目前最新的Xcode7,新建项目默认就打开了bitcode设置.而且大部分开发者都被这个突如其来的bitcode功能给坑过导致项目编译失败,而这些因为bitcode而编译失败的的项目都有一个共同点,就是链接了第三方二进制的库或者框架,而这些框架或者库恰好没有包含bitcode的东西(暂且称为东西),从而导致项目编译不成功.所以每当遇到这个情况时候大部分人都是直接设置Xcode关闭bitcode功能,全部不生成bitcode.也不去深究这一开关背后隐藏的原理.中枪

iOS开发中WiFi相关功能总结

1.Ping域名.Ping某IP 有时候可能会遇到ping 某个域名或者ip通不通,再做下一步操作.这里的ping与传统的做get或者post请求还是有很大区别的.比如我们连接了某个WiFi,测试ping www.baidu.com,如果能ping 通,基本可以断定可以上网了,但是如果我们做了一个get 请求(url 是www.baidu.com),路由器可能重定向这个WiFi内的某网页了,依然没有错误返回,就会误认为可以正常上网. 这里有关于ping命令的详细解释:百度百科Ping iOS中想

iOS开发中访问相册摄像像头

源码下载地址http://download.csdn.net/download/jingjingxujiayou/7270479 在AppDelegate.m文件中 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen

iOS开发中拨打电话功能的实现

//第一种 方法 // 弊端:该方法进行拨号之后,当电话挂断之后不会返回应用程式,会停留在通话记录界面 NSURL *url = [NSURL URLWithString:@”tel//185------”]; [UIApplication shareApplication] openURL:url]; // 第二种 方法(Apple私有API) 审核会有问题 //大拨打电话之后会提升用户是否拨打,当电话挂断之后会返回应用程式 NSURL *url = [NSURL URLWithString:

IOS开发中UITableView(表视图)的性能优化及自定义Cell

IOS开发中UITableView(表视图)的滚动优化及自定义Cell IOS 开发中UITableView是非常常用的一个控件,我们平时在手机上看到的联系人列表,微信好友列表等都是通过UITableView实现的.UITableView这个控件中的列表的每一行是一个cell,当UITableView中cell数量特别大的时候,由于每次都需要alloc分配内存并初始化,会导致app运行不流畅,所以可以使用苹果提供的几个方法进行优化,我把这个过程记录下来供自己以后查阅. 当然,既然说到优化,那我们

iOS开发中文件的上传和下载功能的基本实现-备用

感谢大神分享 这篇文章主要介绍了iOS开发中文件的上传和下载功能的基本实现,并且下载方面讲到了大文件的多线程断点下载,需要的朋友可以参考下 文件的上传 说明:文件上传使用的时POST请求,通常把要上传的数据保存在请求体中.本文介绍如何不借助第三方框架实现iOS开发中得文件上传. 由于过程较为复杂,因此本文只贴出部分关键代码. 主控制器的关键代码: 复制代码代码如下: YYViewController.m#import "YYViewController.h" #define YYEnc

iOS开发中打电话发短信等功能的实现

在APP开发中,可能会涉及到打电话.发短信.发邮件等功能.比如说,通常一个产品的“关于”页面,会有开发者的联系方式,理想情况下,当用户点击该电话号码时,能够自动的帮用户拨出去,就涉及到了打电话的功能. iOS开发中,有三种方式可以打电话: (1)直接跳到拨号界面,代码如下 1 2 NSURL *url = [NSURL URLWithString:@"tel://10010"];  [[UIApplication sharedApplication] openURL:url]; 缺点: