iOS 媒體庫完整流程封裝:圖片視頻選擇、展示布局、上傳等(一)

寫在前面的話

  • 更新:框架上的東西最近有所改動,更具體的使用和功能可以參考GitHub源代碼
  • 本文章是基于自己封裝的一個媒體框架來寫的,比較粗糙,時間短,待改進。具體內容可以到github中查看:ACMediaFrame
  • 下面沒有涉及框架的使用,使用比較簡單方便,可以看github就都清楚了,主要是羅列一些具體功能點,和重點的解析(其實都算不上重點勒。都是簡單的東西,然后加以改進完善而已,所以看起來應該沒什么難度的)
  • 框架主體:本地選擇圖片視頻、拍攝圖片視頻;布局展示圖片視頻,可刪除;直接存儲了供上傳的數據類型,所以距離上傳就一步之遙(發送請求而已)。
ACMediFrame展示.gif

主要內容

本媒體庫系列包括:其一,本地圖片、視頻、拍照、錄像等的選擇;其二,媒體文件布局展示操作等;其三:媒體文件上傳操作簡易化。

一、媒體文件選擇

大部分項目都有圖片選擇和拍照等功能,更甚至錄像和視頻等,對于多次用到這些功能,借此機會進行基礎封裝。選擇照片用到了第三方TZImagePickerController進行多圖選擇。

1、TZImagePickerController的簡單使用

這個框架主要用來選擇多張圖片,下面是簡單使用的代碼,也是媒體系列框架所用到的,更詳細可以點擊鏈接去查看。對于其代理方法中獲取的圖片或視頻的處理,在后面會詳細介紹。

github通道:TZImagePickerController

//初始化 并設置代理 和 最大選擇張數
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:9 delegate:self];
///是否 在相冊中顯示拍照按鈕
imagePickerVc.allowTakePicture = NO;
///是否可以選擇顯示原圖
imagePickerVc.allowPickingOriginalPhoto = NO;
///是否 在相冊中可以選擇視頻
imagePickerVc.allowPickingVideo = YES;
[self presentViewController:imagePickerVc animated:YES completion:nil];

#pragma mark - TZImagePickerController Delegate

//處理從相冊單選或多選的照片
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto{
  
}

///選取視頻后的回調
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset {
  
}
2、系統方法UIImagePickerController的使用
  • 基礎枚舉類型解析
//picker資源類型
typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
    UIImagePickerControllerSourceTypePhotoLibrary, // 媒體庫(所有媒體文件夾:視頻和圖片)
    UIImagePickerControllerSourceTypeCamera, //相機
    UIImagePickerControllerSourceTypeSavedPhotosAlbum //相冊
}
//相機捕獲類型
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraCaptureMode) {
    UIImagePickerControllerCameraCaptureModePhoto, //拍照 (默認)
    UIImagePickerControllerCameraCaptureModeVideo //錄像
}
//進入相冊轉場動畫
typedef NS_ENUM(NSInteger, UIModalTransitionStyle) {
    UIModalTransitionStyleCoverVertical, //默認,豎直方向推出
    UIModalTransitionStyleFlipHorizontal,//水平方向翻轉
    UIModalTransitionStyleCrossDissolve,//溶解,沒有太多動畫
    UIModalTransitionStylePartialCurl //上下翻頁
};
  • 打開相機:拍照或錄像
/** 打開相機 */
- (void)openCamera {
    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;

    if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]){
        UIImagePickerController *picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        //設置拍照后的圖片可被編輯
        picker.allowsEditing = YES;
        picker.sourceType = sourceType;
      /* 如果是錄像
      picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
        picker.videoQuality = UIImagePickerControllerQualityTypeMedium; //錄像質量
        picker.videoMaximumDuration = 600.0f; //錄像最長時間
        */
        [self presentViewController:picker animated:YES completion:nil];
    }else{
      //不支持拍照或模擬器系列
    }
}
  • 相冊或所有媒體文件夾
