1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【SwiftUIのみ】iOS15以下で高さが動的or静的なsheetを表示する

Last updated at Posted at 2024-06-08

iOS16以降であればAPIが存在する

iOS16以降であれば以下の記事のように自分好みな高さでsheetを表示できますよね。

ただ、iOS15以下だと使えない…
これをSwiftUIのみで解決します!

動作確認

Simulator Screen Recording - iPhone 15 Pro Max - 2024-06-08 at 22.33.35.gif

こんな感じです。
私の場合は、画面の3/1くらいでsheetを表示しました。

以下の動作でシートを閉じられるようにしています。

  • シートの表示領域外をタップ
  • シートを下にドラッグ

SwftUIコード(コピペでOK)

以下が、sheetを開閉指示する画面

struct ContentView: View {
    @State private var isShowingCustomSheet = false

    var body: some View {
        GeometryReader { geometry in
            let height = geometry.size.height
            ZStack {
                Button {
                    self.isShowingCustomSheet.toggle()
                } label: {
                    Text("シートを表示する")
                }
                CustomSheet(isShowing: $isShowingCustomSheet, height: height)
            }
        }
    }
}

以下が、iOS15以下でも高さが動的or静的に表示できるsheet


struct CustomSheet: View {
    let height: CGFloat
    @Binding var isShowing: Bool
    @GestureState private var dragState: CGSize = .zero
    @State private var offsetY: CGFloat = 0

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .bottom) {
                if isShowing {
                    Color.black
                        .opacity(0.3)
                        .ignoresSafeArea()
                        .onTapGesture {
                            withAnimation {
                                isShowing = false
                            }
                        }

                    VStack {
                        Text("カスタムシートが表示された")
                    }
                    .frame(maxWidth: .infinity)
                    .frame(maxHeight: height / 4)
                    .background(Color.white)
                    .cornerRadius(16, corners: [.topLeft, .topRight])
                    .offset(y: offsetY + (dragState.height > 0 ? dragState.height : 0))
                    .gesture(
                        DragGesture()
                            .updating($dragState) { value, state, _ in
                                if value.translation.height > 0 {
                                    state = value.translation
                                }
                            }
                            .onEnded { value in
                                if value.translation.height > 100 {
                                    isShowing = false
                                } else {
                                    offsetY = 0
                                }
                            }
                    )
                    .transition(.opacity.combined(with: .move(edge: .bottom)))
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .ignoresSafeArea()
            .animation(.easeInOut, value: isShowing)
        }
        .edgesIgnoringSafeArea(.all)
    }
}

private struct RoundedCorner: Shape {
    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        return Path(path.cgPath)
    }
}

private extension View {
    func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
        clipShape( RoundedCorner(radius: radius, corners: corners) )
    }
}
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?