请求AVAssets时维护顺序

时间:2017-08-19 19:27:03

标签: ios swift grand-central-dispatch photosframework

我正在尝试构建一个视频合并应用,允许用户从集合视图中选择几个短片段,然后生成所有合并为一个视频的预览。我正在使用Photos框架(PHCachingImageManager)来填充集合视图,并将所选PHAssets的数组传递给下面的函数,以便请求低质量的AVAssets(用于合并和生成预览)。

问题是,我需要按照用户选择它们的顺序保留AVAssets,但“requestAVAsset”函数是异步的,并且通常多次调用完成处理程序。我以前从未使用过Dispatch Groups,但是试图在下面使用它们......并且AVAssets有时仍然无序。

func requestAVAssets(assets: [PHAsset]) -> [AVAsset] {
    var videoArray: [AVAsset] = []
    let dispatchGroup = DispatchGroup()
    let videoOptions = PHVideoRequestOptions()
    videoOptions.isNetworkAccessAllowed = true
    videoOptions.deliveryMode = .fastFormat
    for asset in assets {
        dispatchGroup.enter()
        self.imageManager.requestAVAsset(forVideo: asset, options: videoOptions, resultHandler: { (video, audioMix, info) in
            guard video != nil else { return }
            videoArray.append(video!)
            dispatchGroup.leave()
        })
    }
    dispatchGroup.wait()
    return videoArray
}

我猜我错了一些代码或者以完全错误的方式接近它!任何建议都表示赞赏。

2 个答案:

答案 0 :(得分:1)

完全躲避调度问题,因为它已经很晚了,而且我有一个糟糕的一天,但如果你保持与视频相关的“正确”索引然后排序呢?我觉得这样的事情会起作用。

struct SelectedVideo {
    let index: Int
    let asset: AVAsset
}

func requestAVAssets(assets: [PHAsset]) -> [AVAsset] {
    var videoArray: [SelectedVideo] = []
    let dispatchGroup = DispatchGroup()
    let videoOptions = PHVideoRequestOptions()
    videoOptions.isNetworkAccessAllowed = true
    videoOptions.deliveryMode = .fastFormat
    for (index, asset) in assets.enumerated() {
        dispatchGroup.enter()
        self.imageManager.requestAVAsset(forVideo: asset, options: videoOptions, resultHandler: { (videoMb, audioMixMb, infoMb) in
            guard let video = videoMb else return
            videoArray.append(SelectedVideo(index, video))
            dispatchGroup.leave()
        })
    }
    dispatchGroup.wait()
    return videoArray.sort { $0.index < $1.index}.map({$0.video})
}

这是一种黑客攻击(甚至没有尝试编译它),但就像我说的那样,这是糟糕的一天。

一些小改动要注意:我将params改为闭包,说“Mb”,这意味着“也许”,这是一个很好的约定,我已经看到了传递给闭包的命名选项。此外,而不是“保护视频!= nil”,然后强制解包,更喜欢做“看守视频= videoMb”,然后视频是非可选的。

答案 1 :(得分:1)

如果在迭代AVAssets时捕获当前索引,则可以插入而不是追加。这就是我如何做到的,至少。

func requestAVAssets(assets: [PHAsset]) -> [AVAsset] {
    var videoArray = [AVAsset?](repeating: nil, count: assets.count)
    let videoOptions = PHVideoRequestOptions()
    videoOptions.isNetworkAccessAllowed = true
    videoOptions.deliveryMode = .fastFormat
    for (i, asset) in assets.enumerated() {
        self.imageManager.requestAVAsset(forVideo: asset, options: videoOptions, resultHandler: { (video, audioMix, info) in
            guard let video = video else { return }
            videoArray.remove(at: i)
            videoArray.insert(video, at: i)
        })
    }
    return videoArray.flatMap { $0 }
}

为数组提供所需数量的项目nil将阻止其在插入项目时出错,然后在下载完成后删除现有的nil值并将其替换为实际的AVAsset

最后,flatMap生成的数组解压缩选项(并可选择通过将它与传入的assets数组进行比较来检查您是否拥有所需数量的项目。)