- (void)openPhotos {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    //轉場動畫
    picker.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
  /* 選擇所有媒體文件夾
  picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary
  */
    picker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:picker.sourceType];
    picker.allowsEditing = YES;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 代理方法

    • 代理方法回調的 info 字典key解析
    //媒體類型 分為: @"public.movie" 和 @"public.image"
    UIImagePickerControllerMediaType    
      
    //原圖 (視頻和錄像都沒有)  
    UIImagePickerControllerOriginalImage  
      
    //編輯后的圖片(前提是設置了 picker.allowsEditing = YES ,不然沒有這個),視頻和錄像沒有這個
    UIImagePickerControllerEditedImage 
      
    //視頻錄像的URL
    UIImagePickerControllerMediaURL    
      
    //原件的URL,從本地選取的才有,也就是說拍照和錄像都不存在這個  
    UIImagePickerControllerReferenceURL  
    
#pragma mark - UIImagePickerController Delegate
  
///拍照、選視頻圖片、錄像 后的回調(這種方式選擇視頻時,會自動壓縮,但是很耗時間)
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
  //todo: 后面有具體處理方法
}

二、布局、展示

布局的話,也不是太復雜,看圖也知道我的布局,具體內容請看github中源碼,反正都是直接封裝好的。至于布局,用到了第三方MWPhotoBrowser,可以大圖顯示圖片和視頻。

1、MWPhotoBrowser的簡單使用

github通道:MWPhotoBrowser

NSMutableArray *photos = [NSMutableArray array];
//初始化 并 設置代理
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
//是否顯示活動按鈕(分享等操作,默認為 YES)
browser.displayActionButton = NO;
// 是否一直顯示導航欄控制器(默認為NO,點擊圖片消失,再點擊又出現,如果有圖片名,那么和導航欄一樣)
browser.alwaysShowControls = NO;
//是否在每張圖片右上角顯示一個圖片選中的按鈕(默認NO)
browser.displaySelectionButtons = NO;
//是否顯示底部工具條的左右箭頭,多張圖才有效果,箭頭也就是用來翻頁的(默認NO)
browser.displayNavArrows = NO;
//圖片填滿屏幕,依據最小邊來定(默認YES)
browser.zoomPhotosToFill = YES;
browser.startOnGrid = NO;
browser.enableGrid = YES;

//添加圖片數組:數據必須都是 MWPhoto 類型的
//添加本地圖片
[photos addObject:[MWPhoto photoWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"photo2l" ofType:@"jpg"]]]];
//添加url圖片
[photos addObject:[MWPhoto photoWithURL:[NSURL URLWithString:@"http://farm4.static.flickr.com/3629/3339128908_7aecabc34b.jpg"]]];
//添加視頻
//視頻封面圖
MWPhoto *video = [MWPhoto photoWithURL:[NSURL URLWithString:@"https://scontent.cdninstagram.com/hphotos-xpt1/t51.2885-15/e15/11192696_824079697688618_1761661_n.jpg"]];
//視頻內容url
video.videoURL = [[NSURL alloc] initWithString:@"https://scontent.cdninstagram.com/hphotos-xpa1/t50.2886-16/11200303_1440130956287424_1714699187_n.mp4"];
[photos addObject:video];

//設置大圖時 首先顯示的圖片(也就是photos數組的第幾張,一定要在設置了photos數組之后設置,不然是沒有作用的)
[browser setCurrentPhotoIndex:1];
//必須是 push 
[self.navigationController pushViewController:browser animated:YES];
2、代理方法
#pragma mark - <MWPhotoBrowserDelegate>
//圖片總數
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser {
    return self.photos.count;
}
//下標對應的圖片數據源
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
    if (index < self.photos.count) {
        return [self.photos objectAtIndex:index];
    }
    return nil;
}

三、上傳等圖片信息處理

通過前面步驟得到了數據,并知道怎么顯示了,那么接下來就是數據處理和上傳的準備。為此單獨準備了一個model來存儲媒體信息。通過這個模型我們就可以分別得到圖片顯示的image,和視頻顯示的url,最主要也存儲好了上傳的類型,圖片上傳NSData,視頻較大一般都是上傳本地路徑,不會直接上傳NSData。

#import <Photos/Photos.h>

@interface ACMediaModel : NSObject

/** 媒體名字 */
@property (nonatomic, copy) NSString *name;

/** 媒體上傳格式 圖片是NSData,視頻主要是路徑名,也有NSData */
@property (nonatomic, strong) id uploadType;

//兼容 MWPhotoBrowser 的附加屬性

/** 媒體照片 */
@property (nonatomic, strong) UIImage *image;

/** iOS8 之后的媒體屬性 */
@property (nonatomic, strong) PHAsset *asset;

