From 9b5584a2efdffb0b3fc90608fc8b3091d1106510 Mon Sep 17 00:00:00 2001 From: Iana Date: Tue, 19 Nov 2024 17:56:12 +0200 Subject: [PATCH 1/2] [trello.com/c/sI4VXuJa] Added swipe gestures (left & down) to Snackbar --- .../Models/NotificationModel.swift | 14 ++++++++ .../Views/NotificationView.swift | 35 ++++++++++++++----- PopupKit/Sources/PopupKit/PopupManager.swift | 5 ++- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift b/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift index cab717fba..30f0c8700 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift @@ -9,8 +9,22 @@ import UIKit import CommonKit struct NotificationModel: Equatable, Hashable { + static func == (lhs: NotificationModel, rhs: NotificationModel) -> Bool { + return lhs.icon == rhs.icon + && lhs.title == rhs.title + && lhs.description == rhs.description + } + + func hash(into hasher: inout Hasher) { + hasher.combine(icon) + hasher.combine(title) + hasher.combine(description) + hasher.combine(tapHandler) + } + let icon: UIImage? let title: String? let description: String? let tapHandler: IDWrapper<() -> Void>? + let cancelAutoDismiss: () -> Void } diff --git a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift index 4defc4893..60f8b7550 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift @@ -10,8 +10,11 @@ import CommonKit struct NotificationView: View { @State private var dragTranslation: CGFloat = .zero + @State private var horizontalDragTranslation: CGFloat = .zero @State private var minTranslationForDismiss: CGFloat = .infinity - + @State private var minTranslationXForDismiss: CGFloat = .infinity + @State private var isTextLimited: Bool = true + let model: NotificationModel let safeAreaInsets: EdgeInsets let dismissAction: () -> Void @@ -27,9 +30,11 @@ struct NotificationView: View { .padding([.leading, .trailing], 15) .padding([.top, .bottom], 10) .background(GeometryReader(content: processGeometry)) + .expanded(axes: .horizontal) + .offset(y: dragTranslation < .zero ? dragTranslation : .zero) + .offset(x: horizontalDragTranslation < .zero ? horizontalDragTranslation : .zero) .gesture(dragGesture) .onTapGesture(perform: onTap) - .background(Color(.adamant.swipeBlockColor)) .cornerRadius(10) .padding(.horizontal, 15) .padding(.top, safeAreaInsets.top) @@ -48,7 +53,7 @@ private extension NotificationView { } var textStack: some View { - VStack(alignment: .leading, spacing: .zero) { + VStack(alignment: .leading, spacing: 3) { if let title = model.title { Text(title) .font(.system(size: 15, weight: .bold)) @@ -56,20 +61,32 @@ private extension NotificationView { if let description = model.description { Text(description) .font(.system(size: 13)) - .lineLimit(3) + .lineLimit(isTextLimited ? 3 : nil) + } } } var dragGesture: some Gesture { DragGesture() - .onChanged { dragTranslation = $0.translation.height } + .onChanged { + dragTranslation = $0.translation.height + horizontalDragTranslation = $0.translation.width + } .onEnded { - print(-$0.translation.height, minTranslationForDismiss) if $0.velocity.height < -100 || -$0.translation.height > minTranslationForDismiss { dismissAction() + } else if $0.velocity.width < -100 || $0.translation.width > minTranslationXForDismiss { + dismissAction() + } else if $0.velocity.height > -100 || -$0.translation.height < minTranslationForDismiss { + horizontalDragTranslation = .zero + isTextLimited = false + model.cancelAutoDismiss() } else { - withAnimation { dragTranslation = .zero } + withAnimation { + dragTranslation = .zero + horizontalDragTranslation = .zero + } } } } @@ -77,9 +94,11 @@ private extension NotificationView { func processGeometry(_ geometry: GeometryProxy) -> some View { DispatchQueue.main.async { minTranslationForDismiss = geometry.size.height / 2 + minTranslationXForDismiss = geometry.size.width / 2 } - return Color.clear + return Color.init(uiColor: .adamant.swipeBlockColor) + .cornerRadius(10) } func onTap() { diff --git a/PopupKit/Sources/PopupKit/PopupManager.swift b/PopupKit/Sources/PopupKit/PopupManager.swift index a237dbe5a..89074b25a 100644 --- a/PopupKit/Sources/PopupKit/PopupManager.swift +++ b/PopupKit/Sources/PopupKit/PopupManager.swift @@ -83,7 +83,10 @@ public extension PopupManager { icon: icon, title: title, description: description, - tapHandler: tapHandler.map { .init(id: .empty, value: $0) } + tapHandler: tapHandler.map { .init(id: .empty, value: $0) }, + cancelAutoDismiss: { [weak self] in + self?.autoDismissManager.notificationDismissSubscription?.cancel() + } ) if autoDismiss { From 2571459d486ec6a8b2f5b4a8cbd2e90a2674fafc Mon Sep 17 00:00:00 2001 From: Iana Date: Wed, 27 Nov 2024 12:34:39 +0200 Subject: [PATCH 2/2] [trello.com/c/sI4VXuJa] Fixed animations --- .../Models/NotificationModel.swift | 2 +- .../Implementation/Views/NotificationView.swift | 16 ++++++++++++---- .../Views/PopupCoordinatorView.swift | 8 +++++++- PopupKit/Sources/PopupKit/PopupManager.swift | 4 ++-- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift b/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift index 30f0c8700..473fc8c6c 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Models/NotificationModel.swift @@ -26,5 +26,5 @@ struct NotificationModel: Equatable, Hashable { let title: String? let description: String? let tapHandler: IDWrapper<() -> Void>? - let cancelAutoDismiss: () -> Void + let cancelAutoDismiss: IDWrapper<() -> Void>? } diff --git a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift index 60f8b7550..a405c068f 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Views/NotificationView.swift @@ -14,7 +14,9 @@ struct NotificationView: View { @State private var minTranslationForDismiss: CGFloat = .infinity @State private var minTranslationXForDismiss: CGFloat = .infinity @State private var isTextLimited: Bool = true - + + @Binding var dismissEdge: Edge + var onDismissEdgeChanged: ((Edge) -> Void)? let model: NotificationModel let safeAreaInsets: EdgeInsets let dismissAction: () -> Void @@ -75,13 +77,19 @@ private extension NotificationView { } .onEnded { if $0.velocity.height < -100 || -$0.translation.height > minTranslationForDismiss { - dismissAction() + onDismissEdgeChanged?(.top) + Task { + dismissAction() + } } else if $0.velocity.width < -100 || $0.translation.width > minTranslationXForDismiss { - dismissAction() + onDismissEdgeChanged?(.leading) + Task { + dismissAction() + } } else if $0.velocity.height > -100 || -$0.translation.height < minTranslationForDismiss { horizontalDragTranslation = .zero isTextLimited = false - model.cancelAutoDismiss() + model.cancelAutoDismiss?.value() } else { withAnimation { dragTranslation = .zero diff --git a/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift b/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift index bced879de..be054b484 100644 --- a/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift +++ b/PopupKit/Sources/PopupKit/Implementation/Views/PopupCoordinatorView.swift @@ -10,6 +10,7 @@ import CommonKit struct PopupCoordinatorView: View { @ObservedObject var model: PopupCoordinatorModel + @State var dismissEdge: Edge = .top var body: some View { GeometryReader { geomerty in @@ -34,14 +35,19 @@ private extension PopupCoordinatorView { VStack { if let notificationModel = model.notification { NotificationView( + dismissEdge: $dismissEdge, + onDismissEdgeChanged: { newEdge in + dismissEdge = newEdge + }, model: notificationModel, safeAreaInsets: safeAreaInsets, dismissAction: { [weak model] in model?.notification = nil + dismissEdge = .top } ) .id(model.notification?.hashValue) - .transition(.move(edge: .top)) + .transition(.move(edge: dismissEdge)) } Spacer() } diff --git a/PopupKit/Sources/PopupKit/PopupManager.swift b/PopupKit/Sources/PopupKit/PopupManager.swift index 89074b25a..341a8ecb8 100644 --- a/PopupKit/Sources/PopupKit/PopupManager.swift +++ b/PopupKit/Sources/PopupKit/PopupManager.swift @@ -84,9 +84,9 @@ public extension PopupManager { title: title, description: description, tapHandler: tapHandler.map { .init(id: .empty, value: $0) }, - cancelAutoDismiss: { [weak self] in + cancelAutoDismiss: .init(id: .empty, value: { [weak self] in self?.autoDismissManager.notificationDismissSubscription?.cancel() - } + }) ) if autoDismiss {