はじめに
この記事はand factory.inc Advent Calendar 2022 11日目の記事です。
and factory iOSエンジニアのy-okuderaです!
SwiftUIで、以下のように引っ張ると伸縮するヘッダを実装しましたので、まとめてみます。
StatusBarまで画像を描画する
まずは、画像をStatusBarまで描画してみます。
ScrollViewに .edgesIgnoringSafeArea(.top)
を指定して、StatusBarまで描画されるようにします。
ScrollViewのサイズ・位置を取得するために、GeometryReaderを使用して、その中にImageを実装します。
Imageのサイズは、GeometryReaderと同じに設定して、GeometryReaderには任意の高さを設定します。
GeometryReaderに指定する高さは、使用する画像のサイズによって変更する必要があるので、注意が必要です
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)
}
}
画面の上端から画像を描画することができました。この時点では、引っ張っても伸縮されません。
ヘッダを伸縮させる
スクロール位置が上端より上かどうか判定する
ヘッダを伸縮させるために、スクロール位置が初期位置より上かどうか判定できるようにします。
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)
}
}
おまけ
ヘッダだけでは寂しいので、クリスマスっぽいフリー素材をたくさん集めてきて、Pinterest風に並べてみました。
SwiftUIMasonryというPackageを使用すると、簡単に実装できますので、詳細は割愛します
おわりに
今回は、SwiftUIで、スクロールでストレッチするヘッダを実装してみました。
サンプルは以下のリポジトリにpushしてあります。
最後まで見ていただいてありがとうございました。
明日のAdvent Calendarの記事もお楽しみに