动画解密:SwipeViewController中SelectionBar跟随滑动与按钮颜色渐变的实现原理

发布时间:2026/8/17 17:24:21
动画解密:SwipeViewController中SelectionBar跟随滑动与按钮颜色渐变的实现原理 动画解密SwipeViewController中SelectionBar跟随滑动与按钮颜色渐变的实现原理【免费下载链接】SwipeViewControllerSwipeViewController is a Swift modification of RKSwipeBetweenViewControllers - navigate between pages / ViewControllers项目地址: https://gitcode.com/gh_mirrors/sw/SwipeViewControllerSwipeViewController 是一个基于 Swift 的开源滑动切换组件它让你只需左右轻扫就能在多个页面ViewController之间自由切换同时导航栏上的按钮与底部 SelectionBar选中指示条会跟随手指实时联动。很多开发者第一次见到这套丝滑动画时都会好奇指示条为什么能精准追着手指跑按钮颜色渐变又是怎么算出来的本文就带你逐行拆解 SwipeViewController 动画背后的完整实现原理看懂后你也能在自己项目里复刻这套交互。SwipeViewController 是什么一个会滑的导航容器 SwipeViewController 是经典 Objective-C 项目 RKSwipeBetweenViewControllers 的 Swift 重构版它把「顶部滑动按钮 底部选中指示条 页面左右滑动」整合进一个继承自UINavigationController的容器中使用体验类似 App Store 的榜单页或今日头条的栏目页。它的核心能力可以概括为四点手势滑动切页手指左右滑动即可在多个 ViewController 间翻页SelectionBar 实时跟随底部选中指示条随滑动进度同步位移按钮颜色联动当前选中按钮与普通按钮颜色自动切换双模式按钮既支持文字标题也支持图片按钮SwipeButtonWithImage.swift整个动画的核心只有一个入口——scrollViewDidScroll回调几乎所有魔法都发生在这里。下面我们就从架构开始一步步揭开它的面纱。动画核心架构UINavigationController 与 UIPageViewController 的分工 要理解动画原理先要搞清楚 SwipeViewController 的内部结构类声明见 SwipeViewController.swift外层容器SwipeViewController本身继承UINavigationController负责承载导航栏与页面栈内层翻页器内部持有一个UIPageViewControllertransitionStyle 为.scroll负责真正的翻页动画关键 Trick拿到pageController内部的UIScrollView并把它的delegate指向 SwipeViewController 自己见 delegate 设置代码pageController.delegate self pageController.dataSource self if let scrollView pageController.view.subviews.compactMap({ $0 as? UIScrollView }).first { scrollView.delegate self // 关键接管滚动回调 }这一步是整个动画的基石只要手指在页面上滑动scrollViewDidScroll就会被系统高频调用而 SelectionBar 的坐标、按钮的颜色都在这个回调里基于滚动偏移量实时计算。也就是说动画并不是定时器驱动的而是完全由用户手指的位移驱动所以手感才会那么跟手。SelectionBar 跟随滑动原理scrollViewDidScroll 驱动的比例动画 SelectionBar 跟随滑动的完整逻辑位于 scrollViewDidScroll整个过程可以拆成三步第一步计算 xFromCenter —— 手指偏离中心的距离let xFromCenter view.frame.width - scrollView.contentOffset.x这个值代表当前页面相对屏幕中心偏移了多少。左滑为负、右滑为正绝对值最大等于一屏宽度此时翻页完成。第二步计算 newRatio —— 把一屏宽度映射成按钮间距SelectionBar 要从当前按钮平滑走到下一个按钮需要知道手指滑动一屏时指示条应该移动多少像素。这个换算比例就是newRatio// equalSpaces等间距模式下 let expression 2 * x button.frame.width - (selectionBarWidth - nextButton.frame.width) / 2 newRatio view.frame.width / (expression - (x - (selectionBarWidth - button.frame.width) / 2))简单理解它用两按钮中心之间的距离作分母、屏幕宽度作分子得出一个位移映射系数。等间距与不等间距两种布局对应equalSpaces属性的 true/false分别有独立的公式但思想完全一致。第三步更新 SelectionBar 坐标selectionBar.frame CGRect(x: selectionBarOriginX - (xFromCenter / newRatio), ...)指示条每帧的 X 坐标 按钮基准位置 − 手指偏移量 ÷ 映射系数。这样指示条就会严格按比例跟随手指移动滑动结束时恰好停在下一个按钮正下方视觉上就像被手指拖着走。三种滑动状态下的表现手指动作xFromCenterSelectionBar 行为向右滑动看下一页负值变小指示条向右匀速跟随向左滑动回上一页正值变大指示条向左匀速跟随滑动一半松手回弹绝对值 屏宽指示条同步回弹到原按钮按钮颜色渐变的实现原理区间判定 选中状态切换 按钮颜色变化的核心逻辑在 changeButtonColor它在每次滚动回调中都会被调用前提是buttonColor ! selectedButtonColor见 调用条件。它的策略是以屏幕中点为分界线把滑动过程切成四个区间每个区间决定谁被点亮、谁被熄灭区间xFromCenter滑动方向变为选中色的按钮恢复普通色的按钮屏宽/2 ~ 屏宽左滑上一页即将到达上一个按钮当前按钮0 ~ 屏宽/2右滑当前页仍选中当前按钮上一个按钮−屏宽/2 ~ 0左滑当前页仍选中当前按钮下一个按钮−屏宽 ~ −屏宽/2右滑下一页即将到达下一个按钮当前按钮if viewWidthHalf ... border ~ xFromCenter, currentPageIndex 0 { let button buttons[currentPageIndex - 1] let previousButton buttons[currentPageIndex] button.titleLabel?.textColor selectedButtonColor previousButton.titleLabel?.textColor buttonColor ... }这里有两个值得注意的细节渐变其实是分段切换颜色并不是逐帧插值过渡而是在滑动过半的那一刻一次性切换。之所以看起来像渐变是因为 SelectionBar 一直在连续移动视觉焦点被带走了颜色切换便显得自然流畅isSelected 状态同步切换代码中同时维护了button.isSelected这对图片按钮至关重要——正常图与选中图如 Hearts / YellowHearts 两套图标会随选中状态自动替换按钮颜色的最终呈现还依赖两个属性的联动buttonColor控制普通按钮颜色selectedButtonColor控制选中按钮颜色它们的didSet观察器会在赋值瞬间刷新全部按钮见 buttonColor 与 selectedButtonColor。点击按钮跳页switchTabs 如何判断左右方向 除了滑动点击导航栏按钮同样能触发翻页动画这部分由 switchTabs 与 scrollToNextViewController 协作完成方向判断把按钮的tag - 1当作目标索引与currentPageIndex比较——目标在右则向前翻在左则向后翻逐页播放从当前页到目标页之间逐页调用setViewControllers保证中间页面也参与动画例如从第 1 页直接点第 3 页第 2 页会一闪而过防抖保护animationFinished与finalPageIndex两个标志位用于防止快速连点导致的动画冲突保证一次只播放一段动画如何调出丝滑效果常用动画参数配置清单 ⚙️看完原理你可以在自己的项目里直接通过公开属性定制动画效果参数作用默认值selectionBarHeight/selectionBarWidth指示条尺寸0需自行设置selectionBarColor指示条颜色黑色buttonColor普通按钮颜色黑色selectedButtonColor选中按钮颜色绿色buttonFont按钮字体系统 18 号equalSpaces是否等间距布局trueoffset/bottomOffset指示条边距40 / 0一个最小可用的定制示例swipeViewController.selectionBarHeight 3 swipeViewController.selectionBarColor .systemBlue swipeViewController.selectedButtonColor .systemBlue swipeViewController.buttonColor .gray swipeViewController.equalSpaces true如果想要图片按钮只需用SwipeButtonWithImage提供正常图、选中图和尺寸参考 SwipeButtonWithImage.swift再赋给buttonsWithImages属性即可选中态图片会自动跟随页面切换。完整的布局计算可以继续阅读 updateButtonsLayout 与 updateSelectionBarFrame。总结一套完整滑动交互的三个关键点 ✅回顾整个 SwipeViewController 动画实现其实只有三个核心接管滚动回调把UIPageViewController内部滚动视图的 delegate 交给外层控制器让动画与手指位移天然同步比例映射用newRatio把一屏滑动距离换算成按钮间距SelectionBar 由此实现精准跟随区间状态机以屏幕中点为界把滑动分成四个区间配合isSelected状态切换完成按钮颜色与图标的联动这套滚动回调 比例映射 状态判定的组合拳几乎可以复用到任何指示条跟随 标签联动的场景。想亲手调试每一步动画直接打开 SwipeViewController.swift 源码在scrollViewDidScroll里打断点观察 xFromCenter 与 selectionBar.frame 的变化你会对这套原理有更直观的感受。【免费下载链接】SwipeViewControllerSwipeViewController is a Swift modification of RKSwipeBetweenViewControllers - navigate between pages / ViewControllers项目地址: https://gitcode.com/gh_mirrors/sw/SwipeViewController创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考