fix: remove .zoom transition causing trip card disappearance on back
ROOT CAUSE: .navigationTransition(.zoom) + .matchedTransitionSource in a paged TabView. When navigating back, the zoom tries to animate to the source card, but the TabView may have recycled/repositioned the page. The animation targets a stale frame, causing the card to flash black or disappear. FIX: Removed .zoom transitions and .matchedTransitionSource from both UpcomingTripsPageView and PastTripsSection. Standard push/pop navigation is stable with paged TabViews. Also removed all debug logging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,11 +27,8 @@ final class TripsViewModel {
|
|||||||
error = nil
|
error = nil
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let fetched = try await api.getTrips()
|
trips = try await api.getTrips()
|
||||||
print("[TRIPS] loadTrips: fetched \(fetched.count) trips")
|
|
||||||
trips = fetched
|
|
||||||
} catch {
|
} catch {
|
||||||
print("[TRIPS] loadTrips ERROR: \(error)")
|
|
||||||
self.error = error.localizedDescription
|
self.error = error.localizedDescription
|
||||||
}
|
}
|
||||||
isLoading = false
|
isLoading = false
|
||||||
@@ -40,11 +37,8 @@ final class TripsViewModel {
|
|||||||
func refresh() async {
|
func refresh() async {
|
||||||
isLoading = true
|
isLoading = true
|
||||||
do {
|
do {
|
||||||
let fetched = try await api.getTrips()
|
trips = try await api.getTrips()
|
||||||
print("[TRIPS] refresh: fetched \(fetched.count) trips")
|
|
||||||
trips = fetched
|
|
||||||
} catch {
|
} catch {
|
||||||
print("[TRIPS] refresh ERROR: \(error)")
|
|
||||||
self.error = error.localizedDescription
|
self.error = error.localizedDescription
|
||||||
}
|
}
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import SwiftUI
|
|||||||
/// Adapted from Apple's Wishlist TripCollectionView.
|
/// Adapted from Apple's Wishlist TripCollectionView.
|
||||||
struct PastTripsSection: View {
|
struct PastTripsSection: View {
|
||||||
let trips: [Trip]
|
let trips: [Trip]
|
||||||
let namespace: Namespace.ID
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let _ = { print("[TRIPS] PastTripsSection body — trips=\(trips.count) ids=\(trips.map(\.id))") }()
|
|
||||||
if !trips.isEmpty {
|
if !trips.isEmpty {
|
||||||
Section {
|
Section {
|
||||||
ScrollView(.horizontal) {
|
ScrollView(.horizontal) {
|
||||||
@@ -15,11 +13,8 @@ struct PastTripsSection: View {
|
|||||||
ForEach(trips) { trip in
|
ForEach(trips) { trip in
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
TripPlaceholderView(trip: trip)
|
TripPlaceholderView(trip: trip)
|
||||||
.navigationTransition(
|
|
||||||
.zoom(sourceID: trip.id, in: namespace))
|
|
||||||
} label: {
|
} label: {
|
||||||
TripCard(trip: trip, size: .compact)
|
TripCard(trip: trip, size: .compact)
|
||||||
.matchedTransitionSource(id: trip.id, in: namespace)
|
|
||||||
.contentShape(.rect)
|
.contentShape(.rect)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ import SwiftUI
|
|||||||
struct TripsHomeView: View {
|
struct TripsHomeView: View {
|
||||||
@State private var vm = TripsViewModel()
|
@State private var vm = TripsViewModel()
|
||||||
|
|
||||||
@Namespace private var namespace
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Group {
|
Group {
|
||||||
@@ -26,7 +24,7 @@ struct TripsHomeView: View {
|
|||||||
.padding(.bottom, 20)
|
.padding(.bottom, 20)
|
||||||
|
|
||||||
// Secondary: past trips (horizontal scroll, compact cards)
|
// Secondary: past trips (horizontal scroll, compact cards)
|
||||||
PastTripsSection(trips: vm.pastTrips, namespace: namespace)
|
PastTripsSection(trips: vm.pastTrips)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.contentMargins(.bottom, 30, for: .scrollContent)
|
.contentMargins(.bottom, 30, for: .scrollContent)
|
||||||
@@ -37,12 +35,6 @@ struct TripsHomeView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(Color.canvas)
|
.background(Color.canvas)
|
||||||
.onAppear {
|
|
||||||
print("[TRIPS] TripsHomeView onAppear — trips=\(vm.trips.count) upcoming=\(vm.upcomingTrips.count) past=\(vm.pastTrips.count) isLoading=\(vm.isLoading)")
|
|
||||||
}
|
|
||||||
.onChange(of: vm.trips.count) { old, new in
|
|
||||||
print("[TRIPS] trips count changed \(old) → \(new)")
|
|
||||||
}
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
// Custom expanded title — Wishlist pattern
|
// Custom expanded title — Wishlist pattern
|
||||||
ToolbarItem(placement: .title) {
|
ToolbarItem(placement: .title) {
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ import SwiftUI
|
|||||||
struct UpcomingTripsPageView: View {
|
struct UpcomingTripsPageView: View {
|
||||||
let trips: [Trip]
|
let trips: [Trip]
|
||||||
|
|
||||||
@Namespace private var namespace
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let _ = { print("[TRIPS] UpcomingTripsPageView body — trips=\(trips.count) ids=\(trips.map(\.id))") }()
|
|
||||||
if trips.isEmpty {
|
if trips.isEmpty {
|
||||||
// No upcoming trips — show a prompt
|
// No upcoming trips — show a prompt
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
@@ -29,10 +26,6 @@ struct UpcomingTripsPageView: View {
|
|||||||
ForEach(trips) { trip in
|
ForEach(trips) { trip in
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
TripPlaceholderView(trip: trip)
|
TripPlaceholderView(trip: trip)
|
||||||
.navigationTransition(
|
|
||||||
.zoom(sourceID: trip.id, in: namespace))
|
|
||||||
.onAppear { print("[TRIPS] detail onAppear: \(trip.name)") }
|
|
||||||
.onDisappear { print("[TRIPS] detail onDisappear: \(trip.name)") }
|
|
||||||
} label: {
|
} label: {
|
||||||
TripImageView(url: trip.imageURL, fallbackName: trip.name)
|
TripImageView(url: trip.imageURL, fallbackName: trip.name)
|
||||||
.overlay(alignment: .bottomLeading) {
|
.overlay(alignment: .bottomLeading) {
|
||||||
@@ -55,7 +48,6 @@ struct UpcomingTripsPageView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.padding(.bottom, 54)
|
.padding(.bottom, 54)
|
||||||
}
|
}
|
||||||
.matchedTransitionSource(id: trip.id, in: namespace)
|
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user