Bottom sheet can now be dragged vertically. I want to close the bottom sheet when the bottom sheet reaches near the bottom of the iphone screen while dragging.
It seems that value
(DragGesture.Value
), which can be received by onChanged
and onEnded
, has a different position that is not the screen position.
I think there are two solutions.
If you know the drag position based on the screen size, write the close process using it. (I don't know how to get the drag position based on the screen size.)
value.location
will tell you the distance moved by dragging, so close the bottom sheet when the distance is near to the content size (height) of the bottomsheet. (I don't know how to get the height of the content size and use it inonChanged
andonEnded
)
If my idea is wrong or you have other ideas, please let me know.
Code:
import SwiftUI
struct ContentView: View {
@State private var isShow = false
var body: some View {
ZStack {
Button(
"Show Sheet",
action: {
self.isShow.toggle()
}
)
.zIndex(0)
.allowsHitTesting(!isShow)
BottomSheet(
isShow: self.$isShow,
content: {
VStack {
Text("A")
Text("B")
Text("C")
Text("A")
Text("B")
Text("C")
Text("A")
Text("B")
Text("C")
}
.frame(
maxWidth: .infinity
)
.background(Color(.yellow))
}
)
.zIndex(1)
}
}
}
struct ScrimView: View {
var body: some View {
VStack {}.frame(
maxWidth: .infinity,
maxHeight: .infinity,
alignment: .bottom
)
.background(
Color(.gray)
)
.opacity(0.5)
}
}
struct BottomSheet<Content: View>: View {
private let content: () -> Content
@Binding var isShow: Bool
@State var offsetY: CGFloat = 0
init(
isShow: Binding<Bool>,
content: @escaping () -> Content
) {
self._isShow = isShow
self.content = content
}
var body: some View {
ZStack(alignment: .bottom) {
if self.isShow {
ScrimView().zIndex(
0
)
.onTapGesture {
self.isShow = false
}
.transition(.opacity)
VStack {
Button(
"X",
action: {
self.isShow = false
}
)
self.content()
}
.zIndex(1)
.background(Color(.white))
.cornerRadius(10)
.offset(y: self.offsetY)
.gesture(self.gesture)
.transition(.move(edge: .bottom))
}
}
.animation(.default)
.onChange(
of: self.isShow,
perform: { value in
self.offsetY = 0
}
)
}
var gesture: some Gesture {
DragGesture().onChanged{ value in
guard value.translation.height > 0 else { return }
self.offsetY = value.translation.height
// I want to close the bottom sheet when the bottom sheet reaches near the bottom of the screen by gesture
}
.onEnded{ value in
if value.translation.height > 0 {
withAnimation {
self.offsetY = 0
return
}
} else {
return
}
}
}
}
I just need to determine the vertical threshold for the close, but since this bottom sheet gets its content from the outside, this threshold needs to be determined dynamically from the height of the content.
If I can't get the height of the content, then I need to calculate the close position by the position of the bottom of the screen and the gesture position.