History
Loading...
Loading...
September 12, 2025
.tabViewStyle(.page(indexDisplayMode: .never)) to hide default page indicators, then create custom ones with @State var selectedTab = 0 and .onAppear { selectedTab = pageIndex }. This gives you full control over indicator styling and positioning while maintaining smooth swipe gestures.This pattern separates the TabView's built-in pagination from its visual indicators, allowing complete customization of the indicator appearance while maintaining native swipe behavior and accessibility support.
struct CustomPageView: View {
@State private var selectedPage = 0
let pages = ["Page 1", "Page 2", "Page 3"]
var body: some View {
VStack {
TabView(selection: $selectedPage) {
ForEach(0..<pages.count, id: \.self) { index in
Text(pages[index])
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.blue.opacity(0.1))
.tag(index)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
CustomPageIndicator(pageCount: pages.count, currentPage: selectedPage)
}
}
}
struct CustomPageIndicator: View {
let pageCount: Int
let currentPage: Int
var body: some View {
HStack(spacing: 8) {
ForEach(0..<pageCount, id: \.self) { index in
Circle()
.fill(index == currentPage ? Color.primary : Color.secondary)
.frame(width: index == currentPage ? 12 : 8, height: index == currentPage ? 12 : 8)
.animation(.spring(response: 0.3), value: currentPage)
}
}
.padding()
}
}Custom indicators provide better brand consistency, allow for complex animations, and give designers full control over spacing, colors, and interactive states that aren't possible with the default system indicators.