0

I need to downsample the image stored in CoreData to show the image at a specific size.

Currently, I've implemented it synchronously, but the image animation is not smooth when the sheetView comes up from the bottom.

Should I animate it, or is downsampling such a large task that I should do it asynchronously? I'm not sure when to do it synchronously and when to do it asynchronously, please help.

import SwiftUI

struct DetailView: View {
    @Environment(\.managedObjectContext) private var moc
    @ObservedObject var item: ItemEntity
    @State private var uiImage: UIImage? = nil
    
    @Environment(\.dismiss) private var dismiss
    
    var safeArea: EdgeInsets
    var size: CGSize
    private let spacing: CGFloat = 3
    
    var body: some View {
        ScrollView(showsIndicators: false) {
            VStack(spacing: 2) {
                imageHeaderView
            }
        }
        .coordinateSpace(name: "scroll")
        .task(id: size) {
            guard size != .zero else { return }
            loadImage(width: size.width, height: size.height * 0.56) // 
        }
        .onChange(of: item.unwrappedImageData) { newImageData in
            updateImage(data: newImageData, width: size.width, height: size.height * 0.56)
        }
        .onDisappear {
            uiImage = nil
        }
    }
    
    @ViewBuilder
    private var imageHeaderView: some View {
        let imageHeight = size.height * 0.56
        
        GeometryReader { proxy in
            let size = proxy.size
            let minY = proxy.frame(in: .named("scroll")).minY
            let opacityProgress = (minY / imageHeight) * 1.52
            
            Group {
                if !item.unwrappedImageData.isEmpty, let image = uiImage {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(width: size.width, height: size.height + (minY > 0 ? minY : 0), alignment: .top)
                        .clipped()
                } else {
                    Image(systemName: "tshirt.fill")
                        .font(.largeTitle)
                        .imageScale(.large)
                        .fontWeight(.regular)
                        .foregroundColor(.secondary.opacity(0.55))
                        .frame(width: size.width, height: size.height + (minY > 0 ? minY : 0))
                        .background(Color.customSecondBackgroundColor)
                }
            }
    }
    
    private func loadImage(width: CGFloat, height: CGFloat) {
        guard !item.unwrappedImageData.isEmpty else { return }
// 
        uiImage = downsampleImage(imageData: item.unwrappedImageData,
                                  to: CGSize(width: width, height: height))
    }
    
    private func updateImage(data: Data, width: CGFloat, height: CGFloat) {
        uiImage = !data.isEmpty ? downsampleImage(imageData: data, to: CGSize(width: width, height: height)) : nil
    }
    
    private func downsampleImage(imageData: Data, to pointSize: CGSize, scale: CGFloat = UIScreen.main.scale) -> UIImage {
        let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
        guard let imageSource = CGImageSourceCreateWithData(imageData as CFData, imageSourceOptions) else { return UIImage() }
        
        let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
        let downsampleOptions = [
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            kCGImageSourceShouldCacheImmediately: true,
            kCGImageSourceCreateThumbnailWithTransform: true,
            kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
        ] as CFDictionary
        
        guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else { return UIImage() }
        return UIImage(cgImage: downsampledImage)
    }
}
idhun90
  • 19
  • 3

0 Answers0