From a0efde2b508d8b87682785a4470dc70790a5f3e0 Mon Sep 17 00:00:00 2001 From: Zaphod Beeblebrox Date: Tue, 21 May 2024 17:57:53 +0200 Subject: [PATCH] Patch 0.5.1 style: - Scale & Cube Rotation animations reworked chore: - Minor code refactoring --- .gitignore | 1 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 --- .../Internal/Managers/NavigationManager.swift | 6 +- Sources/Internal/Views/NavigationView.swift | 70 ++++++++++--------- 4 files changed, 39 insertions(+), 46 deletions(-) delete mode 100644 .swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/.gitignore b/.gitignore index 48f34b0..60f1e6e 100644 --- a/.gitignore +++ b/.gitignore @@ -99,4 +99,5 @@ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc +.swiftpm/xcode/package.xcworkspace/xcshareddata >>>>>>> bfd2701 (Initial Commit) diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Sources/Internal/Managers/NavigationManager.swift b/Sources/Internal/Managers/NavigationManager.swift index 5af8206..5cb91fb 100644 --- a/Sources/Internal/Managers/NavigationManager.swift +++ b/Sources/Internal/Managers/NavigationManager.swift @@ -12,7 +12,7 @@ import SwiftUI public class NavigationManager: ObservableObject { @Published private(set) var views: [AnyNavigatableView] = [] { willSet { onViewsWillUpdate(newValue) } } - @Published private(set) var transitionsBlocked: Bool = false + private(set) var transitionsBlocked: Bool = false private(set) var transitionType: TransitionType = .push private(set) var transitionAnimation: TransitionAnimation = .no @@ -26,9 +26,7 @@ extension NavigationManager { } extension NavigationManager { static func performOperation(_ operation: Operation) { if !NavigationManager.shared.transitionsBlocked { - DispatchQueue.main.async { withAnimation(nil) { - shared.views.perform(operation) - }} + DispatchQueue.main.async { shared.views.perform(operation) } }} } diff --git a/Sources/Internal/Views/NavigationView.swift b/Sources/Internal/Views/NavigationView.swift index ae20278..2194d35 100644 --- a/Sources/Internal/Views/NavigationView.swift +++ b/Sources/Internal/Views/NavigationView.swift @@ -14,29 +14,21 @@ struct NavigationView: View { @ObservedObject private var stack: NavigationManager = .shared @ObservedObject private var screenManager: ScreenManager = .shared @State private var temporaryViews: [AnyNavigatableView] = [] - @State private var animatableOpacity: CGFloat = 1 - @State private var animatableOffset: CGFloat = 0 - @State private var animatableRotation: CGFloat = 0 - @State private var animatableScale: CGFloat = 0 + @State private var animatableData: AnimatableData = .init() private let config: NavigationGlobalConfig - init(rootView: some NavigatableView, config: NavigationGlobalConfig?) { - NavigationManager.setRoot(rootView) - - self.config = config ?? .init() - self._temporaryViews = .init(initialValue: NavigationManager.shared.views) - } + init(rootView: some NavigatableView, config: NavigationGlobalConfig?) { NavigationManager.setRoot(rootView); self.config = config ?? .init() } var body: some View { ZStack { ForEach(temporaryViews, id: \.id, content: createItem) } .ignoresSafeArea(.container) .onChange(of: stack.views, perform: onViewsChanged) - .onAnimationCompleted(for: animatableOpacity, perform: onAnimationCompleted) + .onAnimationCompleted(for: animatableData.opacity, perform: onAnimationCompleted) } } private extension NavigationView { func createItem(_ item: AnyNavigatableView) -> some View { - item + item.body .padding(.top, getTopPadding(item)) .padding(.bottom, getBottomPadding(item)) .frame(maxWidth: .infinity, maxHeight: .infinity) @@ -84,7 +76,7 @@ private extension NavigationView { return view == lastView } func calculateOpacityValue(_ isLastView: Bool) -> CGFloat { - isLastView ? animatableOpacity : 1 - animatableOpacity + isLastView ? animatableData.opacity : 1 - animatableData.opacity } func calculateFinalOpacityValue(_ opacity: CGFloat) -> CGFloat { switch stack.transitionAnimation { case .no, .dissolve, .scale: opacity @@ -96,7 +88,7 @@ private extension NavigationView { private extension NavigationView { func getOffset(_ view: AnyNavigatableView) -> CGSize { guard canCalculateOffset(view) else { return .zero } let offsetSlideValue = calculateSlideOffsetValue(view) - let offset = animatableOffset + offsetSlideValue + let offset = animatableData.offset + offsetSlideValue let offsetX = calculateXOffsetValue(offset), offsetY = calculateYOffsetValue(offset) let finalOffset = calculateFinalOffsetValue(view, offsetX, offsetY) return finalOffset @@ -135,8 +127,8 @@ private extension NavigationView { return true } func calculateScaleValue(_ view: AnyNavigatableView) -> CGFloat { switch view == temporaryViews.last { - case true: stack.transitionType == .push ? 1 - scaleFactor + animatableScale : 1 - animatableScale - case false: stack.transitionType == .push ? 1 + animatableScale : 1 + scaleFactor - animatableScale + case true: stack.transitionType == .push ? 1 - scaleFactor + animatableData.scale : 1 - animatableData.scale + case false: stack.transitionType == .push ? 1 + animatableData.scale : 1 + scaleFactor - animatableData.scale }} func calculateFinalScaleValue(_ scaleValue: CGFloat) -> CGFloat { stack.transitionsBlocked ? scaleValue : 1 } } @@ -155,8 +147,8 @@ private extension NavigationView { let rotationTranslation = calculateRotationTranslationValue(view) return rotationTranslation } - func getRotationAxis() -> (x: CGFloat, y: CGFloat, z: CGFloat) { (x: 0, y: 1, z: 0) } - func getRotationPerspective() -> CGFloat { 1.8 } + func getRotationAxis() -> (x: CGFloat, y: CGFloat, z: CGFloat) { (x: 0.000000000001, y: 1, z: 0) } + func getRotationPerspective() -> CGFloat { 1.6 } } private extension NavigationView { func canCalculateRotation(_ view: AnyNavigatableView) -> Bool { @@ -165,12 +157,12 @@ private extension NavigationView { return true } func calculateRotationAngleValue(_ view: AnyNavigatableView) -> Angle { switch view == temporaryViews.last { - case true: .degrees(90 + -animatableRotation * 90) - case false: .degrees(-animatableRotation * 90) + case true: .degrees(90 + -animatableData.rotation * 90) + case false: .degrees(-animatableData.rotation * 90) }} func calculateRotationTranslationValue(_ view: AnyNavigatableView) -> CGFloat { switch view == temporaryViews.last { - case true: screenManager.size.width - (animatableRotation * screenManager.size.width) - case false: -1 * (animatableRotation * screenManager.size.width) + case true: screenManager.size.width - (animatableData.rotation * screenManager.size.width) + case false: -1 * (animatableData.rotation * screenManager.size.width) }} } @@ -178,7 +170,8 @@ private extension NavigationView { private extension NavigationView { func getAnimation() -> Animation { switch stack.transitionAnimation { case .no: .easeInOut(duration: 0) - case .scale, .dissolve, .horizontalSlide, .verticalSlide: .spring(duration: 0.36, bounce: 0, blendDuration: 0.1) + case .dissolve, .horizontalSlide, .verticalSlide: .spring(duration: 0.36, bounce: 0, blendDuration: 0.1) + case .scale: .snappy case .cubeRotation: .easeOut(duration: 0.52) }} } @@ -203,16 +196,16 @@ private extension NavigationView { func resetOffsetAndOpacity() { let animatableOffsetFactor = stack.transitionType == .push ? 1.0 : -1.0 - animatableOffset = maxOffsetValue * animatableOffsetFactor - animatableOpacity = 0 - animatableRotation = stack.transitionType == .push ? 0 : 1 - animatableScale = 0 + animatableData.offset = maxOffsetValue * animatableOffsetFactor + animatableData.opacity = 0 + animatableData.rotation = stack.transitionType == .push ? 0 : 1 + animatableData.scale = 0 } func animateOffsetAndOpacityChange() { withAnimation(getAnimation()) { - animatableOffset = 0 - animatableOpacity = 1 - animatableRotation = 1 - animatableRotation - animatableScale = scaleFactor + animatableData.offset = 0 + animatableData.opacity = 1 + animatableData.rotation = 1 - animatableData.rotation + animatableData.scale = scaleFactor }} } @@ -230,15 +223,24 @@ private extension NavigationView { func resetViewOnAnimationCompleted() { if stack.transitionType == .pop { temporaryViews = stack.views - animatableOffset = -maxOffsetValue - animatableRotation = 1 + animatableData.offset = -maxOffsetValue + animatableData.rotation = 1 } } } // MARK: - Configurables private extension NavigationView { - var scaleFactor: CGFloat { 0.38 } + var scaleFactor: CGFloat { 0.46 } var maxXOffsetValueWhileRemoving: CGFloat { screenManager.size.width * 0.33 } var maxOffsetValue: CGFloat { [.horizontalSlide: screenManager.size.width, .verticalSlide: screenManager.size.height][stack.transitionAnimation] ?? 0 } } + + +// MARK: - Animatable Data +fileprivate struct AnimatableData { + var opacity: CGFloat = 1 + var offset: CGFloat = 0 + var rotation: CGFloat = 0 + var scale: CGFloat = 0 +}