4
2

【SwiftUI】StretchableなUIを作成する

Last updated at Posted at 2023-12-09

Stretchableとは

そもそもStretchableなUIとはなんでしょうか。
Stretch + able つまり「伸縮可能なUI」を指します。
ScrollViewの開始位置から上にスクロールしたときに、Headerがビヨーンと伸びるあれです。
ちなみに、Stretchableという言い方以外に「StretchyView」という呼び方もあるそうです。

その作り方を説明していきます!

動作環境

  • MacOS Sonoma 14.1.2
  • Xcode 15.0.1
  • Swift 5.9

作り方

作り方としては、ScrolViewの中にGeometryReaderを入れて、画像の高さとHeaderのY軸のOffsetを計算すればできそうです。

まずは下記のような関数を作成します。

  func stretchableHeaderImage(image: Image) -> some View {
     GeometryReader { proxy in
        image
          .resizable()
          .aspectRatio(contentMode: .fill)
          .frame(
             width: proxy.size.width,
             height: getHeightForHeaderImage(proxy),
             alignment: .center
          )
          .clipped()
          .offset(y: getOffsetForHeaderImage(proxy))
       }
       .scaledToFill()
    }

    func getHeightForHeaderImage(_ proxy: GeometryProxy) -> CGFloat {
        let offset = proxy.frame(in: .global).minY
        let imageHeight = proxy.size.height
        return offset > 0 ? imageHeight + offset : imageHeight
    }

    func getOffsetForHeaderImage(_ proxy: GeometryProxy) -> CGFloat {
        let offset = proxy.frame(in: .global).minY
        return offset > 0 ? -offset : 0
    }

そしてこれらをScrollViewに入れることで、stretchableなHeaderを持つScrollViewが完成です!

・GeometryReaderの外側に.scaledToFill()
・ScrollViewの外側に.ignoresSafeArea(edges: .top)
が必要です!

コード全体

StretchableHeaderView
import SwiftUI

struct StretchableHeaderView: View {
    var body: some View {
        ScrollView {
            stretchableHeaderImage(image: Image(systemname: "faceid"))

            // ここにListなどのviewを追加します
        }
        .ignoresSafeArea(edges: .top)
    }

    func stretchableHeaderImage(image: Image) -> some View {
       GeometryReader { proxy in
          image
             .resizable()
             .aspectRatio(contentMode: .fill)
             .frame(
                width: proxy.size.width,
                height: getHeightForHeaderImage(proxy),
                alignment: .center
              )
            .clipped()
            .offset(y: getOffsetForHeaderImage(proxy))
        }
        .scaledToFill()
    }

    func getHeightForHeaderImage(_ proxy: GeometryProxy) -> CGFloat {
        let offset = proxy.frame(in: .global).minY
        let imageHeight = proxy.size.height
        return offset > 0 ? imageHeight + offset : imageHeight
    }

    func getOffsetForHeaderImage(_ proxy: GeometryProxy) -> CGFloat {
        let offset = proxy.frame(in: .global).minY
        return offset > 0 ? -offset : 0
    }
}

#Preview {
    StretchableHeaderView()
}

終わりに

ここまでお読み頂き、ありがとうございます!
株式会社ゆめみの24卒アドベントカレンダー9日目の記事でした。

4
2
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
4
2