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

SwiftUI中如何在保存至CoreData Binary Data前压缩图片尺寸?

问题描述

我有一个CoreData实体“GameSession”,其属性“photo”类型为“Binary Data”(已勾选“允许外部存储”)。目前在视图中通过PhotosPicker选择图片并保存到该属性,代码如下:

PhotosPicker(selection: $selectedItem ,matching: .images) {
    Label("Select a photo", systemImage: "photo")
}
.onChange(of: selectedItem) { newItem in
    Task {
        if let data = try? await newItem?.loadTransferable(type: Data.self) {
            selectedPhotoData = data
        }
    }
}

保存至CoreData的代码如下:

let newSession = GameSession(context: self.moc)
        newSession.date = self.date
        newSession.setValue(self.selectedPhotoData, forKeyPath: "photo")
self.moc.save()

目前功能正常,但我担心大量“session”(即大量图片)会引发内存问题。此外,我的应用仅在300×300像素的小方块中显示图片,无需存储高分辨率版本。

核心疑问:如何在保存前缩小图片尺寸?

我猜测需要对PhotosPicker中的“selectedItem”或“newItem”应用“缩小尺寸”函数,但不知具体实现方法。我知道可以使用UIImageJPEGRepresentation设置压缩比例,但不知道如何将其应用到$selectedItem上。

解决方案

你可以在将图片数据存入CoreData前,先把图片缩放到目标尺寸(300×300),再转换成压缩后的Data。具体步骤如下:

  • 添加图片缩放工具函数
    先写一个专门处理图片缩放的函数,传入原始UIImage和目标尺寸,返回缩放后的UIImage:
func resizeImage(_ image: UIImage, targetSize: CGSize) -> UIImage? {
    let size = image.size
    
    let widthRatio  = targetSize.width  / size.width
    let heightRatio = targetSize.height / size.height
    
    // 取较小的比例保证图片完整显示,避免拉伸变形
    let scaleRatio = min(widthRatio, heightRatio)
    
    let newSize = CGSize(width: size.width * scaleRatio, height: size.height * scaleRatio)
    
    UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
    image.draw(in: CGRect(origin: .zero, size: newSize))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    return newImage
}
  • 修改图片加载与处理逻辑
    在onChange回调中,先把加载到的Data转换成UIImage,再调用缩放函数处理,最后转换成压缩后的Data:
.onChange(of: selectedItem) { newItem in
    Task {
        guard let data = try? await newItem?.loadTransferable(type: Data.self),
              let originalImage = UIImage(data: data) else {
            selectedPhotoData = nil
            return
        }
        // 缩放到目标尺寸300×300
        let targetSize = CGSize(width: 300, height: 300)
        guard let resizedImage = resizeImage(originalImage, targetSize: targetSize) else {
            selectedPhotoData = nil
            return
        }
        // 转换成JPEG格式,压缩比例可自行调整(范围0.0-1.0,值越小压缩率越高)
        selectedPhotoData = resizedImage.jpegData(compressionQuality: 0.8)
    }
}
  • 保存逻辑无需修改
    原来的CoreData保存代码可以直接复用,因为selectedPhotoData已经是处理后的压缩小图数据了。

额外优化建议

  • 处理完图片后可以手动将原始UIImage实例置为nil,减少内存占用
  • JPEG压缩质量设为0.8在画质和文件大小之间比较均衡,可根据实际需求调整
  • 如果需要保留透明通道,也可以用resizedImage.pngData(),但JPEG的压缩率更高,更适合节省存储空间

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 07:35:22