Reading time: 1 min

This recipe shows how to insert an Image into SwiftUI Text, so that the resulting view is still a Text instance.

The end result looks like this:


This solution works for SwiftUI 2+ (iOS 14+, macOS 11+).

Text has an initializer that takes an Image and converts it into a Text, which can then be concatenated with other Texts into a single Text:

(Text("This image")
  + Text(Image("icon")) // HERE
  + Text("is our logo."))

This all works fine until you need to resize the image. Using the frame modifier results in a View, which then can't be passed into a Text initializer:

(Text("This image")
  + Text(Image("icon").resizable().frame(width: 32, height: 32))
  + Text("is our logo."))

The trick to make this work is to use the method from this recipe, which converts any View into an UIImage. Here's a quick extension that captures the functionality of resizing an Image while also returning an Image:

extension Image {
  func resizedAsImage(width: CGFloat? = nil,
                      height: CGFloat? = nil) -> Image {
    Image(uiImage: self
      .frame(width: width, height: height)
      .snapshot) // check the other recipe for this extension

Then, you can insert a resized image into any Text:

(Text("This image")
  + Text(Image("icon").resizedAsImage(width: 96, height: 96))
  + Text("is our logo."))
What doesn't work - using AttributedString

Some of you may know that in UIKit, it's possible to insert an image into a Label using NSAttributedString that wraps an NSTextAttachment, like this:

let imageAttachment = NSTextAttachment(image: UIImage(named: "icon")!)
let imageString = NSAttributedString(attachment: imageAttachment)

Then, theoretically, you could convert NSAttributedString into an AttributedString which you can then plug into a Text:

let attrStr = try! AttributedString(myString, including: \.uiKit)

However, this doesn't work and the image isn't shown. The issue isn't explicitly documented or explained anywhere.

Next Post Previous Post