🎨

UIHostingControllerを使わずにSwiftUIのViewのスナップショットを撮る

2023/01/10に公開

iOS15まではSwiftUIのViewのスナップショットを撮る場合は、UIHostingControllerに一度入れてから行う必要がありましたが、iOS16からImageRendererというものが提供されていることを発見しました。この記事ではその使い方を2つほど紹介いたします。

公式ドキュメント: https://developer.apple.com/documentation/swiftui/imagerenderer

サンプル

下記は公式ドキュメントのサンプルを少し編集してすぐに実行できるようにしたものです。

struct Trophy: View {
  let playerName = "bannzai"
  let achievementDate = Date.now
  @State var image: UIImage?

  var body: some View {
    let trophyAndDate = createAwardView(forUser: playerName,
                                        date: achievementDate)
    VStack {
      trophyAndDate
      if let image {
        Image(uiImage: image)
      }
      Button("Duplicate") {
        let renderer = ImageRenderer(content: trophyAndDate)
        if let image = renderer.uiImage {
          self.image = image
        }
      }
    }
  }

  private func createAwardView(forUser: String, date: Date) -> some View {
    VStack {
      Image(systemName: "trophy")
        .resizable()
        .frame(width: 200, height: 200)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .shadow(color: .mint, radius: 5)
      Text(playerName)
        .font(.largeTitle)
      Text(achievementDate.formatted())
    }
    .multilineTextAlignment(.center)
    .frame(width: 200, height: 290)
  }
}

struct Trophy_Previews: PreviewProvider {
  static var previews: some View {
    Trophy()
  }
}

最初はトロフィーが一つです。

Duplicate を押すと ImageRenderer の処理が走り、同じ見た目のトロフィーが二つになります。

補足

さきほど示した例ではこの記事のメインテーマであるSwiftUIのViewのスナップショットが撮れました。↑ではUIImageでしたが、CGImageとして取得もできるのでmacOS用にもアプリを提供している人はそちらを使った方が共通化できそうですね。

render(rasterizationScale:renderer:)

もう一つrender(rasterizationScale:renderer:)というメソッドがあります。こちらは好きなCGContextを使用して画像を作成することが可能になります。

公式ドキュメント: https://developer.apple.com/documentation/swiftui/imagerenderer/render(rasterizationscale:renderer:)

こちらも使用例を提示したかったのですが、実はもともとPicture in Pictureを使用したアプリを作っていた時にアプリがバックグラウンドに言った場合に最初に説明した imageRenderer.uiImage だと nil が返却されて困っていた時に Realityさんが書いてくれた記事を見て解決した背景があります。記事に具体例や解説も載っていますのでここではこの記事の紹介にとどめようと思います。この関数が気になる方は下記のnoteをご参考ください。

Now in REALITY Tech #52 iOS 16の新機能「ImageRenderer」でSwiftUIのViewを簡単に画像に書き出す
https://note.com/reality_eng/n/n662347337553

まとめ

また一つUIHostingControllerともおさらばだぜ!ふふー!

他にも ImageRendererObservableObject だったりするので必要があれば objectWillChange を監視することもできるみたいです。こちらもドキュメントにもSwiftの定義にも書いてあるので一度使う前はじっくり見てみましょう。

おしまい \(^o^)/

Discussion