diff --git a/Sources/BottomSheet/BottomSheetView/BottomSheetView+Gestures.swift b/Sources/BottomSheet/BottomSheetView/BottomSheetView+Gestures.swift index edd237a45..8f8bf7b88 100644 --- a/Sources/BottomSheet/BottomSheetView/BottomSheetView+Gestures.swift +++ b/Sources/BottomSheet/BottomSheetView/BottomSheetView+Gestures.swift @@ -11,6 +11,8 @@ internal extension BottomSheetView { func dragGesture(with geometry: GeometryProxy) -> some Gesture { DragGesture() .onChanged { value in + self.lastDragValue = value + // Perform custom onChanged action self.configuration.onDragChanged(value) @@ -19,20 +21,10 @@ internal extension BottomSheetView { // Dismiss the keyboard on drag self.endEditing() } - .onEnded { value in - // Perform custom onEnded action - self.configuration.onDragEnded(value) - - // Switch the position based on the translation and screen height - self.dragPositionSwitch( - with: geometry, - value: value - ) - - // Reset translation, because the dragging ended - self.translation = 0 - // Dismiss the keyboard after drag - self.endEditing() + // Set isDragging flag to true while user is dragging + // The value is reset to false when dragging is stopped or cancelled + .updating($isDragging) { (value, gestureState, transaction) in + gestureState = true } } diff --git a/Sources/BottomSheet/BottomSheetView/BottomSheetView.swift b/Sources/BottomSheet/BottomSheetView/BottomSheetView.swift index 87d963444..036356c34 100644 --- a/Sources/BottomSheet/BottomSheetView/BottomSheetView.swift +++ b/Sources/BottomSheet/BottomSheetView/BottomSheetView.swift @@ -8,7 +8,9 @@ import SwiftUI internal struct BottomSheetView: View { - + @GestureState var isDragging: Bool = false + @State var lastDragValue: DragGesture.Value? + // For iPhone landscape and iPad support #if !os(macOS) @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? @@ -60,6 +62,26 @@ internal struct BottomSheetView: View { self.bottomSheet(with: geometry) } } + // Handle drag ended or cancelled + // Drag cancellation can happen e.g. when user drags from bottom of the screen to show app switcher + .valueChanged(value: isDragging, onChange: { isDragging in + if lastDragValue != nil && !isDragging { + // Perform custom onEnded action + self.configuration.onDragEnded(lastDragValue!) + + // Switch the position based on the translation and screen height + self.dragPositionSwitch( + with: geometry, + value: lastDragValue! + ) + + // Reset translation and last drag value, because the dragging ended + self.translation = 0 + self.lastDragValue = nil + // Dismiss the keyboard after drag + self.endEditing() + } + }) // Animate value changes #if !os(macOS) .animation( diff --git a/Sources/BottomSheet/Helper/Extensions/ViewExtension.swift b/Sources/BottomSheet/Helper/Extensions/ViewExtension.swift new file mode 100644 index 000000000..689f760f5 --- /dev/null +++ b/Sources/BottomSheet/Helper/Extensions/ViewExtension.swift @@ -0,0 +1,24 @@ +// +// ViewExtension.swift +// +// +// Created by Jukka Hietanen on 12.12.2023. +// + +import Foundation + +import SwiftUI +import Combine + +internal extension View { + /// A backwards compatible wrapper for iOS 14 `onChange` + @ViewBuilder func valueChanged(value: T, onChange: @escaping (T) -> Void) -> some View { + if #available(iOS 14.0, macOS 11.0, *) { + self.onChange(of: value, perform: onChange) + } else { + self.onReceive(Just(value)) { (value) in + onChange(value) + } + } + } +}