Skip to main content

相册功能实现教程

1. 概述

在本教程中,您将学习如何使用Mobile SDK访问飞机相机SD卡或机载闪存中的媒体资源。 在本教程结束时,您将拥有一个可用于预览照片,播放视频,下载或删除文件等的应用程序。

下面以Autel_UX_SDK demo为例讲解如何访问相册、下载、删除、播放等功能。

2. 相册流程图

以下为相册UI层、相册(本地相册)业务管理类、Mobile SDK之间的调用流程

相册流程图

以下为Mobile SDK所提供的接口梳理:

相册业务

3. 相册API介绍

相册业务封装在Mobile SDK内,开发者可调用DroneConnection.shared.drone?.camera.playbackManager()获取到SD卡的相册管理类,也可调用DroneConnection.shared.drone?.camera.mmcPlaybackManager()获取到机载闪存的相册管理类;DroneConnection类是飞机对象管理单例类,存在于AutelUXFramework动态库中.

如果不希望通过上面的DroneConnection.shared.drone方式获取相机,也可以参考ATConsoleViewController 或者ATConsoleViewModel,它们遵循了CameraProtocolDroneProtocol协议实时获取相机或者飞机的状态或者参数上报。

注意:若不是继承于BaseViewController的类但继承于NSObject 必须调用以下代码才能注册Protocol,否则遵循的Protocol将不能生效。BaseViewController已经自动注册Protocol,不必再次调用。

  func registerProtocol() {
registerAutelProtocolsIfCan()
}

当类销毁时,需要调用以下以进行注销:

  func  removeProtocol() {
removeAutelProtocolsIfCan()
}

CameraProtocol

public protocol CameraProtocol : AutelUXFramework.CheckProtocol {

func camera(_ camera: AUTELBaseCamera!, didUpdateConnectState connectState: AUTELCameraConnectState)

func camera(_ camera: AUTELBaseCamera!, didUpdateRecordRecoverState recoverState: AUTELCameraRecordRecoverState)

func camera(_ camera: AUTELBaseCamera!, didGenerateNewMediaFile newMedia: AUTELMedia!)

func camera(_ camera: AUTELBaseCamera!, didUpdateSDCardState sdCardState: AUTELCameraSDCardState!)

func camera(_ camera: AUTELBaseCamera!, didUpdateMMCState mmcState: AUTELCameraSDCardState!)

func camera(_ camera: AUTELBaseCamera!, didUpdateSystemState systemState: AUTELCameraSystemBaseState!)

func camera(_ camera: AUTELBaseCamera!, didUpdateParameters parameters: AUTELCameraParameters!, change: [AnyHashable : [Any]]!)

func camera(_ camera: AUTELBaseCamera!, didUpdateCurrentExposureValues exposureParameters: AUTELCameraExposureParameters!)

func camera(_ camera: AUTELBaseCamera!, didUpdateHistogramTotalPixels totalPixels: Int, andPixelsPerLevel pixelsArray: [Any]!)

func camera(_ camera: AUTELBaseCamera!, didUpdateTimeLapseCaptured captureCount: Int, andCountDown timeInterval: TimeInterval)

func camera(_ camera: AUTELBaseCamera!, didUpdateAutoFocusState focusState: AUTELCameraAFLensFocusState, andLensFocusAreaRowIndex rowIndex: Int, andColIndex colIndex: Int)

func camera(_ camera: AUTELBaseCamera!, didUpdateAeAfState state: AUTELCameraAFWorkStatus)
}

只要能获取到AUTELBaseCamera的实例对象,就可以获取到相册管理类playbackManagermmcPlaybackManager

3.1 获取相册列表

// 分页获取文件列表.(默认全部资源类型)
- (void)fetchMediaFileListFromOffset:(NSUInteger)offset
fileCount:(NSUInteger)count
withCompletion:(void (^_Nonnull)(NSArray<AUTELMedia *> *_Nullable mediaList, NSError *_Nullable error))block;
// 分页获取文件列表.(资源类型由调用方传入)
- (void)fetchMediaFileListFromOffset:(NSUInteger)offset
fileCount:(NSUInteger)count
fileType:(AUTELGetFileListType)fileType
withCompletion:(void (^_Nonnull)(NSArray<AUTELMedia *> *_Nullable mediaList, NSError *_Nullable error))block;

