UIScrollerViewのフルスクリーンショットを取得
みなさんこんにちは、スペースマーケットでモバイルアプリエンジニアをしている王です。
最近やっと暖かくなってきてジムに行くモチベが復活しました!
入社した頃に、プロジェクトやユーザー行動の理解を深めるためプロダクト分析ツールAmplitudeを利用し、ユーザーがどの画面でスクリーンショットを取るかのイベントを仕込みました。
これでスクショがどれほど撮られるかわかるので、イベント数が多いかつ同じユーザーが何回も発火する予約詳細画面でフルスクリーンショットの取得機能を作りました。
気になる方はぜひ下記からスペースマーケットのアプリをダウンロードして試してみてください〜
目標
- 特定の画面でスクショを撮ることを検知し、フルスクリーンショットを表示・保存できる
実装の詳細
ざっくりやり方として、UIGraphicsBeginImageContext
とUIGraphicsEndImageContext
で画像を描画するを前提にします。
対象scrollViewのcontentOffsetを.zero
にして、frameをcontentSize
にすると、scrollView全体表示できるようになるので、現在のコンテキストでscrollViewのレイヤーをレンダリングして、そのコンテキストを画像に変換します。
また、ユーザーに混乱させないように、描画の前にcontentOffset
とframe
を保存し、画像描画処理を終了したタイミングで元の値に戻す必要があります。
ではコードに落としていきます。
extension UIScrollView {
func snapshot() -> UIImage? {
UIGraphicsBeginImageContext(contentSize)
let savedContentOffset = contentOffset
let savedContentInset = contentInset
let savedFrame = frame
contentOffset = .zero
contentInset = .zero
layer.frame = .init(origin: .zero, size: contentSize)
if let context = UIGraphicsGetCurrentContext() {
layer.render(in: context)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
contentOffset = savedContentOffset
contentInset = savedContentInset
layer.frame = savedFrame
UIGraphicsEndImageContext()
return image
}
}
できました🎉
振り返り
最初はiOS13から使えるscreenshotServiceで実装しようと思いましたが、当時はPDFのデータしか保存・共有しかできなくて断念しましたが、iOS17からPDFか画像どちらか選べるようになったようです。
Beginning in iOS 17 and iPadOS 17, people have the option to share or save the generated full page screenshot as a PDF or an image.
弊社アプリはiOS17以上サポートするようになったらこちらを使ってみたいと思います。
また、frameとlayer.frameの違いがよく分からず、frameをcontentSizeに設定してしまい、どうしても現在の位置だけ描画するのもハマったところでした(実際画像をレンダリングするのはlayerだからそれはそう)。
今後はSwiftUIのScrollViewやListと、UItableViewにも適用できないかも挑戦していきたいと思います。最後までお読みいただきありがとうございます。
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion