💡

UIScrollerViewのフルスクリーンショットを取得

2024/04/04に公開

みなさんこんにちは、スペースマーケットでモバイルアプリエンジニアをしている王です。
最近やっと暖かくなってきてジムに行くモチベが復活しました!

入社した頃に、プロジェクトやユーザー行動の理解を深めるためプロダクト分析ツールAmplitudeを利用し、ユーザーがどの画面でスクリーンショットを取るかのイベントを仕込みました。
これでスクショがどれほど撮られるかわかるので、イベント数が多いかつ同じユーザーが何回も発火する予約詳細画面でフルスクリーンショットの取得機能を作りました。

気になる方はぜひ下記からスペースマーケットのアプリをダウンロードして試してみてください〜

目標

  • 特定の画面でスクショを撮ることを検知し、フルスクリーンショットを表示・保存できる

実装の詳細

ざっくりやり方として、UIGraphicsBeginImageContextUIGraphicsEndImageContextで画像を描画するを前提にします。
対象scrollViewのcontentOffsetを.zeroにして、frameをcontentSizeにすると、scrollView全体表示できるようになるので、現在のコンテキストでscrollViewのレイヤーをレンダリングして、そのコンテキストを画像に変換します。
また、ユーザーに混乱させないように、描画の前にcontentOffsetframeを保存し、画像描画処理を終了したタイミングで元の値に戻す必要があります。

ではコードに落としていきます。

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にも適用できないかも挑戦していきたいと思います。最後までお読みいただきありがとうございます。

スペースマーケット Engineer Blog

Discussion