成功获取相册列表后,得到列表数组[AUTELMedia]AUTELMedia 为Mobile SDK封装的多媒体文件的基本属性。

/**
* 该类描述SD卡中多媒体文件的基本属性.
*
* This class contains information about a multi-media file on the SD card.
*/
@interface AUTELMedia : NSObject<NSCopying, NSCoding>

/**
* @brief 媒体文件名.
*
* Returns the name of the media file.
*/
@property (nonatomic, readonly) NSString *fileName;

/**
* @brief 媒体文件在SD卡中的存放路径.
*
* Returns the file path of the media file in SD card.
*/
@property (nonatomic, readonly) NSString *filePath;

/**
* @brief 媒体文件创建时间, 格式为: yyyy-MM-dd HH:mm:ss.
*
* Returns the time when the media file was created as a string in
* the format "yyyy-MM-dd HH:mm:ss".
*/
@property (nonatomic, readonly) NSString *timeCreated;

/**
* @brief 媒体文件下标, 新拍摄的文件排放在前.
*
* Returns the index of the media file. The latest taken file will be lined in the front.
*/
@property (nonatomic, readonly) NSUInteger mediaIndex;

/**
* @brief 媒体文件的字节大小.
*
* Returns the size, in bytes, of the media file.
*/
@property (nonatomic, readonly) long long fileSizeInBytes;

/**
* @brief 媒体文件的类型.
*
* Returns the type of media file.
*/
@property (nonatomic, readonly) AUTELMediaType mediaType;

/**
* @brief 媒体文件的唯一标识符.
*
* Returns the unique identifier of media file.
*/
@property (nonatomic, readonly) NSString *uniqueID;

/**
视频时长
*/
@property (nonatomic, assign) NSUInteger videoDuration;

/**
@brief 视频文件的分辨率, 图片文件无分辨率该值为`AUTELCameraVideoResolutionUnknown`, 视频文件初始值也为`AUTELCameraVideoResolutionUnknown`, 当请求过该文件的缩略图后, 该值才有意义。

Returns the resolution of video file.
*/
@property (nonatomic, readonly) AUTELCameraVideoResolution videoResolution;

/**
Returns the video framerate of video file.
*/
@property (nonatomic, readonly) AUTELCameraVideoFrameRate videoFrameRate;

/**
@brief 视频文件的压缩标准

Returns the compreassion standar of video file.
*/
@property (nonatomic, readonly) AUTELCameraVideoFileCompressionStandard videoCompressionStandard;

/**
@brief 全景照片类型

Returns the Types of panoramic photos.
*/
@property (nonatomic, assign) AUTELCameraPanoramaType panoramaType;

/**
@brief 照片任务类型

Returns the Types of mission photos.
*/
@property (nonatomic, assign) AUTELCameraMediaFileType fileType;

AUTELGetFileListType枚举参数说明

typedef NS_ENUM(NSInteger, AUTELGetFileListType) {
/**
* @brief Image file
*
* List of all files
*/
AUTELGetFileListTypeAll = 0,

/**
* @brief Video file list
*
* 视频文件列表
*/
AUTELGetFileListTypeVideo = 1,

/**
* @brief Picture file list
*
* 图片文件列表
*/
AUTELGetFileListTypePhoto = 2,
};

3.2 获取文件的相应缩略图

- (void)fetchThumbnailForMedia:(AUTELMedia *_Nonnull)media
withCompletion:(AUTELLowerResolutionImageCompletion)block; // 获取文件的相应缩略图.

3.3 获取文件的相应预览图

- (void)fetchPreviewImageForMedia:(AUTELMedia *_Nonnull)media
withCompletion:(AUTELLowerResolutionImageCompletion)block;// 获取文件的相应预览图.

3.4 获取视频缓存或下载视频

