如何为SwiftUI中的PHPicker添加图片压缩功能?
解决PHPicker选图后图片模糊的压缩方案
你遇到的问题是因为选取的原图分辨率远高于显示它的Image视图尺寸,直接使用会导致系统缩放时出现模糊。可以通过缩放图片到合适尺寸或者结合文件压缩来解决,下面是具体实现:
方法1:按目标尺寸缩放图片(推荐)
先给UIImage添加一个扩展方法,用来按比例缩放图片到指定的最大边长,保持原图比例:
extension UIImage { func scaledToMaxSideLength(_ maxLength: CGFloat) -> UIImage? { let width = self.size.width let height = self.size.height // 计算缩放比例,保证图片最长边不超过设定值 var scaleFactor: CGFloat = 1.0 if width > maxLength || height > maxLength { scaleFactor = maxLength / max(width, height) } let newWidth = width * scaleFactor let newHeight = height * scaleFactor // 以屏幕分辨率为基准生成新图,避免模糊 UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, UIScreen.main.scale) self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return scaledImage } }
然后修改Coordinator中的图片处理逻辑,在拿到原图后调用这个方法:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) guard let provider = results.first?.itemProvider else { return } if provider.canLoadObject(ofClass: UIImage.self) { provider.loadObject(ofClass: UIImage.self) { image, _ in guard let originalImage = image as? UIImage else { return } // 缩放图片到最大边长500px,可根据你的视图尺寸调整这个值 self.parent.image = originalImage.scaledToMaxSideLength(500) } } }
方法2:同时压缩文件大小(配合缩放使用)
如果需要进一步减小图片的文件体积,可以在缩放后再用jpegData进行压缩:
// 替换缩放后的赋值代码为以下内容 if let scaledImage = originalImage.scaledToMaxSideLength(500), let compressedData = scaledImage.jpegData(compressionQuality: 0.8), // 0.0-1.0,值越小压缩率越高 let compressedImage = UIImage(data: compressedData) { self.parent.image = compressedImage }
完整修改后的代码
import PhotosUI import SwiftUI extension UIImage { func scaledToMaxSideLength(_ maxLength: CGFloat) -> UIImage? { let width = self.size.width let height = self.size.height var scaleFactor: CGFloat = 1.0 if width > maxLength || height > maxLength { scaleFactor = maxLength / max(width, height) } let newWidth = width * scaleFactor let newHeight = height * scaleFactor UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: newHeight), false, UIScreen.main.scale) self.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight)) let scaledImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return scaledImage } } struct ImagePicker: UIViewControllerRepresentable { @Binding var image: UIImage? func makeUIViewController(context: Context) -> PHPickerViewController { var config = PHPickerConfiguration() config.filter = .images let picker = PHPickerViewController(configuration: config) picker.delegate = context.coordinator return picker } func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, PHPickerViewControllerDelegate { let parent: ImagePicker init(_ parent: ImagePicker) { self.parent = parent } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) guard let provider = results.first?.itemProvider else { return } if provider.canLoadObject(ofClass: UIImage.self) { provider.loadObject(ofClass: UIImage.self) { image, _ in guard let originalImage = image as? UIImage else { return } // 基础缩放方案 self.parent.image = originalImage.scaledToMaxSideLength(500) // 可选:缩放+文件压缩方案,注释上面一行后启用下面代码 /* if let scaledImage = originalImage.scaledToMaxSideLength(500), let compressedData = scaledImage.jpegData(compressionQuality: 0.8), let compressedImage = UIImage(data: compressedData) { self.parent.image = compressedImage } */ } } } } }
说明:
- 可以根据实际显示的
Image视图大小调整scaledToMaxSideLength的参数值,比如视图是200x200,就设置最大边长为200。 - 使用
UIScreen.main.scale确保缩放后的图片在不同分辨率屏幕上显示清晰。
内容的提问来源于stack exchange,提问作者phiredrop
相关产品推荐
相关产品推荐

