iOS17设备中PHPickerViewController获取视频缩略图权限问题求助
iOS17真机获取视频缩略图报“无权限查看”问题解决
问题原因
iOS17开始,通过UIDocumentPickerViewController或PHAsset的itemProvider获取的媒体文件URL是安全范围URL(Security-Scoped URL),这类URL包含权限校验参数(即你打印路径里的&type=3&mode=2等)。直接用AVURLAsset访问时,因未主动申请并保留访问权限,会触发“无权限查看”错误。模拟器沙箱限制较宽松,所以不会出现该问题。
解决方案
1. 保留安全范围URL的访问权限
对获取到的URL,必须调用startAccessingSecurityScopedResource()申请访问权限,操作完成后调用stopAccessingSecurityScopedResource()释放权限,这一对方法必须配对使用。
2. 避免将URL转为字符串处理
你当前代码把NSURL转成absoluteString再处理的方式错误,会丢失安全范围URL的权限信息,应直接使用URL类型对象。
修改后的代码示例
获取视频的代码调整:
guard results.count > 0 else { return } for asset in results { if asset.itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { asset.itemProvider.loadItem(forTypeIdentifier: UTType.movie.identifier) { [weak self] movie, error in guard let self = self, error == nil else { return } // 直接转为URL,不要转成字符串 guard let url = movie as? URL else { return } // 检查是否是安全范围URL并申请权限 guard url.startAccessingSecurityScopedResource() else { print("无法获取文件访问权限") return } defer { // 操作完成后释放权限 url.stopAccessingSecurityScopedResource() } // 调用缩略图生成方法 do { if let thumbnail = try url.thumbnailForMovie() { // 处理生成的缩略图 } } catch { print("生成缩略图失败:\(error)") } } } }
获取缩略图的代码调整:
extension URL { func thumbnailForMovie() throws -> UIImage? { let asset = AVURLAsset(url: self, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true]) let generator = AVAssetImageGenerator(asset: asset) generator.appliesPreferredTrackTransform = true // 用CMTimeZero取第一帧更稳妥,避免使用无效的asset.duration let timestamp = CMTime.zero do { let cgImage = try generator.copyCGImage(at: timestamp, actualTime: nil) return UIImage(cgImage: cgImage) } catch { throw error } } }
额外注意事项
- 确保App已开启App Sandbox权限(在Xcode的Signing & Capabilities中开启),并勾选
User Selected File的读写权限(若从文件选择器获取资源)。 - 若从相册获取
PHAsset,也可直接用PHImageManager获取缩略图,绕开安全范围URL的权限处理:
let imageManager = PHImageManager.default() let requestOptions = PHImageRequestOptions() requestOptions.isSynchronous = true requestOptions.deliveryMode = .highQualityFormat imageManager.requestImage(for: asset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: requestOptions) { image, _ in // 处理拿到的缩略图image }
内容的提问来源于stack exchange,提问作者BraveDev
相关产品推荐
相关产品推荐