- (void)playerItemForVideo:(AUTELMedia *_Nonnull)media
withCompletion:(void (^_Nullable)(AVPlayerItem *_Nullable playerItem, BOOL isOriginalData, NSError * _Nullable error))block;// 是否存在缩略视频文件,不存在,判断是否存在原视频文件,不存在,请求缩略视频文件

调用示例

func fetchPlayerItem(media: ATMediaItem, completion: @escaping (Result<AVPlayerItem, Error>) -> Void) {
self.playbackManager?.playerItem(forVideo: media.media, withCompletion: { (item, flag, error) in
if let error = error{
completion(.failure(error.toPlaybackError))
return
}
if let item = item{
completion(.success(item))
}
})
}

3.5 删除资源文件

- (void)deleteMediaItem:(AUTELMedia *_Nonnull)media
withCompletion:(AUTELCompletionBlock)block;// 删除资源文件.

- (void)deleteMedia:(NSArray<AUTELMedia *> *_Nonnull)medias
withProgress:(AUTELFileDeletingProgress)progressBlock
deleteListCompletion:(AUTELFileDeleteListCompletion)deleteListBlock
overallCompletion:(AUTELFileDeleteCompletion)deleteAllBlock;

调用示例

func delete(medias: [AUTELMedia], progress: AUTELFileDeletingProgress?, completion: @escaping (Result<[AUTELMedia], Error>) -> Void) {
playbackManager?.deleteMedia(medias, withProgress: progress, deleteListCompletion: nil, overallCompletion: { (deleteMedias, error) in
if let error = error {
completion(.failure(error.toPlaybackError))
return
}
guard let deleteMedias = deleteMedias else{
completion(.failure(ATAlbumError.delete))
return
}
let mds = medias.filter { media -> Bool in
!deleteMedias.contains(where: { $0.uniqueID == media.uniqueID })
}
completion(.success(mds))
})
}

3.6 下载资源文件

- (void)downloadMedia:(NSArray<AUTELMedia *> *_Nonnull)medias
withPreparation:(AUTELFileDownloadPreparingBlock)prepareBlock
progress:(AUTELFileDownloadProgressBlock)progressBlock
downloadCompletion:(AUTELFileDownloadCompletionBlock)fileCompletionBlock
overallCompletion:(AUTELCompletionBlock)overallCompletionBlock;


- (void)downloadMedia:(NSArray<AUTELMedia *> *_Nonnull)medias
withType:(AUTELDownloadVideoResolutionType)resolutionType
preparation:(AUTELFileDownloadPreparingBlock)prepareBlock
progress:(AUTELFileDownloadProgressBlock)progressBlock
downloadCompletion:(AUTELFileDownloadCompletionBlock)fileCompletionBlock
overallCompletion:(AUTELCompletionBlock)overallCompletionBlock;

调用示例

func downloadOriginal(medias: [ATMediaItem]) {
mediaItems = medias
let medias = mediaItems.compactMap({ $0.media })
let totalBytes = medias.reduce(0) { (result, media) -> Int64 in
return result + media.fileSizeInBytes
}
printLog("totalBytes:",totalBytes)
var downloadedCount: Int = 0
let type = ATSettings.shared.flieExporLeve
ATMediaDataManager.shared.playbackManager?.downloadMedia(medias, with: type == 0 ? AUTELDownloadVideoResolutionType.thumbnail : .original, preparation: { (media, type, skip) in
guard let media = media else { return }
self.currentMedia = media.copy() as? AUTELMedia
self.preSize = 0
self.currentSize = 0
if skip{
downloadedCount += 1
}
}, progress: {[weak self] (total, expected) in
let progress = CGFloat(total)/CGFloat(expected)
self?.progressHandler?(downloadedCount,CGFloat(progress))
self?.currentSize = total
printLog("downloadMedia progress",progress,downloadedCount + 1)
}, downloadCompletion: { (location) in
guard let media = self.currentMedia, let uniqueID = location?.lastPathComponent.components(separatedBy: ".").first else { return }
printLog("downloadCompletion",location,uniqueID)
media.setValue(uniqueID, forKey: "uniqueID")
downloadedCount += 1
self.preSize = 0
self.currentSize = 0
self.saveMedia(media,originalPath: location)
}, overallCompletion: { (error) in
logInfo("图片下载失败:name:\(self.currentMedia?.fileName ?? ""), err:\(error?.localizedDescription) 回调 \(self.completionHandler)")
if let err = error as NSError? {
if let wifiDownload = err.userInfo["wifiDownload"] as? NSNumber,wifiDownload.boolValue == true,err.code != -999 {
if DroneConnection.shared.isConnected {
NotificationCenter.default.post(name: NSNotification.Name("WiFiDisConnectDownloadFailToRCDownload"), object: nil)
self.stopSpeedTimer()
return
}
}
}
self.completionHandler?(error?.toPlaybackError)
self.stopSpeedTimer()
})
startSpeedTimer()
}

