【免费下载链接】SDSegmentedControl
A drop-in remplacement for UISegmentedControl that mimic iOS 6 AppStore tab controls
SDSegmentedControl 是一个可以直接替换系统 UISegmentedControl的经典 iOS 分段控件库,完整复刻了 iOS 6 时代 App Store 里带"箭头 + 下划线指示器"的分段标签样式。本文面向 iOS 新手,系统梳理它的iOS 6/7 兼容处理机制、Interface Builder(IB)集成技巧、常见坑位排查清单与现代化改造建议,帮助你在维护老项目或仿写 App Store 风格导航栏时少踩坑、少走弯路。
1️⃣ SDSegmentedControl 能解决什么问题?
系统 UISegmentedControl 的样式在 iOS 6 与 iOS 7 之间差异很大,而且画不出当年 App Store 那种"选中箭头沿下划线滑动"的效果。SDSegmentedControl 的核心卖点是drop-in replacement(零改动替换):接口与 UISegmentedControl 保持一致,拿来即用。
它的主要能力(完整列表见 README.md):
| 能力 | 说明 |
|---|---|
| 图文混排分段 | 支持纯文本、纯图片、图片 + 文字三种分段形态 |
| 动画选择 | 选中箭头沿下划线平滑滑动,动画时长可调 |
| 内容自适应宽度 | 分段宽度随标题长度自动伸缩,也可单独指定宽度 |
| 横向滚动 | 分段过多放不下时自动启用内部 UIScrollView |
| UIAppearance 换肤 | 通过外观代理全局统一主题 |
| 细粒度控制 | 单独启用/禁用某一分段、监听 valueChanged 事件 |
整个库只有两个源码文件:SDSegmentedControl.h 与 SDSegmentedControl.m。它的动画核心是用CAShapeLayer的路径动画实时绘制箭头与下划线,实现位于 SDSegmentedControl.m#L660-L837。运行效果演示可参考仓库中的 doc/screencast.gif。
2️⃣ iOS 6/7 兼容处理:SD_IS_IOS7 宏的工作方式
这套库诞生在 iOS 6 与 iOS 7 并存的时代,作者用运行时版本检测宏SD_IS_IOS7同时兼容两套视觉规范,宏定义见 SDSegmentedControl.m#L7-L11:
#ifdef NSFoundationVersionNumber_iOS_6_1 #define SD_IS_IOS7 (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) #else #define SD_IS_IOS7 NO #endif不同系统版本下的默认样式差异如下:
| 样式项 | iOS 6 分支 | iOS 7 分支 |
|---|---|---|
| 控件背景 | 浅灰(0.961 白)+ 阴影 | 透明 |
| 下划线 | 白色、0.5pt | 黑色、0.25pt |
| 标题字体 | 全部 Bold 14pt,带白色标题阴影 | 常规 14pt,仅选中项 Bold |
| 选中胶囊(SDStainView) | 灰底 + 白色内描边 + 投影 | 透明底 |
这些分支分别位于commonInit(SDSegmentedControl.m#L108-L136)、SDSegmentView初始化(SDSegmentedControl.m#L1082-L1113)和SDStainView外观默认值(SDSegmentedControl.m#L1261-L1280)。
⚠️注意:检测基于运行时版本号。在今天的设备上
SD_IS_IOS7恒为 YES,也就是说iOS 6 分支实际上永远不会生效——这一点在后面的"现代化改造"一节里会展开讲。
3️⃣ IB(Interface Builder)集成技巧
SDSegmentedControl 对 IB 的集成非常巧妙:不需要画新控件,只需要换一下类名。官方示例 Example/Example/en.lproj/ViewController.xib 中,IB 里拖入的就是一个普通的UISegmentedControl,只是把customClass改成了SDSegmentedControl。
操作步骤:
- 在 IB 画布拖入
UISegmentedControl,正常配置分段标题、selectedSegmentIndex与valueChanged事件; - 打开 Identity 检查器,将Class 从 UISegmentedControl 改为 SDSegmentedControl;
- IB 中定义的分段会在 awakeFromNib 中被逐个读出,重建为动画分段,随后清空原生分段——所以 IB 里看到的静态样式仅供参考,最终视觉效果以代码运行为准。
这一"改名即集成"的设计是它最大的易用性亮点,但也是新手最常见的翻车点(见下节坑位清单)。
4️⃣ 常见坑位排查清单 🔍
| # | 坑点 | 症状 | 解决方案 |
|---|---|---|---|
| 1 | IB 改了类名却没把库加进工程 | 运行时报Unknown class 'SDSegmentedControl',xib 加载崩溃 | 确认 SDSegmentedControl.h / .m 已加入 Target |
| 2 | 未链接 QuartzCore | 编译报undefined symbols(CAShapeLayer 相关) | 在 Build Phases → Link Binary With Libraries 添加QuartzCore(SDSegmentedControl.podspec#L38 已声明该依赖) |
| 3 | UIAppearance 不生效 | 给 UISegmentedControl 设外观代理无效 | 头文件注释已说明:大多数继承的外观选择器会被忽略,只对SDSegmentedControl、SDSegmentView、SDStainView三个类的 appearance 有效(见 SDSegmentedControl.h#L11-L15) |
| 4 | podspec 版本号与 tag 不一致 | 用 CocoaPods 安装到的源码与声明版本对不上 | 该 SDSegmentedControl.podspec 声明 version 1.0.5,实际 tag 却指向 1.0.4,直接引用源码更稳妥 |
| 5 | 高度被重置 | IB 里设置的控件高度不生效 | commonInit会把默认高度固定为 43(SDSegmentedControl.m#L86-L91),仅在高度为 0 时才会跟随父视图(SDSegmentedControl.m#L476-L487),需要自定义高度请在代码中显式设置 frame |
| 6 | 图片分段显示过小 | 图标被缩到 18×18 pt | 超大图片会被自动缩放到imageSize(默认 18×18,SDSegmentedControl.m#L22),可通过SDSegmentView的imageSize外观属性调整 |
| 7 | iOS 13+ 出现弃用警告 | setTitleShadowColor:、titleShadowOffset标红 | UIButton 标题阴影 API 在新系统已弃用,可加警告抑制或按下文建议改造 |
| 8 | 快速连点时动画错乱 | 箭头路径闪烁 | 库内部已用removeAllAnimations+ 完成回调兜底处理(SDSegmentedControl.m#L736-L743),无需额外处理 |
5️⃣ 五分钟上手:集成与主题定制
获取代码
git clone https://gitcode.com/gh_mirrors/sd/SDSegmentedControl基本用法
把两个源文件拖入工程并链接 QuartzCore 后,即可像使用 UISegmentedControl 一样使用:
#import "SDSegmentedControl.h" SDSegmentedControl *seg = [SDSegmentedControl new]; [seg insertSegmentWithTitle:@"Today" atIndex:0 animated:NO]; [seg insertSegmentWithTitle:@"For You" atIndex:1 animated:NO]; seg.selectedSegmentIndex = 1;动态增删分段、启禁用分段、自定义分段宽度的完整示例可参考 Example/Example/ViewController.m。
全局主题定制
通过UIAppearance一次设置、全局生效,官方示例见 Example/Example/ViewController.m#L85-L106:
SDSegmentedControl *appearance = SDSegmentedControl.appearance; appearance.backgroundColor = [UIColor whiteColor]; appearance.arrowSize = 8; // 调大选中箭头 appearance.animationDuration = 0.3; // 调整动画时长6️⃣ 现代化改造建议 💡
这套库停留在 Objective-C 手写 UIKit 的时代,如果要在 2026 年的项目中使用,建议这样取舍:
新项目(推荐不直接使用)
- 纯文本分段:直接用系统
UISegmentedControl+UIAppearanceProxy,iOS 13 之后官方控件的可定制性已今非昔比; - SwiftUI 项目:
Picker的.segmented样式已能满足绝大多数需求,无需用 UIKit 控件桥接。
老项目维护 / 必须复刻箭头风格
- 保留路径动画思路:
CAShapeLayer遮罩 +CABasicAnimation这套方案依然高效,可直接移植到 Swift 重写; - 砍掉双版本分支:如前文所述,
SD_IS_IOS7在现代系统恒为 YES,迁移时可整体删除 iOS 6 样式分支,代码量立刻瘦身; - 替换弃用 API:把 UIButton 标题阴影相关属性去掉,选中态文字差异改用字体/颜色区分即可;
- 补上现代体验:选中时加
UISelectionFeedbackGenerator触感反馈、确认isAccessibilityElement与分段值的无障碍描述; - 参考滚动逻辑:其"选中后自动滚动到可见区域"的实现(SDSegmentedControl.m#L599-L628)在自研可滚动分段控件时值得借鉴。
小结
SDSegmentedControl 用一个极小的代码量(两个源文件)复刻了 iOS 6 App Store 的经典箭头分段标签,"IB 中改个类名即可集成"的设计对新手非常友好。只要记住三件事——链接 QuartzCore、只对三个类的 appearance 生效、IB 类名与工程类必须一致——就能避开 90% 的坑;而在新项目中,建议以系统控件或 SwiftUI 为主,把它的CAShapeLayer箭头动画思路作为自研控件时的参考。
【免费下载链接】SDSegmentedControl
A drop-in remplacement for UISegmentedControl that mimic iOS 6 AppStore tab controls
相关推荐
7.x迁移避坑指南:从6.x到ArduinoJson新版的5个核心改造技巧
7.x迁移避坑指南:从6.x到ArduinoJson新版的5个核心改造技巧 还在为ArduinoJson升级后编译错误发愁?本文整理5个破坏性变更修复方案,帮你
物联网嵌入式sebastian/diff 6.x到7.x迁移指南:兼容性处理技巧
sebastian/diff 6.x到7.x迁移指南:兼容性处理技巧 还在为版本升级后的兼容性问题头疼?本文将带你一文解决sebastian/diff从6.x到
开发工具测试AppIntro 5.x到6.x迁移指南:避坑指南与代码转换技巧
AppIntro 5.x到6.x迁移指南:避坑指南与代码转换技巧 你是否在将AppIntro从5.x升级到6.x时遇到编译错误?本文将系统梳理所有重大变更,提供
移动开发UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考