/** 是否屬于可播放的視頻類型 */
@property (nonatomic, assign) BOOL isVideo;

/** 視頻的URL */
@property (nonatomic, strong) NSURL *mediaURL;

@end

四、根據得到的媒體數據轉化存儲為自定義的ACMediaModel模型

這個算的上是一個比較關鍵的數據處理步驟了,分為2種,分別是UIImagePickerControllerTZImagePickerController得到的數據處理,涉及到PHAsset的解析和URL、Image的處理

? PHAsset是iOS8之后<Photos/Photos.h>庫下的API,對于PHAsset具體解析可以參考:ALAsset/PHAsset 中的圖片和視頻文件,比較詳細。

? 在封裝的方法中,確并沒有完全用上上面的方法,主要是PHAsset解析視頻時,異步加載回調需要等待一段時間,這個就不太符合要求了,所以換了如下方法來處理

/**
 根據 PHAsset 來獲取 媒體文件(視頻或圖片)相關信息:文件名、文件上傳類型(data 或 path)

 @param asset  PHAsset對象
 @param completion 成功回調
 */
+ (void)getMediaInfoFromAsset: (PHAsset *)asset completion: (void(^)(NSString *name, id pathData))completion {
    if (!asset) {
        return;
    }
    NSString *mediaName;
    __block NSData *data;
    
    if (asset.mediaType == PHAssetMediaTypeImage) {
        //圖片文件名
        mediaName = [self getMediaNameWithPHAsset:asset extensionName:@"Image.png"];
        PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
        options.version = PHImageRequestOptionsVersionCurrent;
        //返回圖片的質量類型 (效率高,質量低)
        options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
        //同步請求獲取iCloud圖片(默認為NO)
        //options.synchronous = YES;
        
        [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
            data = [NSData dataWithData:imageData];
            
            if (completion && data.length > 0) {
                completion(mediaName, data);
            }
        }];
    }
    else if (asset.mediaType == PHAssetMediaTypeVideo ||
             asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {
        //視頻文件名
        mediaName = [self getMediaNameWithPHAsset:asset extensionName:@"IMG.MOV"];
        
        NSString *videoPath = [NSTemporaryDirectory() stringByAppendingString:mediaName];
        BOOL success = [[NSFileManager defaultManager] fileExistsAtPath:videoPath];
        //當前處理方式:本地的視頻 直接返回路徑,非本地的也不存儲到本地,直接返回data
        if (success) {
            !completion ?  : completion(mediaName, videoPath);
        }else{
            NSData *videoData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:videoPath]];
            !completion ?  : completion(mediaName, videoData);
        }
    }
}

? 上面的圖片解析是根據PHAsset方法來的,但是剛開始出現同時解析多張,回調缺少了數據的問題,后來沒怎么修改,但是這個問題又不存在了,所以到時候看看需要需要也把這個方法替換掉。。。

? 經過之前一系列數據的處理,基本都屬于ACMediaModel屬性范圍,對此就基本完成了顯示的媒體數據源的添加,根據這個ACMediaModel,我們可以展示圖片視頻、刪除并且上傳,對于上傳我們所需要添加到request的header中去的,就是ACMediaModel中的uploadType屬性。

五、結語

  • 一開始講到了媒體文件選擇,這個都很基礎,列出來做個參考
  • 其次是涉及到TZImagePickerControllerMWPhotoBrowser兩個第三方庫的基本用法,以及代理方法,但是代理方法中怎么處理,也一直沒說,簡單點就是通過第四步的方法處理PHAsset或者info,然后轉化為Model存儲、展示等。具體可以看代碼中。
  • 寫這個的初衷是連續多次用到這個知識,每次都去寫布局、方法處理,覺得很麻煩,所以就想著寫到一起來,雖然PHAsset在這里沒用到詳細,但是一開始研究的時候是翻閱了很多相關知識,而且研究下去也是有很多新的知識,只是最后發現有了不足的地方才改換簡單的方法。
  • 在本文章中并沒有說太多框架使用的方法,只是對其中一些方法的羅列和簡單分析。具體框架使用可以到github中查看。
  • github地址:ACMediaFrame 還是那句話:寫的不好,歡迎探討、指正。I am a rookie ,I am not God (有建議或想法請q:331864805 ,你的點贊是我最大的動力)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容