3.7 取消下载

- (void)cancelDownloadingWithCompletion:(AUTELCompletionBlock)completion;

/** 取消所有下载<包括缩略图、高清图> */
- (void)cancelAllDownload;

- (void)cancelAllDownloadWithCompletionBlock:(void(^)())completionBlock;

调用示例

func cancelDownload(_ completionBlock:@escaping (()->())) {
if let playbackManager = playbackManager {
playbackManager.cancelDownloading(completion: { error in
completionBlock()
})
} else {
completionBlock()
}
}

4. 相册接入流程

下面以Autel_UX_SDK demo为例讲解如何进行相册功能开发。

在本例中,为方便相册功能开发,我们对底层Mobile SDK相册提供的API做了二次封装,主要涉及的类有ATMediaDataManagerATAlbumViewController

  • ATAlbumViewController:相册视图控制器,主要支持SD卡、机载闪存、本地相册的相册列表的展示、下载、播放、删除功能的业务。
  • ATMediaDataManager:对Mobile SDK提供API的二次封装,同时实现了本地相册的功能封装。

上述类封装在AutelUXFramework.framework动态库。

4.1 配置获取资源文件来源

4.1.1 资源文件存储类型

/** 媒体存储类型 */
public enum ATAlbumStoreType : Int {
case sdCard // 资源文件存储于SD卡
case flash // 资源文件存储于机载闪存
case local // 资源文件存储于手机本地相册
}

image-20220804155808272

4.1.2 更新资源文件来源

在demo中,切换相册导航栏的SD卡、机载闪存、本地相册时,对应的要更新ATMediaDataManager.shared.storeType = type,保证能正确的读取到对应来源的资源文件。

比如,要请求SD卡内的资源,我们需要先配置ATMediaDataManager.shared.storeType = ATAlbumStoreType.sdCard

func switchDidChange(index: Int) {
guard let type = ATAlbumStoreType(rawValue: index) else {
return
}
ATMediaDataManager.shared.storeType = type
collectionView.mj_header?.beginRefreshing()
}

4.2 获取资源文件

配置好资源文件来源(SD卡、机载闪存、本地相册)后,开始请求相册资源。

4.2.1 枚举类型

4.2.1.1 资源文件类型
typedef NS_ENUM(NSInteger, AUTELGetFileListType) {
/**
* @brief Image file
*
* List of all files
*/
AUTELGetFileListTypeAll = 0,

/**
* @brief Video file list
*
* 视频文件列表
*/
AUTELGetFileListTypeVideo = 1,

/**
* @brief Picture file list
*
* 图片文件列表
*/
AUTELGetFileListTypePhoto = 2,
};
4.2.1.2 资源文件类型
public enum ATMediaType : Int {

/** 照片 */
case photo

/** 视频 */
case video

/** 全景 */
case panaromic

/** 定时拍 */
case burst

/** 未知 */
case unknown
}

4.2.2 拉取相册列表

函数名

public func getFileList(with fileListType: AUTELGetFileListType = .all, offset: Int, count: Int, handler: @escaping (Result<[AUTELMedia], Error>) -> Void)

函数实例

