fix: remove .zoom transition causing trip card disappearance on back
All checks were successful
Security Checks / dockerfile-lint (push) Successful in 4s
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s

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:
Yusuf Suleman
2026-04-04 14:37:37 -05:00
parent 48b6522cf5
commit 864ca679ce
4 changed files with 3 additions and 30 deletions

View File

@@ -27,11 +27,8 @@ final class TripsViewModel {
error = nil
do {
let fetched = try await api.getTrips()
print("[TRIPS] loadTrips: fetched \(fetched.count) trips")
trips = fetched
trips = try await api.getTrips()
} catch {
print("[TRIPS] loadTrips ERROR: \(error)")
self.error = error.localizedDescription
}
isLoading = false
@@ -40,11 +37,8 @@ final class TripsViewModel {
func refresh() async {
isLoading = true
do {
let fetched = try await api.getTrips()
print("[TRIPS] refresh: fetched \(fetched.count) trips")
trips = fetched
trips = try await api.getTrips()
} catch {
print("[TRIPS] refresh ERROR: \(error)")
self.error = error.localizedDescription
}
isLoading = false

View File

@@ -4,10 +4,8 @@ import SwiftUI
/// Adapted from Apple's Wishlist TripCollectionView.
struct PastTripsSection: View {
let trips: [Trip]
let namespace: Namespace.ID
var body: some View {
let _ = { print("[TRIPS] PastTripsSection body — trips=\(trips.count) ids=\(trips.map(\.id))") }()
if !trips.isEmpty {
Section {
ScrollView(.horizontal) {
@@ -15,11 +13,8 @@ struct PastTripsSection: View {
ForEach(trips) { trip in
NavigationLink {
TripPlaceholderView(trip: trip)
.navigationTransition(
.zoom(sourceID: trip.id, in: namespace))
} label: {
TripCard(trip: trip, size: .compact)
.matchedTransitionSource(id: trip.id, in: namespace)
.contentShape(.rect)
}
.buttonStyle(.plain)

View File

@@ -10,8 +10,6 @@ import SwiftUI
struct TripsHomeView: View {
@State private var vm = TripsViewModel()
@Namespace private var namespace
var body: some View {
NavigationStack {
Group {
@@ -26,7 +24,7 @@ struct TripsHomeView: View {
.padding(.bottom, 20)
// Secondary: past trips (horizontal scroll, compact cards)
PastTripsSection(trips: vm.pastTrips, namespace: namespace)
PastTripsSection(trips: vm.pastTrips)
}
}
.contentMargins(.bottom, 30, for: .scrollContent)
@@ -37,12 +35,6 @@ struct TripsHomeView: View {
}
}
.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 {
// Custom expanded title Wishlist pattern
ToolbarItem(placement: .title) {

View File

@@ -5,10 +5,7 @@ import SwiftUI
struct UpcomingTripsPageView: View {
let trips: [Trip]
@Namespace private var namespace
var body: some View {
let _ = { print("[TRIPS] UpcomingTripsPageView body — trips=\(trips.count) ids=\(trips.map(\.id))") }()
if trips.isEmpty {
// No upcoming trips show a prompt
VStack(spacing: 16) {
@@ -29,10 +26,6 @@ struct UpcomingTripsPageView: View {
ForEach(trips) { trip in
NavigationLink {
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: {
TripImageView(url: trip.imageURL, fallbackName: trip.name)
.overlay(alignment: .bottomLeading) {
@@ -55,7 +48,6 @@ struct UpcomingTripsPageView: View {
.padding(.horizontal)
.padding(.bottom, 54)
}
.matchedTransitionSource(id: trip.id, in: namespace)
}
.buttonStyle(.plain)
}