mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Fix: Janky Animation
This commit is contained in:
parent
7578ccae60
commit
8d7cc2ed58
3 changed files with 37 additions and 9 deletions
|
|
@ -184,6 +184,22 @@ struct ContentView: View {
|
||||||
private let musicControlPauseGrace: TimeInterval = 5
|
private let musicControlPauseGrace: TimeInterval = 5
|
||||||
private let musicControlResumeDelay: TimeInterval = 0.24
|
private let musicControlResumeDelay: TimeInterval = 0.24
|
||||||
|
|
||||||
|
// MARK: - Tab switch direction for smooth transitions
|
||||||
|
|
||||||
|
private var tabSwitchTransition: AnyTransition {
|
||||||
|
if coordinator.tabSwitchForward {
|
||||||
|
return .asymmetric(
|
||||||
|
insertion: .move(edge: .trailing).combined(with: .opacity),
|
||||||
|
removal: .move(edge: .leading).combined(with: .opacity)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return .asymmetric(
|
||||||
|
insertion: .move(edge: .leading).combined(with: .opacity),
|
||||||
|
removal: .move(edge: .trailing).combined(with: .opacity)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var standardMediaControlsActive: Bool {
|
private var standardMediaControlsActive: Bool {
|
||||||
showStandardMediaControls && !enableMinimalisticUI
|
showStandardMediaControls && !enableMinimalisticUI
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +415,6 @@ struct ContentView: View {
|
||||||
alignment: .top
|
alignment: .top
|
||||||
)
|
)
|
||||||
.frame(maxHeight: .infinity, alignment: .top)
|
.frame(maxHeight: .infinity, alignment: .top)
|
||||||
.animation(nil, value: coordinator.currentView)
|
|
||||||
.environmentObject(privacyManager)
|
.environmentObject(privacyManager)
|
||||||
.onChange(of: dynamicNotchSize) { oldSize, newSize in
|
.onChange(of: dynamicNotchSize) { oldSize, newSize in
|
||||||
guard oldSize != newSize else { return }
|
guard oldSize != newSize else { return }
|
||||||
|
|
@ -755,13 +770,16 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.id(coordinator.currentView) // Force SwiftUI to treat each view as unique
|
.id(coordinator.currentView)
|
||||||
|
.transition(tabSwitchTransition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.clipped()
|
||||||
.zIndex(1)
|
.zIndex(1)
|
||||||
.allowsHitTesting(vm.notchState == .open)
|
.allowsHitTesting(vm.notchState == .open)
|
||||||
.blur(radius: abs(gestureProgress) > 0.3 ? min(abs(gestureProgress), 8) : 0)
|
.blur(radius: abs(gestureProgress) > 0.3 ? min(abs(gestureProgress), 8) : 0)
|
||||||
.opacity(abs(gestureProgress) > 0.3 ? min(abs(gestureProgress * 2), 0.8) : 1)
|
.opacity(abs(gestureProgress) > 0.3 ? min(abs(gestureProgress * 2), 0.8) : 1)
|
||||||
|
.animation(.smooth(duration: 0.3), value: coordinator.currentView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,12 +102,21 @@ class DynamicIslandViewCoordinator: ObservableObject {
|
||||||
static let shared = DynamicIslandViewCoordinator()
|
static let shared = DynamicIslandViewCoordinator()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
private static let tabOrder: [NotchViews] = [.home, .shelf, .timer, .stats, .colorPicker, .notes, .clipboard, .extensionExperience]
|
||||||
|
|
||||||
|
/// Direction of the most recent tab switch (true = forward/right, false = backward/left)
|
||||||
|
@Published var tabSwitchForward: Bool = true
|
||||||
|
|
||||||
@Published var currentView: NotchViews = .home {
|
@Published var currentView: NotchViews = .home {
|
||||||
didSet {
|
didSet {
|
||||||
if Defaults[.enableMinimalisticUI] && currentView != .home {
|
if Defaults[.enableMinimalisticUI] && currentView != .home {
|
||||||
currentView = .home
|
currentView = .home
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Track direction before SwiftUI re-renders
|
||||||
|
let oldIdx = Self.tabOrder.firstIndex(of: oldValue) ?? 0
|
||||||
|
let newIdx = Self.tabOrder.firstIndex(of: currentView) ?? 0
|
||||||
|
tabSwitchForward = newIdx >= oldIdx
|
||||||
handleStatsTabTransition(from: oldValue, to: currentView)
|
handleStatsTabTransition(from: oldValue, to: currentView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,9 +99,6 @@ struct TabSelectionView: View {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async {
|
|
||||||
ensureValidSelection(with: tabsArray)
|
|
||||||
}
|
|
||||||
return tabsArray
|
return tabsArray
|
||||||
}
|
}
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
@ -110,11 +107,13 @@ struct TabSelectionView: View {
|
||||||
let isSelected = isSelected(tab)
|
let isSelected = isSelected(tab)
|
||||||
let activeAccent = tab.accentColor ?? .white
|
let activeAccent = tab.accentColor ?? .white
|
||||||
TabButton(label: tab.label, icon: tab.icon, selected: isSelected) {
|
TabButton(label: tab.label, icon: tab.icon, selected: isSelected) {
|
||||||
|
withAnimation(.smooth(duration: 0.3)) {
|
||||||
if tab.view == .extensionExperience {
|
if tab.view == .extensionExperience {
|
||||||
coordinator.selectedExtensionExperienceID = tab.experienceID
|
coordinator.selectedExtensionExperienceID = tab.experienceID
|
||||||
}
|
}
|
||||||
coordinator.currentView = tab.view
|
coordinator.currentView = tab.view
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.frame(height: 26)
|
.frame(height: 26)
|
||||||
.foregroundStyle(isSelected ? activeAccent : .gray)
|
.foregroundStyle(isSelected ? activeAccent : .gray)
|
||||||
.background {
|
.background {
|
||||||
|
|
@ -132,8 +131,10 @@ struct TabSelectionView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.animation(.smooth, value: coordinator.currentView)
|
|
||||||
.clipShape(Capsule())
|
.clipShape(Capsule())
|
||||||
|
.onAppear {
|
||||||
|
ensureValidSelection(with: tabs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var extensionTabsEnabled: Bool {
|
private var extensionTabsEnabled: Bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue