You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS应用:从网络URL加载PHLivePhoto及本地LivePhoto展示咨询

从网络URL加载PHLivePhoto的实现方案

嘿,我明白你已经能搞定本地LivePhoto的展示了,要从网络加载的话核心思路是先把网络上的JPG和MOV文件下载到本地临时目录——因为PHLivePhoto的初始化只接受本地文件URL,不能直接用远程链接。下面给你一步步拆解实现:

核心步骤

  • 异步下载LivePhoto对应的图片和视频资源到本地临时文件夹
  • 使用下载后的本地URL请求生成PHLivePhoto实例
  • 将生成的LivePhoto展示到PHLivePhotoView中
  • 处理下载/加载过程中的错误,以及临时文件的清理

完整代码示例

首先确保你导入了必要的框架:

import UIKit
import PhotosUI

然后在视图控制器里实现完整逻辑:

class LivePhotoViewController: UIViewController {
    // 用来展示LivePhoto的视图
    private let livePhotoView = PHLivePhotoView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupLivePhotoView()
        
        // 替换成你的网络图片和视频URL
        let remoteImageURL = URL(string: "https://example.com/your-live-photo.jpg")!
        let remoteVideoURL = URL(string: "https://example.com/your-live-photo.mov")!
        
        loadLivePhoto(fromImageURL: remoteImageURL, videoURL: remoteVideoURL)
    }
    
    private func setupLivePhotoView() {
        livePhotoView.frame = view.bounds
        livePhotoView.contentMode = .scaleAspectFit
        view.addSubview(livePhotoView)
        // 设置加载占位图
        livePhotoView.placeholderImage = UIImage(named: "placeholder")
    }
    
    private func loadLivePhoto(fromImageURL imageURL: URL, videoURL: URL) {
        // 用DispatchGroup等待两个下载任务完成
        let downloadGroup = DispatchGroup()
        var localImageURL: URL?
        var localVideoURL: URL?
        var downloadError: Error?
        
        // 下载图片资源
        downloadGroup.enter()
        URLSession.shared.downloadTask(with: imageURL) { tempURL, _, error in
            defer { downloadGroup.leave() }
            guard let tempURL = tempURL, error == nil else {
                downloadError = error
                return
            }
            // 移动到临时目录并保留原文件名
            let fileManager = FileManager.default
            let tempDir = fileManager.temporaryDirectory
            let destImageURL = tempDir.appendingPathComponent(imageURL.lastPathComponent)
            do {
                if fileManager.fileExists(atPath: destImageURL.path) {
                    try fileManager.removeItem(at: destImageURL)
                }
                try fileManager.moveItem(at: tempURL, to: destImageURL)
                localImageURL = destImageURL
            } catch {
                downloadError = error
            }
        }.resume()
        
        // 下载视频资源
        downloadGroup.enter()
        URLSession.shared.downloadTask(with: videoURL) { tempURL, _, error in
            defer { downloadGroup.leave() }
            guard let tempURL = tempURL, error == nil else {
                downloadError = error
                return
            }
            let fileManager = FileManager.default
            let tempDir = fileManager.temporaryDirectory
            let destVideoURL = tempDir.appendingPathComponent(videoURL.lastPathComponent)
            do {
                if fileManager.fileExists(atPath: destVideoURL.path) {
                    try fileManager.removeItem(at: destVideoURL)
                }
                try fileManager.moveItem(at: tempURL, to: destVideoURL)
                localVideoURL = destVideoURL
            } catch {
                downloadError = error
            }
        }.resume()
        
        // 等待下载完成后生成LivePhoto
        downloadGroup.notify(queue: .main) { [weak self] in
            guard let self = self else { return }
            if let error = downloadError {
                print("资源下载失败:\(error.localizedDescription)")
                // 这里可以添加错误提示UI逻辑
                return
            }
            guard let imgURL = localImageURL, let vidURL = localVideoURL else {
                print("本地资源URL无效")
                return
            }
            
            // 请求生成PHLivePhoto实例
            PHLivePhoto.request(withResourceFileURLs: [imgURL, vidURL],
                                placeholderImage: self.livePhotoView.placeholderImage,
                                targetSize: self.livePhotoView.bounds.size,
                                contentMode: .scaleAspectFit) { livePhoto, info in
                if let livePhoto = livePhoto {
                    self.livePhotoView.livePhoto = livePhoto
                } else {
                    let error = info[PHLivePhotoInfoErrorKey] as? Error
                    print("LivePhoto生成失败:\(error?.localizedDescription ?? "未知错误")")
                }
                
                // 可选:使用完临时文件后删除,避免占用存储空间
                DispatchQueue.global().async {
                    let fileManager = FileManager.default
                    try? fileManager.removeItem(at: imgURL)
                    try? fileManager.removeItem(at: vidURL)
                }
            }
        }
    }
}

关键注意点

  • 异步操作:所有网络下载和文件操作都放在后台线程执行,避免阻塞主线程,最后在主线程更新UI
  • 临时文件管理:下载的文件存在系统临时目录,使用后建议删除,防止占用设备空间
  • 错误处理:覆盖了下载失败、URL无效、LivePhoto生成失败等异常场景,提升用户体验
  • 占位图:设置合适的占位图可以在加载过程中保持界面友好

内容的提问来源于stack exchange,提问作者sanchez23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:25:26