#swiftui
Вопрос:
Я создал представление нижнего листа, которое должно исчезнуть, когда пользователь перетаскивает представление вниз, но DragGesture
по какой-то причине не запускается. Кто-нибудь знает, в чем здесь проблема?
struct BottomSheetView<Content: View>: View {
@Binding var isOpen: Bool
let maxHeight: CGFloat
let minHeight: CGFloat
let content: Content
@GestureState private var translation: CGFloat = 0
private var offset: CGFloat {
isOpen ? 0 : maxHeight - minHeight
}
private var indicator: some View {
RoundedRectangle(cornerRadius: SheetConstants.radius)
.fill(Colors.gray)
.frame(
width: SheetConstants.indicatorWidth,
height: SheetConstants.indicatorHeight
).onTapGesture {
self.isOpen.toggle()
}
}
init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self.minHeight = maxHeight * SheetConstants.minHeightRatio
self.maxHeight = maxHeight
self.content = content()
self._isOpen = isOpen
}
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.indicator.padding()
self.content
}
.frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
.background(Color.white)
.clipShape(RoundedShape(corners: [.topLeft, .topRight], radii: SheetConstants.radius))
.frame(height: geometry.size.height, alignment: .bottom)
.offset(y: max(self.offset self.translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
state = value.translation.height
}.onEnded { value in
let snapDistance = self.maxHeight * SheetConstants.snapRatio
guard abs(value.translation.height) > snapDistance else {
return
}
self.isOpen = value.translation.height < 0
}
)
}
.opacity(self.isOpen ? 1 : 0)
}
}
fileprivate enum SheetConstants {
static let radius: CGFloat = 16
static let indicatorHeight: CGFloat = 6
static let indicatorWidth: CGFloat = 60
static let snapRatio: CGFloat = 0.25
static let minHeightRatio: CGFloat = 0
}
Комментарии:
1. Отлично работает с Xcode 12. Их только я прокомментировал из-
clipShape(RoundedShape
за отсутствия в предоставленном снимке.2. @Asperi спасибо, могу я проверить пример кода? потому что это не работает для меня, даже когда я удаляю строку
3. Разместил в ответе все протестированные модули, удалю, если не поможет.
Ответ №1:
Вот полностью протестированный модуль (Xcode 12b5 / iOS 14)
struct BottomSheetView<Content: View>: View {
@Binding var isOpen: Bool
let maxHeight: CGFloat
let minHeight: CGFloat
let content: Content
@GestureState private var translation: CGFloat = 0
private var offset: CGFloat {
isOpen ? 0 : maxHeight - minHeight
}
private var indicator: some View {
RoundedRectangle(cornerRadius: SheetConstants.radius)
.fill(Color.gray)
.frame(
width: SheetConstants.indicatorWidth,
height: SheetConstants.indicatorHeight
).onTapGesture {
self.isOpen.toggle()
}
}
init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self.minHeight = maxHeight * SheetConstants.minHeightRatio
self.maxHeight = maxHeight
self.content = content()
self._isOpen = isOpen
}
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.indicator.padding()
self.content
}
.frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
.background(Color.white)
// .clipShape(RoundedShape(corners: [.topLeft, .topRight], radii: SheetConstants.radius))
.frame(height: geometry.size.height, alignment: .bottom)
.offset(y: max(self.offset self.translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
state = value.translation.height
}.onEnded { value in
let snapDistance = self.maxHeight * SheetConstants.snapRatio
guard abs(value.translation.height) > snapDistance else {
return
}
self.isOpen = value.translation.height < 0
}
)
}
.opacity(self.isOpen ? 1 : 0)
}
}
fileprivate enum SheetConstants {
static let radius: CGFloat = 16
static let indicatorHeight: CGFloat = 6
static let indicatorWidth: CGFloat = 60
static let snapRatio: CGFloat = 0.25
static let minHeightRatio: CGFloat = 0
}
struct TestBottomSheet: View {
@State private var isOpen = false
var body: some View {
VStack {
Button("Open") { isOpen.toggle() }
BottomSheetView(isOpen: $isOpen, maxHeight: 400) {
Text("Hello Sheet!")
}.background(Color.yellow)
}
}
}
struct TestBottomSheet_Previews: PreviewProvider {
static var previews: some View {
TestBottomSheet()
}
}
Комментарии:
1. спасибо, он работает только в режиме предварительного просмотра, но не закрывается в симуляторе. Я попробую это на реальном устройстве
2. хорошо, мой код и ваш код отлично работают на реальном устройстве, но не работают в симуляторе