📖

[SwiftUI]自作HalfModalの作り方

2021/12/30に公開

今回作成するもの

iOS15からUIKitではUISheetPresentationControllerが追加されたので、標準でハーフモーダルと言われるものを簡単に表現できるようになりました。
しかしSwiftUIでは追加されませんでしたので自作するか、外部ライブラリをしようするかでしか実装できません。今回は自作する方法をご紹介したいと思います。
以下完成イメージです。

ボタンを押すとハーフモーダルが表示され、ドラッグ少し上に引っ張るとモーダル表示されます。
(このあたりのサイズは使用しやすいように適時変更していただければと思います)

環境

・ macOS: Monterey
・ Xcode: 13.2

実装

ハーフモーダルで使用するViewを適当に作ります。

struct HalfModalView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .foregroundColor(.gray)
            VStack {
            RoundedRectangle(cornerRadius: 10)
                .frame(width: 60, height: 5)
                .padding(.top)
                Spacer()
            }
        }
    }
}


先ほど作ったHalfModalViewに対してハーフモーダルのように表示されるようにmodifierを追加していきます。
使用するプロパティ

@State var show = false
@State var showFull = false
@State var position = CGSize.zero
@State var viewSize = UIScreen.main.bounds
@State var higthRate = 0.35

実装するコード

HalfModalView()
            .cornerRadius(20)
            .offset(y:show ? viewSize.height * 0.4 : viewSize.height)
            .offset(y: position.height)
            .animation(.timingCurve(0.2, 0.8, 0.2, 1, duration: 0.9), value: show)
        
            .gesture(
                DragGesture().onChanged { value in
                    position = value.translation
                    if showFull {
                        position.height += -(viewSize.height * higthRate)
                    }
                    if position.height < -(viewSize.height * higthRate) {
                        position.height = -(viewSize.height * higthRate)
                    }
                }
                    .onEnded { value in
                        if position.height > 50 {
                            show = false
                        }
                        if (position.height < -100 && !showFull) || (position.height < -250 && showFull) {
                            position.height = -(viewSize.height * higthRate)
                            showFull = true
                        } else {
                            position = .zero
                            showFull = false
                        }
                    }
            )

後は適当にボタンを作れば完成です。

Button {
                        show.toggle()
                    } label: {
                        Text("half modal")
                    }

まとめ

gestureを使用して、モーダルの高さによってFull表示とHalf表示をする実装をしてみましたが、コードがどうしても冗長的になってしまいます。SwiftUIでも標準で追加される事を期待しています。

Discussion