ATMediaDataManager.shared.getFileList(with: .all, offset: pageIndex == 0 ? 0 : allMediaItems.count, count: pageCount) {[weak self] (result) in
completion()
guard let self = self else { return }
switch result{
case let .success(medias):
self.configMediaItems(medias)
case let .failure(error):
GCDUtils.main {
self.configMediaItems([])
ToastManager.shared.show(title: error.localizedDescription)
}
}
}

参数说明

fileListType(资源文件类型,见2.2.1.1):AUTELGetFileListType。可拉取全部类型资源文件,也可以单独拉取照片或单独拉取视频

offset(分页,从0开始):Int

count(每页数量,最大50):Int

handler(回调):<[AUTELMedia], Error>

4.2.3 获取首张照片缩略图

函数名

public func getFirstThumbImage(completion: @escaping (UIImage?) -> Void)

函数调用示例

private func setupThumbnail(){
if thumbnailImage.value != nil {
return
}
ATMediaDataManager.shared.getFirstThumbImage(completion: {[weak self] (image) in
if image != nil {
self?.thumbnailImage.accept(image)
}
})
}

4.2.4 获取本地相册缩略图(文件路径)

函数名

public func fetchThumbImagePath(media: AUTELMedia) -> String?

函数调用示例

func setAlbumLocalThumbImage(with media: ATMediaItem) {
GCDUtils.main {
guard let path = ATMediaDataManager.shared.fetchThumbImagePath(media: media.media) else{
return
}
self.image = UIImage(contentsOfFile: path)
}
}

4.2.5 获取本地相册原文件(文件路径)

函数名

public func fetchOriginalImagePath(media: AUTELMedia) -> String?

函数调用示例

func setAlbumLocalOriginalImage(with media: ATMediaItem) {
setAlbumLocalThumbImage(with: media)
guard let path = ATMediaDataManager.shared.fetchOriginalImagePath(media: media.media) else{
return
}
if media.mediaType == .video{
coverFromVideoPath(path, completion: { (image) in
GCDUtils.main {
self.image = image
}
})
return
}
GCDUtils.main {
self.image = UIImage(contentsOfFile: path)
}
}

4.2.6 读取资源文件基本信息

public struct ATMediaItem {

/** 资源文件 */
public var media: AUTELMedia

/** 文件类型 */
public var mediaType: AutelUXFramework.ATMediaType

/** 文件名(带后缀) */
public var fileName: String?

/** 创建时间戳 */
public var timestamp: TimeInterval

/** 视频时长 */
public var videoLength: String

/** 缩略图下载器 */
public let lowProvider: AutelUXFramework.ATImageDataProvider

/** 预览图下载器 */
public let highProvider: AutelUXFramework.ATImageDataProvider

/** 是否选中 */
public var isSelected: Bool

/** 选中下标 */
public var selectIndex: Int

/** 是否下载 */
public var isDownloaded: Bool { get }

public init(media: AUTELMedia, dateFormatter: DateFormatter?)
/**获取视频封面图。URL:视频文件本地路径*/
public func coverFromVideoPath(_ url: URL?) -> UIImage?
}

4.3 下载资源文件

4.3.1 枚举类型

4.3.1.1 资源清晰度(下载)
public enum PhotoResolutionType : Int {
/// 低清
case low
/// 高清
case high
/// 原文件
case original
/// 未知
case unknown
}

4.3.2 下载单个图片文件(原图,非缩略图)

函数名

public func startDownload(media: AUTELMedia, resoType: AutelUXFramework.PhotoResolutionType, completion: @escaping (Result<Data, Error>) -> Void)

函数调用示例

ATMediaDataManager.shared.startDownload(media: media, resoType: .original) { result in           
}

参数说明

media(资源文件):AUTELMedia

resoType(清晰度,见2.2.1.2):PhotoResolutionType

completion(回调):<Data, Error>

4.3.3 下载缩略图(小)

利用资源文件基本信息模型中的缩略图下载器进行下载并缓存本地,依赖swift常用图片下载缓存管理库:kingfisher

属性名

lowProvider // 缩略图(小)下载管理器

函数调用示例

