UIHostingControllerを使わずにSwiftUIのViewのスナップショットを撮る
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を簡単に画像に書き出す
まとめ
また一つUIHostingControllerともおさらばだぜ!ふふー!
他にも ImageRenderer
は ObservableObject
だったりするので必要があれば objectWillChange
を監視することもできるみたいです。こちらもドキュメントにもSwiftの定義にも書いてあるので一度使う前はじっくり見てみましょう。
おしまい \(^o^)/
Discussion