hello. I'm creating an app that shows a list of items on the main screen, and images and details of the selected item in a DetailView.
In order to minimize the memory usage of the image, I am downsampling the size value of the GeometryReader's closure parameter in the .task modifier of the DetailView, but the first time I get the size value, the width and height values are all 0, so I can't see the image.
When I debugged it, I saw that I was getting the first 0 value and then the correct value, so I confirmed that the image is visible through the .task(id: size) modifier, but I'm not sure if this is the correct way to do it.
I'd appreciate it if you could tell me why the size.width, size.height values I'm getting at first are all 0 and if what I've done is the right way to do it, and should I always downsample the image?
struct ContextView: View {
@State private var seletedItem: Item?
var body: some View {
MainListView()
.sheet(item: $seletedItem) { item in
GeometryProxy { proxy in
let size = proxy.size // 👈
DetailImageView(item: item, size: size)
.ignoresSafeArea(.container, edges: .top)
}
}
}
}
struct DetailImageView: View {
var item: Item
var size: CGSize
@State private var uiImage: UIImage? = nil
var body: some View {
ScrollView {
GeometryReader { proxy in
let childSize = proxy.size
if let uiImage {
Image(uiImage: uiImage)
.resizable()
.scaledToFill()
.frame(width: childSize.width, height: childSize.height)
}
}
.frame(height: size.height)
}
.coordinateSpace(name: "scroll")
.task {
// ❌ not show Image, not working 👈
// size.width = 0, size.height = 0
uiImage = downsampleImage(imageData: item.imageData,
to: CGSize(width: size.width, height: size.height))
}
.task(id: size) {
// ✅ show Image, working 👈
uiImage = downsampleImage(imageData: item.imageData,
to: CGSize(width: size.width, height: size.height))
}
}
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)
}
}