你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS開發中自定義相冊功能性能改善

iOS開發中自定義相冊功能性能改善

編輯:IOS開發基礎

相冊浏覽器/選擇器/照相機Demo:LGPhotoBrowser

大多數項目中都會用到相冊浏覽和選擇功能,如果需要使用到自定義相冊浏覽器,那麼,性能優化將是一個很重要的課題。畢竟操作對象是圖片這樣相對較大寫數據單位。今天就針自定義相冊浏覽選擇器四個優化點進行剖析:

  • 縮略圖頁面加載速度優化

  • 縮略圖頁面滑動流暢度優化

  • 大圖浏覽滑動流暢度優化

  • 內存優化

先看看自定義相冊的兩個主要界面:

20160121114434176.jpg20160121114455145.jpg

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

以上就是本文的所有內容,難免會出纰漏,望各位匡正!

  • 優化後的自定義相冊選擇浏覽器Demo:LGPhotoBrowser

  • 關於相冊方面的基礎知識請參考: iOS的AssetsLibrary框架解讀與應用

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved