Skip to content

Commit 3aa5523

Browse files
authored
Patch 4.0.5
feat: - added an ability to customise dismiss drag gesture area size fix: - fix problem with scrollview conflicts
1 parent af78b4f commit 3aa5523

13 files changed

Lines changed: 90 additions & 17 deletions

Sources/Internal/Configurables/Global/GlobalConfig+Center.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ public final class GlobalConfigCenter: GlobalConfig { required public init() {}
2727
public var isDragGestureEnabled: Bool = false
2828
public var dragThreshold: CGFloat = 0
2929
public var isStackingEnabled: Bool = false
30+
public var dragGestureAreaSize: CGFloat = 0
3031
}

Sources/Internal/Configurables/Global/GlobalConfig+Vertical.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public final class GlobalConfigVertical: GlobalConfig { required public init() {
2424
public var isTapOutsideToDismissEnabled: Bool = false
2525
public var isDragGestureEnabled: Bool = true
2626
public var dragThreshold: CGFloat = 1/3
27+
public var dragGestureAreaSize: CGFloat = 30
2728

2829
// MARK: Non-Customizable
2930
public var ignoredSafeAreaEdges: Edge.Set = []

Sources/Internal/Configurables/Local/LocalConfig+Center.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ public class LocalConfigCenter: LocalConfig { required public init() {}
2525
public var heightMode: HeightMode = GlobalConfigContainer.center.heightMode
2626
public var dragDetents: [DragDetent] = GlobalConfigContainer.center.dragDetents
2727
public var isDragGestureEnabled: Bool = GlobalConfigContainer.center.isDragGestureEnabled
28+
public var dragGestureAreaSize: CGFloat = GlobalConfigContainer.center.dragGestureAreaSize
2829
}

Sources/Internal/Configurables/Local/LocalConfig+Vertical.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class LocalConfigVertical: LocalConfig { required public init() {}
2525
// MARK: Gestures
2626
public var isTapOutsideToDismissEnabled: Bool = GlobalConfigContainer.vertical.isTapOutsideToDismissEnabled
2727
public var isDragGestureEnabled: Bool = GlobalConfigContainer.vertical.isDragGestureEnabled
28+
public var dragGestureAreaSize: CGFloat = GlobalConfigContainer.vertical.dragGestureAreaSize
2829
}
2930

3031
// MARK: Subclasses

Sources/Internal/Configurables/Local/LocalConfig.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ public protocol LocalConfig { init()
2525
// MARK: Gestures
2626
var isTapOutsideToDismissEnabled: Bool { get set }
2727
var isDragGestureEnabled: Bool { get set }
28+
var dragGestureAreaSize: CGFloat { get set }
2829
}

Sources/Internal/Extensions/View+Gestures.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ extension View {
2424

2525
// MARK: On Drag Gesture
2626
extension View {
27-
func onDragGesture(onChanged actionOnChanged: @escaping (CGFloat) async -> (), onEnded actionOnEnded: @escaping (CGFloat) async -> (), isEnabled: Bool) -> some View {
27+
func onDragGesture(onChanged actionOnChanged: @escaping (DragGestureState) async -> (), onEnded actionOnEnded: @escaping (DragGestureState) async -> (), isEnabled: Bool) -> some View {
2828
#if os(tvOS)
2929
self
3030
#else
31-
highPriorityGesture(
31+
simultaneousGesture(
3232
DragGesture()
33-
.onChanged { newValue in Task { @MainActor in await actionOnChanged(newValue.translation.height) }}
34-
.onEnded { newValue in Task { @MainActor in await actionOnEnded(newValue.translation.height) }},
33+
.onChanged { newValue in Task { @MainActor in await actionOnChanged(DragGestureState(newValue)) }}
34+
.onEnded { newValue in Task { @MainActor in await actionOnEnded(DragGestureState(newValue)) }},
3535
isEnabled: isEnabled
3636
)
3737
#endif

Sources/Internal/Models/AnyPopupConfig.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct AnyPopupConfig: LocalConfig, Sendable { init() {}
2525
// MARK: Gestures
2626
var isTapOutsideToDismissEnabled: Bool = false
2727
var isDragGestureEnabled: Bool = false
28+
var dragGestureAreaSize: CGFloat = 0
2829
}
2930

3031
// MARK: Initialize
@@ -40,5 +41,6 @@ extension AnyPopupConfig {
4041
self.dragDetents = config.dragDetents
4142
self.isTapOutsideToDismissEnabled = config.isTapOutsideToDismissEnabled
4243
self.isDragGestureEnabled = config.isDragGestureEnabled
44+
self.dragGestureAreaSize = config.dragGestureAreaSize
4345
}
4446
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// DragGestureState.swift of MijickPopups
3+
//
4+
// Created by Alina Petrovska
5+
// - Mail: alina.petrovskaya@mijick.com
6+
// - GitHub: https://github.com/alina-p-k
7+
//
8+
// Copyright ©2025 Mijick. All rights reserved.
9+
10+
11+
import SwiftUI
12+
13+
struct DragGestureState {
14+
let startLocationY: Double
15+
let height: Double
16+
}
17+
18+
extension DragGestureState {
19+
init(_ value: DragGesture.Value) {
20+
self.startLocationY = value.startLocation.y
21+
self.height = value.translation.height
22+
}
23+
}

Sources/Internal/UI/PopupVerticalStackView.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ struct PopupVerticalStackView: View {
1818
var body: some View { if viewModel.screen.height > 0 {
1919
ZStack(alignment: (!viewModel.alignment).toAlignment(), content: createPopupStack)
2020
.frame(height: viewModel.screen.height, alignment: viewModel.alignment.toAlignment())
21-
.onDragGesture(onChanged: viewModel.onPopupDragGestureChanged, onEnded: viewModel.onPopupDragGestureEnded, isEnabled: viewModel.dragGestureEnabled)
2221
}}
2322
}
2423
private extension PopupVerticalStackView {
@@ -33,15 +32,16 @@ private extension PopupVerticalStackView {
3332
.padding(viewModel.activePopupProperties.innerPadding)
3433
.fixedSize(horizontal: false, vertical: viewModel.activePopupProperties.verticalFixedSize)
3534
.onHeightChange { await viewModel.updatePopupHeight($0, popup) }
36-
.frame(height: viewModel.activePopupProperties.height, alignment: (!viewModel.alignment).toAlignment())
37-
.frame(maxWidth: .infinity, maxHeight: viewModel.activePopupProperties.height, alignment: (!viewModel.alignment).toAlignment())
35+
.frame(height: viewModel.activePopupProperties.height, alignment: popupAlignment)
36+
.frame(maxWidth: .infinity, maxHeight: viewModel.activePopupProperties.height, alignment: popupAlignment)
3837
.background(backgroundColor: getBackgroundColor(for: popup), overlayColor: getStackOverlayColor(for: popup), corners: viewModel.activePopupProperties.corners)
3938
.offset(y: viewModel.calculateOffsetY(for: popup))
4039
.scaleEffect(x: viewModel.calculateScaleX(for: popup))
4140
.focusSection_tvOS()
4241
.padding(viewModel.activePopupProperties.outerPadding)
4342
.transition(transition)
4443
.zIndex(viewModel.calculateZIndex())
44+
.onDragGesture(onChanged: viewModel.onPopupDragGestureChanged, onEnded: viewModel.onPopupDragGestureEnded, isEnabled: viewModel.dragGestureEnabled)
4545
}}
4646
}
4747

@@ -52,4 +52,5 @@ private extension PopupVerticalStackView {
5252
private extension PopupVerticalStackView {
5353
var stackOverlayColor: Color { .black }
5454
var transition: AnyTransition { .move(edge: viewModel.alignment.toEdge()) }
55+
var popupAlignment: Alignment { (!viewModel.alignment).toAlignment() }
5556
}

Sources/Internal/View Models/ViewModel+VerticalStack.swift

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,29 @@ extension VM.VerticalStack {
310310

311311
// MARK: On Changed
312312
extension VM.VerticalStack {
313-
func onPopupDragGestureChanged(_ value: CGFloat) async {
314-
guard dragGestureEnabled else { return }
313+
func onPopupDragGestureChanged(_ value: DragGestureState) async {
314+
guard dragGestureEnabled, isValidDragGesture(value) else { return }
315315

316-
let newGestureTranslation = await calculateGestureTranslation(value)
316+
let newGestureTranslation = await calculateGestureTranslation(value.height)
317317
await updateGestureTranslation(newGestureTranslation)
318318
}
319319
}
320320
private extension VM.VerticalStack {
321+
func isValidDragGesture(_ value: DragGestureState) -> Bool {
322+
guard popups.isEmpty == false else { return false }
323+
guard let gestureAreaSize = popups.last?.config.dragGestureAreaSize, gestureAreaSize >= 0 else { return false }
324+
325+
var minStartPointOffset: CGFloat {
326+
if screen.height <= activePopupProperties.height ?? 0 { return screen.safeArea.top + gestureAreaSize }
327+
return gestureAreaSize
328+
}
329+
let popupHeight = activePopupProperties.height ?? minStartPointOffset
330+
331+
switch popups.last?.config.alignment {
332+
case .top: return popupHeight - minStartPointOffset <= value.startLocationY
333+
default: return value.startLocationY <= minStartPointOffset
334+
}
335+
}
321336
func calculateGestureTranslation(_ value: CGFloat) async -> CGFloat { switch popups.last?.config.dragDetents.isEmpty ?? true {
322337
case true: calculateGestureTranslationWhenNoDragDetents(value)
323338
case false: calculateGestureTranslationWhenDragDetents(value)
@@ -355,8 +370,8 @@ private extension VM.VerticalStack {
355370

356371
// MARK: On Ended
357372
extension VM.VerticalStack {
358-
func onPopupDragGestureEnded(_ value: CGFloat) async {
359-
guard value != 0, let activePopup = popups.last else { return }
373+
func onPopupDragGestureEnded(_ value: DragGestureState) async {
374+
guard value.height != 0, let activePopup = popups.last, isValidDragGesture(value) else { return }
360375

361376
await dismissLastPopupIfNeeded(activePopup)
362377

0 commit comments

Comments
 (0)