SwiftUI TabView with .web page type appears to permit person interplay to interrupt an in-flight web page transition, leaving the UI in an inconsistent state.
Anticipated: the seen web page all the time matches the certain choice.
Noticed: if the person faucets/drags the pager throughout a programmatic web page change, the certain choice updates, however the seen web page can stay the earlier one. After that, subsequent web page modifications can “bounce” (e.g. two pages at a time), as a result of the visible web page and choice have diverged.
Here’s a GIF displaying the problem:
Within the GIF, I begin with choice = 5 and web page “Web page 5” seen. I faucet Prev after which work together with the pager mid-transition. The seen web page stays “Web page 5”, however the mannequin has up to date (choice = 4). Tapping Prev once more then animates two pages to succeed in “Web page 3”, which suggests the pager’s inside state is now out of sync with the binding.
Repro steps:
- Run the minimal reproducible instance beneath.
- Faucet Prev or Subsequent to vary
choice. - Whereas the web page transition remains to be in progress, shortly faucet/drag on the pager.
- Observe that
choicemodifications however the seen web page generally doesn’t.
Minimal reproducible instance
struct Web page: View {
let index: Int
var physique: some View {
ZStack {
RoundedRectangle(cornerRadius: 24)
.fill(Shade(white: 0.92))
.padding(24)
Textual content("Web page (index)")
.font(.system(measurement: 48, weight: .daring, design: .rounded))
}
}
}
struct PagerDesyncReproView: View {
@State personal var choice: Int = 0
personal let pageCount = 6
var physique: some View {
VStack(spacing: 0) {
HStack(spacing: 12) {
Button("Prev") {
guard choice > 0 else { return }
choice -= 1
}
.buttonStyle(.bordered)
Button("Subsequent") {
guard choice < pageCount - 1 else { return }
choice += 1
}
.buttonStyle(.borderedProminent)
Spacer()
Textual content("choice = (choice)")
.font(.system(.caption, design: .monospaced))
.foregroundStyle(.secondary)
}
.padding(.horizontal)
TabView(choice: $choice) {
ForEach(0..<pageCount, id: .self) { i in
Web page(index: i)
.tag(i)
}
}
.animation(.default, worth: choice)
.transition(.slide)
.tabViewStyle(.web page(indexDisplayMode: .all the time))
.body(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
Is there a supported strategy to forestall this desynchronization (e.g. disable interplay till the web page transition completes), or to detect cancellation/completion of the web page transition so the binding might be stored constant?