imageView.setAlbumThumbImage(with: media)
func setAlbumThumbImage(with media: ATMediaItem) {
GCDUtils.main {
self.kf.setImage(with: media.lowProvider, placeholder: self.placeholderImage)
}
}

4.3.4 下载预览图(大)

利用资源文件基本信息模型中的预览图下载器进行下载并缓存本地,依赖swift常用图片下载缓存管理库:kingfisher

属性名

highProvider // 预览图(大)下载管理器

函数调用示例

func config(media: ATMediaItem) {
self.media = media
imageView.setAlbumHighImage(with: media)
updateLayout()
}
func setAlbumHighImage(with media: ATMediaItem) {
GCDUtils.main {
self.kf.setImage(with: media.lowProvider, placeholder: self.placeholderImage, options: nil) {[weak self] (result) in
var placeImage: UIImage?
if case let .success(image) = result{
placeImage = image.image
}
self?.kf.setImage(with: media.highProvider, placeholder: placeImage ?? self?.placeholderImage, options: nil, completionHandler: nil)
}
}
}

4.3.5 下载多个图片文件

函数名

public func downloadOriginal(medias: [AutelUXFramework.ATMediaItem])

函数调用示例

func downloadItems(_ medias: [ATMediaItem]) {
ATMediaDataManager.shared.downloadOriginal(medias: medias)
showDownloadProgress(medias.count)
}

4.3.6 取消全部下载任务

函数名

public func cancelAllDownload()// 取消下载队列所有任务,结束正在下载的任务

函数调用示例

ATMediaDataManager.shared.cancelAllDownload(completionBlock)

4.3.7 取消当前下载任务

函数名

public func cancelDownload()

函数调用示例

func showDownloadProgress(_ mediasCount: Int) {
let progressView = ATAlbumProgressView()

var countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), 1,mediasCount)
var speedTitle = ": 0K/s" + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")

let pop = ATPopup<ATAlert>.build.theme(.white).title(countTitle + speedTitle)
.addConfirmButton(title: "ATAlbumCancel".fLocal("ATAlbum")) { (alert) in
alert.hide(animiated: true)
ATMediaDataManager.shared.cancelDownload()
}.addCustomView(progressView).done()
pop.show(animiated: true)

progressView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
make.width.height.equalTo(55.fit)
}

let downloader = ATMediaDataManager.shared.downloader
downloader.progressHandler = {[weak progressView, weak pop] count,progress in
GCDUtils.main {
progressView?.update(progress)
countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), count + 1,mediasCount)
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}

}
downloader.speedHandler = {[weak pop] speed in
GCDUtils.main {
speedTitle = ": " + speed + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}
}
downloader.completionHandler = { [weak self,weak pop] error in
GCDUtils.main {
pop?.hide(animiated: true)
if error != nil{
self?.showSuccessAlert(false)
}else{
self?.showFailedAlert(false)
}
}
}
}

4.4 删除资源文件

4.4.1 删除资源文件

函数名

public func delete(medias: [AUTELMedia], progress: AUTELFileDeletingProgress?, completion: @escaping (Result<[AUTELMedia], Error>) -> Void)

函数调用示例

func deleteItems(_ medias: [AUTELMedia]) {
var progressHandler: AUTELFileDeletingProgress?
var completionHandler: (() -> Void)?
showDeleteProgress(medias.count, progressHandler: &progressHandler, completionHandler: &completionHandler)
ATMediaDataManager.shared.delete(medias: medias, progress: progressHandler) { result in
GCDUtils.main {
completionHandler?()
switch result{
case let .success(medias):
self.removeItems(medias: medias)
self.showSuccessAlert()
case .failure(_):
self.showFailedAlert()
}
}
}
}

4.4.2 取消删除

函数名

public func cancelDelete()// 将所有删除任务清空,结束删除

函数调用示例

ATMediaDataManager.shared.cancelDelete()

4.5 播放视频文件

4.5.1 获取视频资源文件(视频)

函数名

public func fetchPlayerItem(media: AUTELMedia, completion: @escaping (Result<AVPlayerItem, Error>) -> Void)

函数调用示例

