相册组件 PHPickerViewController

2024-08-07  本文已影响0人  大成小栈

PHPickerViewController

iOS的相册系统组件UIImagePickerController 目前已经不推荐使用,我们需要逐渐切换到新的组件 PHPickerViewController 上来:

import UIKit
import PhotosUI

class ViewController: UIViewController, UINavigationControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    @IBAction func openClick(_ sender: UIButton) {
        var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
        configuration.filter = PHPickerFilter.any(of: [.livePhotos, .videos])
        configuration.selectionLimit = 1
        
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self
        present(picker, animated: true, completion: nil)
    }

    func presentVideoEditor(for url: URL) {
        // 调试路径打印
           print("Video URL: \(url.path)")

           // 确保视频文件路径存在
           let fileManager = FileManager.default
           if fileManager.fileExists(atPath: url.path) {
               print("File exists at path: \(url.path)")
           } else {
               print("File does not exist at path: \(url.path)")
               return
           }
           
           // 验证视频是否可编辑
           if UIVideoEditorController.canEditVideo(atPath: url.path) {
               let editor = UIVideoEditorController()
               editor.videoMaximumDuration = 10
               editor.videoPath = url.path
               editor.delegate = self
               present(editor, animated: true, completion: nil)
           } else {
               print("This video cannot be edited.")  // 打印调试信息
           }
    }

}

extension ViewController: PHPickerViewControllerDelegate {
    
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true, completion: nil)
        
        guard let provider = results.first?.itemProvider else {
            return
        }
        
        if provider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
            provider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { (url, error) in
                if let error = error {
                    print("Error loading video file: \(error.localizedDescription)")
                    return
                }
                
                guard let tempURL = url else {
                    print("Error: URL is nil")
                    return
                }
                
                // 拷贝临时文件到合适的位置,以防止被删除
                let destinationURL = FileManager.default.temporaryDirectory.appendingPathComponent(tempURL.lastPathComponent)
                do {
                    if FileManager.default.fileExists(atPath: destinationURL.path) {
                        try FileManager.default.removeItem(at: destinationURL)
                    }
                    try FileManager.default.copyItem(at: tempURL, to: destinationURL)
                    print("Video copied to \(destinationURL)")
                    
                    // 使用系统的视频编辑器
                    DispatchQueue.main.async {
                        self.presentVideoEditor(for: destinationURL)
                    }
                } catch {
                    print("Error copying video file: \(error.localizedDescription)")
                }
            }
        } else {
            print("load file fail!")
        }
    }
}


extension ViewController: UIVideoEditorControllerDelegate {
    func videoEditorController(_ editor: UIVideoEditorController, didSaveEditedVideoToPath editedVideoPath: String) {
        editor.dismiss(animated: true, completion: nil)
        print("Edited video saved at: \(editedVideoPath)")
    }

    func videoEditorController(_ editor: UIVideoEditorController, didFailWithError error: Error) {
        editor.dismiss(animated: true, completion: nil)
        print("Video editing failed with error: \(error.localizedDescription)")
    }

    func videoEditorControllerDidCancel(_ editor: UIVideoEditorController) {
        editor.dismiss(animated: true, completion: nil)
        print("Video editing canceled.")
    }
}

PHContentEditingController

https://developer.apple.com/documentation/photokit/phcontenteditingcontroller?language=objc_2
https://blog.51cto.com/u_13360/7840126

PHContentEditingController 是 iOS 中用于扩展相册(Photos)应用的接口,允许开发者提供自定义的内容编辑扩展。通过实现这个协议,可以将自己的编辑工具集成到 iOS 相册中,用于编辑照片或视频。

核心概念
扩展点 (Extension Point): PHContentEditingController 被设计为用于 iOS 相册应用的扩展点,允许第三方应用提供自定义的照片或视频编辑功能。
输入 (Input): 相册中的照片或视频作为输入内容传递给 PHContentEditingController。
输出 (Output): 编辑后的内容将以新的形式保存,供用户使用。
实现步骤
创建内容编辑扩展 (Content Editing Extension):
在 Xcode 中创建一个新的目标(Target),选择 "Photo Editing Extension"。这会生成一个包含 PHContentEditingController 协议的模板文件。

实现 PHContentEditingController 方法:
你需要实现以下几个核心方法:

canHandle(_:):判断扩展是否能处理给定的输入内容类型。
startContentEditing(with:placeholderImage:):在用户选择内容后,准备编辑界面。
finishContentEditing(completionHandler:):用户完成编辑后,处理并返回编辑结果。
cancelContentEditing():用户取消编辑时,撤销所有操作。
shouldShowCancelConfirmation():返回一个布尔值,决定是否显示取消编辑的确认提示。
示例代码:
下面是一个基本的实现示例:

import Photos
import UIKit

class PhotoEditingViewController: UIViewController, PHContentEditingController {
    
    var input: PHContentEditingInput?
    var isEditingVideo = false
    
    func canHandle(_ adjustmentData: PHAdjustmentData?) -> Bool {
        // 检查是否可以处理这个调整数据
        return true
    }
    
    func startContentEditing(with contentEditingInput: PHContentEditingInput, placeholderImage: UIImage) {
        // 保存输入内容
        self.input = contentEditingInput
        
        // 检查输入内容是否是视频
        if let videoURL = contentEditingInput.audiovisualAsset?.url {
            isEditingVideo = true
            // 在这里加载并显示视频
        } else {
            isEditingVideo = false
            // 在这里加载并显示图片
        }
    }
    
    func finishContentEditing(completionHandler: @escaping ((PHContentEditingOutput?) -> Void)) {
        guard let input = input else {
            completionHandler(nil)
            return
        }
        
        // 创建编辑输出对象
        let output = PHContentEditingOutput(contentEditingInput: input)
        
        if isEditingVideo {
            // 视频处理逻辑
            // 保存修改后的视频到 output.renderedContentURL
        } else {
            // 图片处理逻辑
            // 保存修改后的图片到 output.renderedContentURL
        }
        
        // 生成调整数据
        let adjustmentData = PHAdjustmentData(formatIdentifier: "com.yourcompany.appname", formatVersion: "1.0", data: Data())
        output.adjustmentData = adjustmentData
        
        // 调用回调并传递输出结果
        completionHandler(output)
    }
    
    func cancelContentEditing() {
        // 处理用户取消编辑的情况
    }
    
    func shouldShowCancelConfirmation() -> Bool {
        // 返回是否应该显示取消确认提示
        return true
    }
}

关键点说明:
PHContentEditingInput 和 PHContentEditingOutput: 这两个类用于在编辑过程中处理输入和输出内容。
PHAdjustmentData: 用于保存编辑操作的元数据,以便将来恢复或重新应用这些操作。
注意事项
这个扩展是直接与系统相册集成的,用户可以在相册应用中使用你的自定义编辑器。
确保正确处理用户的操作,无论是保存编辑结果还是取消编辑,都要提供流畅的用户体验。
如果你需要进一步了解或实现具体功能,随时告诉我!

上一篇 下一篇

猜你喜欢

热点阅读