Search code examples
swiftuiuiimage

Why does putting an SF symbol in a UIImage and then in a SwiftUI Image View result in a blurry image?


I'm trying to write a public init that takes a UIImage, but I want to allow both a normal, actual UIImage made from say a photo (or otherwise drawn) OR use the SF Symbols, but I am stumped as to why the SF Symbols are blurred. What am I missing?

Here is the code I've got:

struct TempUIImageView: View {

    var defaultImage: UIImage = UIImage(systemName: "globe")!

    var defaultImageString: String = "globe"

    var body: some View {
        VStack  {
            Image(uiImage: defaultImage)
                .resizable()
                .scaledToFill()
                .aspectRatio(contentMode: .fit)
            Image(systemName: defaultImageString)
                .resizable()
                .scaledToFill()
                .aspectRatio(contentMode: .fit)
        }
        .padding()
    }
}

i'm hoping to use a solution in this:

@State private var displayedImage: UIImage?

...

private var displayImage: some View {
    Image(uiImage: displayedImage!)
        .resizable()
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        .scaledToFill()
        .aspectRatio(contentMode: .fit)
        .clipShape(Circle())
        .shadow(radius: 4)
}

But displayedImage could be a UIImage OR an SF Symbol.

(The forced unwrap is dealt with in other code, but this is preliminary code anyways)


Solution

  • When you use Image(systemName:) the system draws to the screen using vector graphics, resulting in sharp, un-blurry renderings.

    However, UIImage, including, it seems UIImage(systemName:) is a bitmapped image, at a set resolution. If you scale it up (as you're doing in your second block of code), you'll get blurriness, because you're not getting the benefits of the vector graphics.

    Using a pattern like the following would allow you to conditionally display a Image(uiImage:) or Image(systemName:) and still use the same set of modifiers for each:

    struct ContentView: View {
        @State var displayedImage : UIImage?
        
        var imageToDisplay: Image {
            if let displayedImage = displayedImage {
                return Image(uiImage: displayedImage)
            } else {
                return Image(systemName: "camera.circle")
            }
        }
        
        var body: some View {
            imageToDisplay
                .resizable()
                .scaledToFill()
                .aspectRatio(contentMode: .fit)
                .frame(width: 400, height: 400)
        }
    }