使用SwiftUI对图像进行降采样

3

我正在我的应用程序中显示从网络下载的图像,但我希望对它们进行降采样,以便它们不占用多个MB的内存。我以前可以使用UIKit很容易地完成这个任务:

func resizedImage(image: UIImage, for size: CGSize) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    return renderer.image { (context) in
        image.draw(in: CGRect(origin: .zero, size: size))
    }
}

还有其他方法,但它们都依赖于了解 SwiftUI 图像视图所需的大小,这在 SwiftUI 中并不容易。

是否有专门用于降采样 SwiftUI 图像的良好 API/方法?


你对视图的期望大小是多少? - Asperi
我不确定,我猜我可以使用GeometryReader?如果你的意思是我想要按实际渲染图像的大小进行降采样。 - bze12
然后,您可以像之前一样使用相同的函数,并使用Image(uiImage: ...)来创建图像。 - Asperi
你可以使用CIFilter来按比例而不是cgsize进行降采样。 - Won
3个回答

2

使用此函数进行调整大小。它在列表或LazyVStack中的工作速度很快,并减少图像的内存消耗。

public var body: some View {
    GeometryReader { proxy in
        let image = UIImage(named: imageName)?
            .resize(height: proxy.size.height)
        Image(uiImage: image ?? UIImage())
            .resizable()
            .scaledToFill()
    }
}

public extension UIImage {
    
    /// Resizes the image by keeping the aspect ratio
    func resize(height: CGFloat) -> UIImage {
        let scale = height / self.size.height
        let width = self.size.width * scale
        let newSize = CGSize(width: width, height: height)
        let renderer = UIGraphicsImageRenderer(size: newSize)
        
        return renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}

1

我最终使用了 geometry reader 来解决它,这不是最理想的解决方案,因为它会使布局有些混乱。

@State var image: UIImage
var body: some View {
   GeometryReader { geo in
       Image(uiImage: self.image)
           .resizable()
           .aspectRatio(contentMode: .fit)
           .onAppear {
               let imageFrame = CGRect(x: 0, y: 0, width: geo.size.width, height: geo.size.height)
               self.downsize(frame: imageFrame) // call whatever downsizing function you want
           }
      }
}

使用几何代理来确定图像的框架,然后缩小到该框架。我希望SwiftUI有自己的API来做到这一点。

-1

这个方法使用CIFilter来缩小UIImage。

https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CILanczosScaleTransform

public extension UIImage {
    func downsampled(by reductionAmount: Float) -> UIImage? {

        let image = UIKit.CIImage(image: self)
        guard let lanczosFilter = CIFilter(name: "CILanczosScaleTransform") else { return nil }
        lanczosFilter.setValue(image, forKey: kCIInputImageKey)
        lanczosFilter.setValue(NSNumber.init(value: reductionAmount), forKey: kCIInputScaleKey)

        guard let outputImage = lanczosFilter.outputImage else { return nil }
        let context = CIContext(options: [CIContextOption.useSoftwareRenderer: false])
        guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil}
        let scaledImage = UIImage(cgImage: cgImage)

        return scaledImage
    }
}

然后你可以在SwiftUI视图中使用

struct ContentView: View {
    var body: some View {
        if let uiImage = UIImage(named: "sample")?.downsampled(by: 0.3) {
            Image(uiImage: uiImage)
        }
    }
}


我怎么知道要将reductionAmount设置为多少?通常,UIKit的降采样技术依赖于知道要显示的图像的imageView框架,并进行降采样以适应该框架。我的问题是,SwiftUI没有带有框架属性的ImageViews。 - bze12
@bze12 当你知道要缩小的比例时,可以使用这个方法。1.0 是默认值,你可以设置小于 1.0 的值来进行缩小。 - Won
是的,我知道,但我的观点是我不知道要缩小多少比例。 - bze12
这个过滤器不起作用。当我使用0.5的因子时,我的内存使用量从120 MB几乎增加到197 MB,并且速度非常慢(好吧,这可能可以通过任何缓存来解决)。 - Hans Bondoka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接