BLACK FRIDAY SALE: Save 50% on all my Swift books and bundles! >>

Why does the first size of the GeometryReader's closure parameter in the .task modifier have a value of 0 and how can I fix it?

Forums > SwiftUI

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 {
            .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)
                        .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)


Hacking with Swift is sponsored by Guardsquare

SPONSORED AppSweep by Guardsquare helps developers automate the mobile app security testing process with fast, free scans. By using AppSweep’s actionable recommendations, developers can improve the security posture of their apps in accordance with security standards like OWASP.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.