寫在前面的話
- 更新:框架上的東西最近有所改動,更具體的使用和功能可以參考GitHub源代碼
- 本文章是基于自己封裝的一個媒體框架來寫的,比較粗糙,時間短,待改進。具體內容可以到github中查看:ACMediaFrame
- 下面沒有涉及框架的使用,使用比較簡單方便,可以看github就都清楚了,主要是羅列一些具體功能點,和重點的解析(其實都算不上重點勒。都是簡單的東西,然后加以改進完善而已,所以看起來應該沒什么難度的)
- 框架主體:本地選擇圖片視頻、拍攝圖片視頻;布局展示圖片視頻,可刪除;直接存儲了供上傳的數據類型,所以距離上傳就一步之遙(發送請求而已)。
主要內容
本媒體庫系列包括:其一,本地圖片、視頻、拍照、錄像等的選擇;其二,媒體文件布局展示操作等;其三:媒體文件上傳操作簡易化。
一、媒體文件選擇
大部分項目都有圖片選擇和拍照等功能,更甚至錄像和視頻等,對于多次用到這些功能,借此機會進行基礎封裝。選擇照片用到了第三方
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種,分別是
UIImagePickerController
和TZImagePickerController
得到的數據處理,涉及到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
屬性。
五、結語
- 一開始講到了媒體文件選擇,這個都很基礎,列出來做個參考
- 其次是涉及到
TZImagePickerController
和MWPhotoBrowser
兩個第三方庫的基本用法,以及代理方法,但是代理方法中怎么處理,也一直沒說,簡單點就是通過第四步的方法處理PHAsset或者info,然后轉化為Model存儲、展示等。具體可以看代碼中。 - 寫這個的初衷是連續多次用到這個知識,每次都去寫布局、方法處理,覺得很麻煩,所以就想著寫到一起來,雖然PHAsset在這里沒用到詳細,但是一開始研究的時候是翻閱了很多相關知識,而且研究下去也是有很多新的知識,只是最后發現有了不足的地方才改換簡單的方法。
- 在本文章中并沒有說太多框架使用的方法,只是對其中一些方法的羅列和簡單分析。具體框架使用可以到github中查看。
- github地址:ACMediaFrame 還是那句話:寫的不好,歡迎探討、指正。I am a rookie ,I am not God (有建議或想法請q:331864805 ,你的點贊是我最大的動力)