swiftuiswiftui-view

How do I stop SwiftUI's .resizable() adding a space when an assets catalog image doesn't exist?


I am building a rather long quiz app. Some questions have images and others don't. All the images are locally stored. I want to dynamically show an image if it exists in the local assets catalog. For those questions that do not have an image, I would like the question text to occupy the space on the screen where the image would be. Whenever I add .resizable() to the image, I get an ugly space even when SwiftUI finds no image.

Here's what I'm getting

Here's what I want

import SwiftUI

struct Question {
    var text: String
}

struct ContentView: View {
    @State var questionNumber = 0
    
    var questions = [
        Question(text: "Question 0 has an image in the assets folder"),
        Question(text: "No image for Question 1 in the assets folder"),
    ]
    
    var body: some View {
        VStack {
            VStack {

                //I have an image named 0 in the assets folder

                Image("\(questionNumber)").resizable().scaledToFit()
                Text("\(questions[questionNumber].text)")
            }
            .padding()
            
            Spacer()
            
            HStack {
                Button {
                    questionNumber = 0
                } label: {
                    Text("Question 0")
                }
                
                Button {
                    questionNumber = 1
                } label: {
                    Text("Question 1")
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Solution

  • You can use UIImage or NSImage (macOS) to check if the image exists. If UIImage(named:) returns nil, the image doesn't exist.

    extension View {
        func isImageExist(_ imageName: String) -> Bool {
            #if os(macOS)
            return (NSImage(named: imageName) != nil)
            #elseif os(iOS)
            return (UIImage(named: imageName) != nil)
            #endif
        }
    }
    

    Use:

    VStack {
        if isImageExist("\(questionNumber)") {
            Image("\(questionNumber)").resizable().scaledToFit()
        }
        Text("Hello, world!")
    }
    .padding()