LoginSignup
12
1

More than 1 year has passed since last update.

スクロールで伸縮するヘッダをSwiftUIで実装する

Last updated at Posted at 2022-12-10

はじめに

この記事はand factory.inc Advent Calendar 2022 11日目の記事です。

and factory iOSエンジニアのy-okuderaです!
SwiftUIで、以下のように引っ張ると伸縮するヘッダを実装しましたので、まとめてみます。

result.gif

StatusBarまで画像を描画する

まずは、画像をStatusBarまで描画してみます。
ScrollViewに .edgesIgnoringSafeArea(.top) を指定して、StatusBarまで描画されるようにします。

ScrollViewのサイズ・位置を取得するために、GeometryReaderを使用して、その中にImageを実装します。
Imageのサイズは、GeometryReaderと同じに設定して、GeometryReaderには任意の高さを設定します。
GeometryReaderに指定する高さは、使用する画像のサイズによって変更する必要があるので、注意が必要です:point_up_2:

ContentView.swift
import SwiftUI

struct ContentView: View {

    var body: some View {
        ScrollView {
            GeometryReader { geometryProxy in
                Image("header") // Assetsに追加した画像名
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geometryProxy.size.width, height: geometryProxy.size.height)
            }
            .frame(height: 350)
        }
        .edgesIgnoringSafeArea(.top)
    }
}

画面の上端から画像を描画することができました。この時点では、引っ張っても伸縮されません。

result.gif

ヘッダを伸縮させる

スクロール位置が上端より上かどうか判定する

ヘッダを伸縮させるために、スクロール位置が初期位置より上かどうか判定できるようにします。
GeometryReaderのminYを使用します。GeometryReaderのminYは初期位置だと0で、上にスクロールするとプラス、下にスクロールするとマイナスの値になります。

スクロール位置 minYの値
初期位置 GeometryReaderのminY == 0
初期位置より上 GeometryReaderのminY > 0
初期位置より下 GeometryReaderのminY < 0

画面全体を基準としてScrollViewの上端の位置を取得したいので、globalの座標空間を指定してframeを取得し、そのminYを使用します。

/// スクロール位置が初期位置より上かどうか
private func isAboveTop(geometryProxy: GeometryProxy) -> Bool {
    return geometryProxy.frame(in: .global).minY > 0
}

スクロール位置が初期位置より上のとき、画像を伸縮させる

条件判定ができるようになったので、画像を伸縮させます。
スクロール位置が初期位置より上のとき、初期位置との差分だけ画像の高さを高くして、その分だけ画像の位置を調整します。

import SwiftUI

struct ContentView: View {

    private func isAboveTop(geometryProxy: GeometryProxy) -> Bool {
        return geometryProxy.frame(in: .global).minY > 0
    }

    var body: some View {
        ScrollView {
            GeometryReader { geometryProxy in
                ZStack {
                    if isAboveTop(geometryProxy: geometryProxy) {
                        Image("header")
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: geometryProxy.size.width, height: geometryProxy.size.height + geometryProxy.frame(in: .global).minY) // 初期位置からの差分だけ高さを追加
                            .clipped()
                            .offset(y: -geometryProxy.frame(in: .global).minY) // 高さを追加した分位置を調整
                    } else {
                        Image("header")
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: geometryProxy.size.width, height: geometryProxy.size.height)
                    }
                }
            }
            .frame(height: 350)
        }
        .edgesIgnoringSafeArea(.top)
    }
}

スクロールに応じて、画像を伸縮できました。
result.gif

おまけ

ヘッダだけでは寂しいので、クリスマスっぽいフリー素材をたくさん集めてきて、Pinterest風に並べてみました。

result.gif

SwiftUIMasonryというPackageを使用すると、簡単に実装できますので、詳細は割愛します:pray:

おわりに

今回は、SwiftUIで、スクロールでストレッチするヘッダを実装してみました。
サンプルは以下のリポジトリにpushしてあります。

最後まで見ていただいてありがとうございました。
明日のAdvent Calendarの記事もお楽しみに:santa:

12
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
1