FB24767077
After programmatically setting the ID to B, manually paging back to A causes the binding to update to A and then unexpectedly back to B. The view hangs between pages while SwiftUI attempts to animate toward the stale B target.
The issue occurs only when an app built with Xcode 27 runs on iOS 27. It does not occur in:
Xcode 27 build running on iOS 26
Xcode 26 build running on iOS 27
Xcode 26 build running on iOS 26
Steps to reproduce:
- Launch the minimal reproduction project
- Tap “Set B” to assign B directly to the scrollPosition(id:) binding
- Swipe right to return to page A
Expected result:
After paging from B back to A, the scroll position remains A once the pager settles.
Actual result:
The binding emits A followed by B, even though page A is the visible, settled page. The resulting stale B value causes the animation to hang between pages.
Code:
struct ContentView: View {
@State private var selectedPage: Page? = .a
var body: some View {
VStack {
HStack {
Button("Set B") {
selectedPage = .b
}
Text("Selected: \(selectedPage?.rawValue ?? "nil")")
}
PagerView(selectedPage: $selectedPage)
}
.onChange(of: selectedPage, initial: false) { _, newValue in
print("selectedPage: \(newValue?.rawValue ?? "nil")")
}
}
}
struct PagerView: View {
@Binding var selectedPage: Page?
var body: some View {
ScrollView(.horizontal) {
LazyHStack(spacing: .zero) {
ForEach(Page.allCases) { page in
Text(page.rawValue)
.font(.largeTitle)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(page.color)
.containerRelativeFrame(.horizontal)
}
}
.scrollTargetLayout()
}
.frame(height: 300)
.scrollIndicators(.hidden)
.scrollPosition(id: $selectedPage)
.scrollTargetBehavior(.paging)
.animation(.default, value: selectedPage)
}
}
enum Page: String, CaseIterable, Identifiable {
case a = "A"
case b = "B"
case c = "C"
var id: Self { self }
var color: Color {
switch self {
case .a: .red.opacity(0.2)
case .b: .green.opacity(0.2)
case .c: .blue.opacity(0.2)
}
}
}