K.W*_*.Wu 1 ios avasset swift phasset
我在 Swift 中编写了一个图像选择器(NOT UIImagePickerController),按下“完成”后,我返回所选 PHAssets(图像或视频或两者)的 URL,然后将它们上传到 firebase 存储。
图像的 URL 如下所示: file:///var/mobile/Media/DCIM/101APPLE/IMG_1239.PNG
视频的 URL 如下所示: file:///var/mobile/Media/DCIM/101APPLE/IMG_1227.MOV
然后我显示 PHAsset。播放视频资产没有问题:
let player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.frame
playerLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(playerLayer)
player.play()
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试使用其 URL 显示图像时,出现“无权读取文件”错误:
do {
let data = try Data(contentsOf: url)
imageView.image = UIImage(data: data)
} catch {
print(error)
}
Run Code Online (Sandbox Code Playgroud)
我确实有权访问用户的照片库,这就是图像选择器工作正常的原因。我只是无法使用它们的 URL 读取图像文件。
我在想,也许我应该将图像资产复制到临时文件并从那里读取它,但是为什么视频资产可读但不是图像资产没有任何意义。
- - - - - - - 更新 - - - - - - -
这就是我获取 PHAsset 网址的方式
extension PHAsset {
func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)) {
if self.mediaType == .image {
let options = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = { (adjustmeta: PHAdjustmentData) -> Bool in
return true
}
self.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) in
completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
})
} else if self.mediaType == .video {
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl: URL = urlAsset.url as URL
completionHandler(localVideoUrl)
} else {
completionHandler(nil)
}
})
}
}
}
Run Code Online (Sandbox Code Playgroud)
iOS 中的应用程序以沙盒模式运行,以防止它访问其自身范围之外的文件。
一个网址,例如:
file:///var/mobile/Media/DCIM/101APPLE/...
不在您的应用程序的范围内,因此不能通过 直接访问Data(contentsOf:)。
AVPlayer但是,能够访问外部urls,因为它具有特殊权利,这就是您能够使用url.
要加载从图像url中,我们会用这个url内PHAssets.fetchAssets(withALAssetURLs:options:)取回PHAsset,以加载它,但是该方法已在iOS的11被弃用。
作为替代,PHAssets.fetchAssets(withLocalIdentifiers:options:)可用,但为了使用它,您需要提供一个localIdentifier.
为此url,您应该保存PHAssets ,而不是保存localIdentifiers。
PHAsset的asset.localIdentifiers 而不是他们的urls需要时,返回PHAsset使用[1]:
PHAssets.fetchAssets(withLocalIdentifiers:options:)
Run Code Online (Sandbox Code Playgroud)过程PHAsset,根据它的mediaType
对于图像[2]:
PHImageManager().requestImageData(for:options:resultHandler:)
Run Code Online (Sandbox Code Playgroud)对于视频[3]:
PHImageManager().requestAVAsset(forVideo:options:resultHandler:)
Run Code Online (Sandbox Code Playgroud)警告:如果您的设置出现问题,已被用于故意终止应用程序。
如果它崩溃,请通过以下方式检查您对照片库的使用和/或许可状态 fatalError()Info.plistNSPhotoLibraryUsageDescriptionPHPhotoLibrary.requestAuthorization(_:)
获取最新资产的演示功能(图像/视频;您决定)
func getLatestAsset(for type: PHAssetMediaType) {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate",
ascending: false)]
fetchOptions.fetchLimit = 2
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: type,
options: fetchOptions)
guard let asset = fetchResult.firstObject else { fatalError("Unable to fetch photos") }
//save `localIdentifier` for later use (app relaunch or whatever)
let assetIdentifier = asset.localIdentifier
print(assetIdentifier)
//Just a demo that you can get the same asset based on a local identifier
loadAsset(identifier: assetIdentifier)
}
Run Code Online (Sandbox Code Playgroud)基于一个加载资源的函数 localIdentifier
func loadAsset(identifier: String) {
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier],
options: nil).firstObject
else {
fatalError("No Asset found with identifier: \(identifier)")
}
print(asset.localIdentifier)
//Do something with the asset now
if asset.mediaType == .image {
loadImage(asset: asset)
}
else if asset.mediaType == .video {
loadVideo(asset: asset)
}
}
Run Code Online (Sandbox Code Playgroud)从一个图像加载 PHAsset
func loadImage(asset: PHAsset) {
PHImageManager().requestImageData(for: asset, options: nil) { (data, string, orientation, userInfo) in
guard let data = data else { fatalError("Image Data is nil") }
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
}
}
Run Code Online (Sandbox Code Playgroud)从一个加载视频 PHAsset
func loadVideo(asset: PHAsset) {
PHImageManager().requestAVAsset(forVideo: asset, options: nil) { (avAsset, audioMix, userInfo) in
guard let avAsset = avAsset else { fatalError("AVAsset is nil") }
let playerItem = AVPlayerItem(asset: avAsset)
let player = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = .resizeAspectFill
DispatchQueue.main.async {
self.view.layer.addSublayer(playerLayer)
player.play()
}
}
}
Run Code Online (Sandbox Code Playgroud)PS:您将不再需要,getURL(completionHandler:)因为您的整个逻辑将基于localIdentifiers 而不是urls,我会说这要好得多。
Urls只是,有点,善变,所以让我们至少在这里避免它们。
| 归档时间: |
|
| 查看次数: |
1575 次 |
| 最近记录: |