func play(_ playerView: ATPlayerView) {
guard let media = media else {
return
}
if let media = media as? ATMediaItem, let localPath = ATMediaDataManager.shared.fetchLocalPath(media: media, media.videoPathType) {// 如果存在视频media,并且media存在本地文件路径
let item = AVPlayerItem(url: URL(fileURLWithPath: localPath))
playerView.setPlayerItem(item)
playerView.player?.autoReplay = false
}else {// 通过此方法获取视频文件路径
loadingIndicatorView.startAnimating()
media.fetchPlayerItem {[weak playerView] result in
GCDUtils.main {
self.loadingIndicatorView.stopAnimating()
switch result{
case let .success(item):
playerView?.setPlayerItem(item)
playerView?.player?.autoReplay = false
playerView?.player?.uniqueID = media.uniqueID
playerView?.player?.play()
case .failure(_):
// ToastManager.shared.show(title: error.localizedDescription)
break
}
}
}
}
playerView.player?.uniqueID = media.uniqueID
contentView.addSubview(playerView)
playerView.snp.remakeConstraints { (make) in
make.edges.equalTo(imageView)
}
playerView.player?.play()
self.playerView = playerView
contentView.layoutIfNeeded()
}

4.6 下载管理模块

4.6.1 获取下载管理模块

属性调用示例

let downloader = ATMediaDataManager.shared.downloader

通过let downloader = ATMediaDataManager.shared.downloader获取到下载管理模块后,可以通过调用下载进度回调progressHandler、下载速度回调speedHandler、完成回调completionHandler来进行界面展示

func showDownloadProgress(_ mediasCount: Int) {
let progressView = ATAlbumProgressView()

var countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), 1,mediasCount)
var speedTitle = ": 0K/s" + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")

let pop = ATPopup<ATAlert>.build.theme(.white).title(countTitle + speedTitle)
.addConfirmButton(title: "ATAlbumCancel".fLocal("ATAlbum")) { (alert) in
alert.hide(animiated: true)
ATMediaDataManager.shared.cancelDownload()
}.addCustomView(progressView).done()
pop.show(animiated: true)

progressView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
make.width.height.equalTo(55.fit)
}

let downloader = ATMediaDataManager.shared.downloader
downloader.progressHandler = {[weak progressView, weak pop] count,progress in
GCDUtils.main {
progressView?.update(progress)
countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), count + 1,mediasCount)
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}

}
downloader.speedHandler = {[weak pop] speed in
GCDUtils.main {
speedTitle = ": " + speed + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}
}
downloader.completionHandler = { [weak self,weak pop] error in
GCDUtils.main {
pop?.hide(animiated: true)
if error != nil{
self?.showSuccessAlert(false)
}else{
self?.showFailedAlert(false)
}
}
}
}

4.6.2 展示下载进度

通过let downloader = ATMediaDataManager.shared.downloader获取下载管理器,进而获取下载速度、下载进度、完成情况。

函数调用示例

func showDownloadProgress(_ mediasCount: Int) {
let progressView = ATAlbumProgressView()

var countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), 1,mediasCount)
var speedTitle = ": 0K/s" + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")

let pop = ATPopup<ATAlert>.build.theme(.white).title(countTitle + speedTitle)
.addConfirmButton(title: "ATAlbumCancel".fLocal("ATAlbum")) { (alert) in
alert.hide(animiated: true)
ATMediaDataManager.shared.cancelDownload()
}.addCustomView(progressView).done()
pop.show(animiated: true)

progressView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
make.width.height.equalTo(55.fit)
}

let downloader = ATMediaDataManager.shared.downloader
downloader.progressHandler = {[weak progressView, weak pop] count,progress in
GCDUtils.main {
progressView?.update(progress)
countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), count + 1,mediasCount)
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}

}
downloader.speedHandler = {[weak pop] speed in
GCDUtils.main {
speedTitle = ": " + speed + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}
}
downloader.completionHandler = { [weak self,weak pop] error in
GCDUtils.main {
pop?.hide(animiated: true)
if error != nil{
self?.showSuccessAlert(false)
}else{
self?.showFailedAlert(false)
}
}
}
}