SwiftUI实现带动画的TabBar圆形选中指示器移动效果
SwiftUI实现带动画圆形指示器的TabBar
要实现单个圆形指示器在Tab间平滑移动,核心是获取每个Tab的位置坐标,再通过动画让圆形跟随选中索引动态调整位置。以下是修改后的完整代码及关键说明:
完整实现代码
struct ContentView: View { @State var selectedIndex = 2 // 存储每个Tab的中心X坐标,数量对应Tab总数 @State private var tabCenters: [CGFloat] = Array(repeating: 0, count: 5) var body: some View { ZStack(alignment: .center) { HStack(spacing: 0) { // 遍历生成Tab,避免重复代码 ForEach(0..<5, id: \.self) { index in Image(systemName: "\(index).square") .onTapGesture { // 点击时触发动画更新选中索引 withAnimation(.easeInOut(duration: 0.3)) { selectedIndex = index } } .background( // 通过GeometryReader获取当前Tab在TabBar坐标系中的位置 GeometryReader { geo in Color.clear .onAppear { // 保存Tab的中心X坐标 tabCenters[index] = geo.frame(in: .named("TabBar")).midX } } ) // 最后一个Tab不添加Spacer if index != 4 { Spacer() } } } .padding() .coordinateSpace(name: "TabBar") // 定义专属坐标系,方便位置计算 // 圆形指示器 Image(systemName: "circle") .resizable() .scaledToFit() .frame(width: 32, height: 32) .allowsHitTesting(false) // 根据选中索引,计算圆形的X偏移量 .offset(x: tabCenters[selectedIndex] - UIScreen.main.bounds.width / 2) // 绑定动画到选中索引的变化 .animation(.easeInOut(duration: 0.3), value: selectedIndex) } .onChange(of: selectedIndex) { newValue in print("newValue is \(newValue)") } } }
关键实现细节
获取Tab位置:
- 给
HStack设置专属坐标系TabBar,通过GeometryReader获取每个Tab在该坐标系中的中心X坐标,并存入tabCenters数组。 onAppear时初始化每个Tab的位置,确保布局完成后能拿到准确坐标。
- 给
动画移动逻辑:
- 点击Tab时用
withAnimation包裹选中索引的变更,或者给圆形指示器添加.animation(..., value: selectedIndex),两种方式都能触发平滑过渡动画。 - 圆形的
offset计算:因为ZStack默认以屏幕中心为原点,所以用Tab的中心X坐标减去屏幕宽度的一半,得到需要偏移的距离。
- 点击Tab时用
代码优化:
- 用
ForEach遍历生成Tab,避免重复编写相同代码,后续增减Tab只需修改遍历范围即可。
- 用
这样就能实现单个圆形指示器在不同Tab间的动画移动效果,点击任意Tab时,圆形会平滑过渡到对应位置。
内容的提问来源于stack exchange,提问作者Mac
相关产品推荐
相关产品推荐

