You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)")
        }
    }
}

关键实现细节

  1. 获取Tab位置:

    • 给HStack设置专属坐标系TabBar,通过GeometryReader获取每个Tab在该坐标系中的中心X坐标,并存入tabCenters数组。
    • onAppear时初始化每个Tab的位置,确保布局完成后能拿到准确坐标。
  2. 动画移动逻辑:

    • 点击Tab时用withAnimation包裹选中索引的变更,或者给圆形指示器添加.animation(..., value: selectedIndex),两种方式都能触发平滑过渡动画。
    • 圆形的offset计算:因为ZStack默认以屏幕中心为原点,所以用Tab的中心X坐标减去屏幕宽度的一半,得到需要偏移的距离。
  3. 代码优化:

    • 用ForEach遍历生成Tab,避免重复编写相同代码,后续增减Tab只需修改遍历范围即可。

这样就能实现单个圆形指示器在不同Tab间的动画移动效果,点击任意Tab时,圆形会平滑过渡到对应位置。

内容的提问来源于stack exchange,提问作者Mac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:14:57