iOS16以降であればAPIが存在する
iOS16以降であれば以下の記事のように自分好みな高さでsheetを表示できますよね。
ただ、iOS15以下だと使えない…
これをSwiftUIのみで解決します!
動作確認
こんな感じです。
私の場合は、画面の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) )
}
}