SwiftUI iOS 16 TabView PageTabViewStyle index behavior is wrong for right to left layoutDirection

TabView page control element has a bug on iOS 16 if tabview is configured as RTL with PageTabViewStyle.

Found iOS 16 Issues:

  • Page indicators display dots in reverse order (appears to treat layout as LTR while showing RTL)
  • Index selection is reversed - tapping indicators selects wrong pages
  • Using the page control directly to navigate eventually breaks the index binding
  • The underlying index counting logic conflicts with the visual presentation

iOS 18 Behavior:

Works as expected with correct dot order and index selection.

Xcode version:

Version 16.3 (16E140)

Conclusion:

  • Confirmed broken on iOS 16
  • Confirmed working on iOS 18
  • iOS 17 and earlier versions not yet tested

I've opened a feedback assistant ticket quite a while ago but there is no answer. There's a code example and a video there.

Anyone else had experience with this particular bug?

Here's the code:

public struct PagingView<Content: View>: View {
    //MARK: - Public Properties
    let pages: (Int) -> Content
    let numberOfPages: Int
    let pageMargin: CGFloat
    @Binding var currentPage: Int

    //MARK: - Object's Lifecycle
    public init(currentPage: Binding<Int>,
                pageMargin: CGFloat = 20,
                numberOfPages: Int,
         @ViewBuilder pages: @escaping (Int) -> Content) {
        self.pages = pages
        self.numberOfPages = numberOfPages
        self.pageMargin = pageMargin

        _currentPage =  currentPage
    }
    
    //MARK: - View's Layout
    public var body: some View {
            TabView(selection: $currentPage) {
                ForEach(0..<numberOfPages, id: \.self) { index in
                    pages(index)
                        .padding(.horizontal, pageMargin)

                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
            .ignoresSafeArea()
    }
}

//MARK: - Previews
struct ContentView: View {
    @State var currentIndex: Int = 0

    var body: some View {
        ZStack {
            Rectangle()
                .frame(height: 300)
                .foregroundStyle(Color.gray.opacity(0.2))

            PagingView(
                currentPage: $currentIndex.onChange({ index in
                    print("currentIndex: ", index)
                }),
                pageMargin: 20,
                numberOfPages: 10) { index in
                    ZStack {
                        Rectangle()
                            .frame(width: 200, height: 200)
                            .foregroundStyle(Color.gray.opacity(0.2))
                        Text("\(index)")
                            .foregroundStyle(.brown)
                            .background(Color.yellow)
                            
                    }
                    
                }.frame(height: 200)
        }
    }
}

#Preview("ContentView") {
    ContentView()
}

extension Binding {
    @MainActor
    func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { newValue in
                self.wrappedValue = newValue
                handler(newValue)
            }
        )
    }
}

SwiftUI iOS 16 TabView PageTabViewStyle index behavior is wrong for right to left layoutDirection
 
 
Q