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

如何为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 08:57:14