在 SwiftUI 的 TabView 中,使用 .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always)) 会自动显示分页指示器(圆点),但 SwiftUI 没有提供直接修改分页指示器位置或偏移的官方 API。

有一个变通方式是:把 TabView 包在一个 VStack 里,自定义分页指示器的位置,如下所示。
struct ContentView: View {
@State private var selectedIndex = 0
let pages = ["Page 1", "Page 2", "Page 3"]
var body: some View {
VStack {
TabView(selection: $selectedIndex) {
ForEach(pages.indices, id: \.self) { index in
Text(pages[index])
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.orange)
.tag(index)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) // 禁用默认指示器
.frame(height: 140)
.background(Color.red)
// 自定义分页指示器,偏移位置
HStack(spacing: 8) {
ForEach(pages.indices, id: \.self) { index in
Circle()
.fill(index == selectedIndex ? Color.white : Color.gray)
.frame(width: 8, height: 8)
}
}
}
.padding(.vertical,10)
.background(.purple)
}
}

使用 .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) 禁用系统自带的分页指示器。
自己手动添加一个 HStack 放圆点。
用 @State 跟踪当前页码。
通过自定义分页指示器,可以实现偏移TabView分页指示器的效果。
实际应用
在实际应用中,可以使用tag直接绑定@State变量,并设置自定义分页指示器:
@State private var selectedIndex = 0
TabView(selection: $selectedIndex) {
// 外币,更新时间,折算,统计
HStack {}
.tag(0)
// 数字货币、大宗商品
VStack {}
.tag(1)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.frame(height:160)
// 自定义分页指示器,偏移位置
HStack(spacing: 8) {
ForEach(0...1, id: \.self) { index in
Circle()
.fill(index == selectedIndex ? Color.gray : Color.white)
.frame(width: 8, height: 8)
.overlay(
Circle()
.stroke(Color.gray, lineWidth: 1)
}
}
之所以在自定义分页指示器中使用overlay,是因为.fill()和.stroke()存在冲突,相关可见文章《SwiftUI关于fill()和stroke()组合方式导致无效视图报错问题》
相关文章
1、SwiftUI指定TabView样式的tabViewStyle:https://fangjunyu.com/2025/01/16/swiftui%e6%8c%87%e5%ae%9atabview%e6%a0%b7%e5%bc%8f%e7%9a%84tabviewstyle/
2、SwiftUI关于fill()和stroke()组合方式导致无效视图报错问题:https://fangjunyu.com/2025/04/30/swiftui%e5%85%b3%e4%ba%8efill%e5%92%8cstroke%e7%bb%84%e5%90%88%e6%96%b9%e5%bc%8f%e5%af%bc%e8%87%b4%e6%97%a0%e6%95%88%e8%a7%86%e5%9b%be%e6%8a%a5%e9%94%99%e9%97%ae%e9%a2%98/