How it works
PageTabViewStyle is a tab-view style that lays out a TabView's children as a horizontally paged, swipeable scroll, the SwiftUI equivalent of a UIKit page view controller. Instead of a bottom tab bar, each child becomes a full-width page that the user advances by swiping left or right, with an optional row of page-indicator dots tracking the current position. Reach for it when your content is a sequence of peer screens meant to be browsed in order — onboarding flows, image carousels, or a paged dashboard — rather than a fixed set of destinations.
Apply it through tabViewStyle(_:)
PageTabViewStyle is never used on its own; you hand an instance to the tabViewStyle(_:) modifier, which conforms the enclosing container to the chosen TabViewStyle. In the example, .tabViewStyle(PageTabViewStyle()) is what converts the plain TabView into a paged, swipeable presentation.
Each TabView child becomes one page
Under this style every top-level view inside the TabView is treated as a separate page, sized to fill the container and reached by swiping. Here the three siblings — the blue, green, and orange Color views — turn into Page 1, Page 2, and Page 3, advancing one full screen per swipe.
Control the index dots with init(indexDisplayMode:)
The initializer takes an indexDisplayMode argument of type PageTabViewStyle.IndexDisplayMode that governs the row of page dots: .automatic shows them when appropriate, .always forces them on, and .never hides them. Writing PageTabViewStyle() with no argument uses .automatic, so the demo shows the standard dot indicator beneath the pages.
Drive the current page with a TabView selection
Because the style only changes presentation, paging still flows through TabView's normal selection mechanism: tagging each page and binding TabView(selection:) lets you read or set the visible page programmatically, and swiping updates that binding. The example omits the binding, so the TabView manages the current page internally and starts on the first child.
Size the paged region
A paged TabView expands to fill the space it's offered, so you typically constrain it where it plugs into your layout. In the demo .frame(height: 300) gives the paged area a fixed height while .padding() insets it, keeping the swipe surface to a defined region of the screen.
Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct PageTabViewStyleDemo: View {
var body: some View {
TabView {
Color.blue.overlay(Text("Page 1").foregroundColor(.white).font(.largeTitle))
Color.green.overlay(Text("Page 2").foregroundColor(.white).font(.largeTitle))
Color.orange.overlay(Text("Page 3").foregroundColor(.white).font(.largeTitle))
}
.tabViewStyle(PageTabViewStyle())
.frame(height: 300)
.padding()
}
}