使用Photos自定義相簿

造輪子的皮皮蟹發表於2017-12-21

#前言 最近做的專案中遇到了自定義相簿這個需求,於是就搜尋到了Photos這個框架,說來慚愧。。這個是iOS8的東西,現在才用到。。由於網上的各種講解也很多,我就只講講裡面的遇到的坑和用法,當做筆記記錄一下吧

先來看一下效果圖

Untitled.gif

大概就是這樣的,點選按鈕獲取系統相簿資源,然後放到自定義的介面中,我們的專案底部需要加入預覽的scroll,然後就是大圖檢視,圖片回撥。。具體的程式碼裡面有詳細註釋。。

#好了,簡單粗暴,直接上關鍵程式碼 ##獲取相簿許可權

//相簿許可權判斷
        PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
        if (status == PHAuthorizationStatusDenied)
        {
            //相簿許可權未開啟
            NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
            // app名稱
            NSString *app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"];
            
            [weakSelf SetAlertWithTitle:@"提醒" andMessage:[NSString stringWithFormat:@"請在iPhone的“設定->隱私->照片”開啟%@訪問你的手機相簿",app_Name]];
            
        }
        else if(status == PHAuthorizationStatusNotDetermined)
        {
            //相簿進行授權
            /* * * 第一次安裝應用時直接進行這個判斷進行授權 * * */
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status)
            {
                //授權後直接開啟照片庫
                if (status == PHAuthorizationStatusAuthorized)
                {
                    dispatch_async(dispatch_get_main_queue(), ^
                    {
                        [weakSelf pushViewController];
                    });
                    
                }
            }];
        }
        else if (status == PHAuthorizationStatusAuthorized)
        {
            [weakSelf pushViewController];
        }
複製程式碼

##然後就是獲取系統相簿資源了

-(void) getSystemPhotos
{
    // 獲取所有資源的集合,並按資源的建立時間排序
    PHFetchOptions *options = [[PHFetchOptions alloc] init];
    options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    
    dispatch_async(dispatch_get_global_queue(0,0), ^{
        // 獲得相機膠捲的圖片
        PHFetchResult<PHAssetCollection *> *collectionResult1 = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
        for (PHAssetCollection *collection in collectionResult1) {
            if (![collection.localizedTitle isEqualToString:@"Camera Roll"]) continue;
            [self searchAllImagesInCollection:collection];
            break;
        }
    });
}

- (void)searchAllImagesInCollection:(PHAssetCollection *)collection
{
    // 採取同步獲取圖片(只獲得一次圖片)
    PHImageRequestOptions *imageOptions = [[PHImageRequestOptions alloc] init];
    imageOptions.resizeMode = PHImageRequestOptionsResizeModeFast;
    imageOptions.synchronous = YES;
    imageOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
    
    PHVideoRequestOptions *videoOptions = [[PHVideoRequestOptions alloc] init];
    videoOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeFastFormat;
    
    __weak typeof (self) weakSelf = self;
    
    // 遍歷這個相簿中的所有資源
    PHFetchResult<PHAsset *> *assetResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
    
    for (PHAsset *asset in assetResult) {
        
        if (weakSelf.isPhotoModel) {
            // 過濾非圖片
            if (asset.mediaType != PHAssetMediaTypeImage) continue;
            
            PhotoModel *photo = [[PhotoModel alloc] init];
            photo.asset = asset;
            [_imgViewArr addObject:photo];
        }
        else
        {
            // 過濾非視訊
            if (asset.mediaType != PHAssetMediaTypeVideo) continue;
            
            PhotoModel *photo = [[PhotoModel alloc] init];
            photo.asset = asset;
            [_imgViewArr addObject:photo];
        }
    }
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf loadMainView];
    });
    
}
複製程式碼

這裡有一個重點,就是獲取到的asset資源一定不要嘗試在這裡使用requestImageForAsset方法去獲得圖片資訊,特別是高清圖,一到真機上幾百張圖片載入記憶體暴增導致閃退,就算使用裁剪圖片或者PHImageRequestOptionsResizeModeFast模式,也會有一小段的空白時間,並不能進來後瞬間顯示圖片,所以此處我選擇直接將asset儲存進陣列中,利用collctionview的重用機制,在自定義的cell裡面再去使用requestImageForAsset載入,效果會好很多,自定義CollectionViewCell中的程式碼如下

-(void)loadPhotoData:(PhotoModel *)photo withTargetSize:(CGSize)target state:(BOOL)isPhotoModel
{
    __weak typeof (self) weakSelf = self;
    
    if (photo)
    {
        [[PHImageManager defaultManager] requestImageForAsset:photo.asset targetSize:CGSizeMake(200, 200) contentMode:PHImageContentModeAspectFit options:nil resultHandler:^(UIImage *result, NSDictionary *info){
            
            //修正圖片方向
            UIImage *targetImg = [weakSelf fixOrientation:result];
            
            //縮小圖片
            targetImg = [weakSelf reSizeImage:targetImg ForSize:target];
            
            weakSelf.PhotoImg.image = targetImg;
            photo.isPhotoModel = YES;
        }];
        
        if (!isPhotoModel) {
            
            _bottom.hidden = NO;
            
            PHImageManager *manager = [PHImageManager defaultManager];
            [manager requestAVAssetForVideo:photo.asset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info)
             {
                 //CMTime轉換
                 NSTimeInterval total = CMTimeGetSeconds(asset.duration);
                 
                 NSString *duration = [weakSelf stringWithTime:total];
                 
                 //回到主執行緒
                 dispatch_async(dispatch_get_main_queue(), ^{
                     weakSelf.VideoDuration.text = duration;
                     
                     photo.asset_video = asset;
                     photo.isPhotoModel = NO;
                 });
             }];
        }
    }
}
複製程式碼

這段程式碼中還加入了是否為圖片模式的判斷,如果是選擇的視訊,則這個cell中就將視訊asset儲存到對應的key中,之後再通過AVPlayer去播放這個資源

#結尾 首先感謝網上各位大神的資料講解,我也看過好幾份例子。。GitHub上那幾個其實都有一點小問題(圖片回撥不全什麼的,希望大神看到不要見怪)。。不能滿足我們專案的需求,所以就自己擼了一個。。

目前我在使用Photos時遇到最大的一個坑就是上文中提到的記憶體暴增導致的閃退問題,一直嘗試著在外部獲取高清圖片再儲存進陣列,後來無意間發現利用collctionview的重用就解決了這個問題,GIF中的圖片回撥什麼的我就不一一列舉了,程式碼裡面我幾乎都註釋了,需要的可以去看看,覺得有用的話記得給個star吧~

PS:對了,我攝像頭拍照錄視訊的功能還沒加進來,如果有需要,時間充裕的話,我會盡量補上~

地址:GitHub

#更新 現已加入攝像頭拍照錄影功能、加入iOS9之後的collection長按拖動圖片功能、優化圖片選擇器中的卡頓現象,去除在collectioncell中繪製圖片程式碼,加快載入速度,以下程式碼已去除

//修正圖片方向
UIImage *targetImg = [weakSelf fixOrientation:result];
            
//縮小圖片
targetImg = [weakSelf reSizeImage:targetImg ForSize:target];
複製程式碼

真機測試效果更佳,大家用真機測試吧,程式碼都在GitHub中,註釋很詳細,大家可以去看,覺得有用記得點個star~

相關文章