iOS 26: Interactive sheet dismissal causes layout hitch in underlying SwiftUI view

I’ve been investigating a noticeable animation hitch when interactively dismissing a sheet over a SwiftUI screen with moderate complexity. This was not the case on iOS 18, so I’m curious if others are seeing the same on iOS 26 or have found any mitigations.

When dismissing a sheet via the swipe gesture, there’s a visible hitch right after lift-off.

  • The hitch comes from layout work in the underlying view (behind the sheet)
  • The duration scales with the complexity of that view (e.g. number of TextFields/layout nodes)

The animation for programmatic dismiss (e.g. tapping a “Done” button) is smooth, although it hangs for a similar amount of time before dismissing, so it appears that the underlying work still happens.

SwiftUI is not reevaluating the body during this (validated with Self._printChanges()), so that is not the cause.

Using Instruments, the hitch shows up as a layout spike on the main thread:

54ms  UIView layoutSublayersOfLayer
54ms  └─ _UIHostingView.layoutSubviews
38ms     └─ SwiftUI.ViewGraph.updateOutputs
11ms        ├─ partial apply for implicit closure #1 in closure #1
            │  in closure #1 in Attribute.init<A>(_:)
 4ms        └─ -[UIView

For the same hierarchy with varying complexity:

  • ~3 TextFields in a List: ~25ms (not noticeable)
  • ~20+ TextFields: ~60ms (clearly visible hitch)

The same view hierarchy on iOS 18 did not exhibit a visible hitch. I’ve tested this on an iOS 26.4 device and simulator.

I’ve also included a minimum reproducible example that illustrates this:


struct ContentView: View {
    @State var showSheet = false
    
    var body: some View {
        NavigationStack {
            ScrollView {
                ForEach(0..<120) { _ in
                    RowView()
                }
            }
            .navigationTitle("Repro")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Present") {
                        showSheet = true
                    }
                }
            }
            .sheet(isPresented: $showSheet) {
                PresentedSheet()
            }
        }
    }
}

struct RowView: View {
    @State var first = ""
    @State var second = ""
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Row")
                .font(.headline)
            
            HStack(spacing: 12) {
                TextField("First", text: $first)
                    .textFieldStyle(.roundedBorder)
                
                TextField("Second", text: $second)
                    .textFieldStyle(.roundedBorder)
            }
            
            HStack(spacing: 12) {
                Text("Third")
                
                Text("Fourth")
                
                Image(systemName: "chevron.right")
            }
        }
    }
}

struct PresentedSheet: View {
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        NavigationStack {
            List {}
                .navigationTitle("Swipe To Dismiss Me")
                .toolbar {
                    ToolbarItem(placement: .topBarTrailing) {
                        Button("Done") {
                            dismiss()
                        }
                    }
                }
        }
    }
}

Is anyone else experiencing this and have any mitigations been found beyond reducing view complexity?

I’ve filed a feedback report under FB22501630.

Thanks for this extremely interesting post. This is great write-up and solid Instruments work and thanks for the bug report.

You can see the status of your feedback in Feedback Assistant. There, you can track if the report is still being investigated, has a potential identifiable fix, or has been resolved in another way. The status appears beside the label "Resolution." We're unable to share any updates on specific reports on the forums.

For more details on when you'll see updates to your report, please see What to expect after submission.

Please attach the .trace file from Instruments directly to the feedback.

The Instruments trace is the key tell. ViewGraph.updateOutputs being triggered on lift-off of an interactive dismiss suggests that may be changed when the underlying view's attribute graph layout pass is scheduled relative to the gesture recognizer's begin phase.

I wonder if you have tried LazyVStack instead.

ScrollView {
    LazyVStack {
        ForEach(items) { item in
            RowView(item: item)
                .geometryGroup()    // ← isolate each row's geometry
        }
    }
}

Albert  WWDR

iOS 26: Interactive sheet dismissal causes layout hitch in underlying SwiftUI view
 
 
Q