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

SwiftUI实现无图片自定义分段控件的最优方案与动画实现方法

实现方案选择&动画实现教程

方案选型结论

优先选你已经在尝试的ScrollView嵌套HStack加Button的方案,理由如下:

  • 系统原生的Segmented Control(SwiftUI中Picker的segmented样式)定制化上限极低,要修改底部选中边框、自定义间距、支持多选项横向滚动都需要桥接UIKit做大量重写,维护成本远高于自定义实现
  • 自定义HStack方案灵活度拉满,不管是后续调整文字样式、间距、选中动效还是加更多交互都很方便

底部选中边框动画实现

SwiftUI可以非常简单实现你要的选中条跟随动画,核心用matchedGeometryEffect修饰符做共享元素过渡即可,完整可运行的实现代码参考如下:

import SwiftUI

struct CustomSegmentedControl: View {
    // 所有分段选项文本
    let segments: [String]
    // 当前选中索引
    @Binding var selectedIndex: Int
    // 共享动画的命名空间
    @Namespace private var animationNamespace
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(alignment: .center, spacing: 24) {
                ForEach(0..<segments.count, id: \.self) { index in
                    Button(action: {
                        // 加动画触发选中条过渡
                        withAnimation(.easeInOut(duration: 0.25)) {
                            selectedIndex = index
                        }
                        // 选中后自动滚动到对应选项可见区域
                        UIScrollView.appearance().scrollRectToVisible(
                            CGRect(x: CGFloat(index) * 80, y: 0, width: 80, height: 80),
                            animated: true
                        )
                    }) {
                        VStack(spacing: 12) {
                            Text(segments[index])
                                .fontWeight(selectedIndex == index ? .semibold : .regular)
                                .foregroundColor(selectedIndex == index ? .black : .gray)
                            
                            // 选中状态的底部边框
                            if selectedIndex == index {
                                Rectangle()
                                    .fill(.blue)
                                    .frame(height: 2)
                                    // 绑定id,触发共享动画
                                    .matchedGeometryEffect(id: "selectedIndicator", in: animationNamespace)
                            } else {
                                // 未选中状态的底部占位透明条,保证高度一致
                                Rectangle()
                                    .fill(.clear)
                                    .frame(height: 2)
                            }
                        }
                        .padding(.horizontal, 8)
                    }
                }
            }
            .frame(height: 80, alignment: .leading)
            .padding(.horizontal, 16)
        }
    }
}

// 调用示例
struct ContentView: View {
    @State private var selected = 0
    let testSegments = ["推荐", "关注", "热榜", "科技", "数码", "游戏", "影视", "生活"]
    
    var body: some View {
        CustomSegmentedControl(segments: testSegments, selectedIndex: $selected)
    }
}

优化点说明

  • 代码里的间距、颜色、动画时长都可以根据你的设计需求自行调整
  • 如果不需要横向滚动,直接去掉外层的ScrollView即可,选中动画不受影响
  • 不需要桥接任何UIKit组件,纯SwiftUI实现兼容iOS 14及以上系统

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 09:36:04