PolarisGuideKit:轻量、低侵入的 iOS 新手引导组件(遮罩挖孔 + Buddy View + 插件化)
关键词:UIKit · 新手引导 · 低侵入 · 插件扩展
GitHub:github.com/noodles1024...

背景:我为什么做这个组件?
可能是新手引导这个功能太小,随便实现一下也能用,导致没有人愿意认真写一个iOS下的新手引导组件,搜遍整个github也找不到一个在现实项目中能直接拿来用的。如果只考虑某一个具体的新手引导界面,实现起来很容易(特别是现在在AI的加持下,UI仔都不需要了)。但在不同项目、不同场景下,经过和和产品经理&设计师的多次沟通中,我发现了做"新手引导/功能提示"时的一些令人头疼的问题:
- 需要高亮某个控件,但布局变化、屏幕旋转后挖孔(高亮)位置容易偏
- 指引说明(箭头/气泡/按钮)形态不固定,可能还伴随着音频播放等附加功能,复用困难
- 点击高亮区域时,难以做到不侵入原有点击业务逻辑
- 显示新手引导时难以在不改变原有逻辑的情况下阻止NavigationController的滑动返回
- UITableView/UICollectionView
reloadData后高亮经常失效
于是我做了 PolarisGuideKit :一个基于 UIKit 的轻量新手引导组件,主打低侵入 + 可扩展 + 动态高亮。
PolarisGuideKit 能解决什么?
| 能力 | 说明 | 带来的价值 |
|---|---|---|
| 高亮遮罩 | 遮罩挖孔高亮 focusView | 高亮区域自动跟随,内置高亮效果,可自定义 |
| Buddy View | 说明视图可自由定制 | 文案、箭头、按钮任意组合 |
| 步骤编排 | 多步骤引导流程 | 支持下一步、跳过、完成 |
| 动态 focusView | reloadData 后自动修正 | TableView/CollectionView场景稳定 |
| 插件化扩展 | Audio/埋点/持久化 | 可插拔、解耦 |
快速上手(3 分钟接入)
swift
import UIKit
import PolarisGuideKit
final class MyViewController: UIViewController {
private var guide: GuideController?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let step = GuideStep()
step.focusView = myButton
step.buddyView = MyBuddyView()
step.forwardsTouchEventsToFocusView = true
step.completer = ControlEventCompleter(control: myButton, event: .touchUpInside)
let controller = GuideController(hostView: view, steps: [step])
controller.onDismiss = { _, context in
print("引导结束,原因 = \(context.reason)")
}
_ = controller.show()
guide = controller
}
}
核心概念一图速览
- GuideController:流程编排器,负责 show/hide/切换步骤
- GuideStep:一步引导配置(focus、buddy、style、completer)
- FocusStyle:高亮形状(矩形/圆形/圆角/无高亮)
- GuideBuddyView:说明视图(可继承自定义)
- GuidePlugin:生命周期扩展(音频/埋点/持久化)
重点能力拆解
1) FocusStyle:高亮样式可拔插
内置样式包含:
DefaultFocusStyle(矩形)RoundedRectFocusStyle(圆角矩形)CircleFocusStyle(圆形)NoHighlightFocusStyle(全屏遮罩)
swift
let step = GuideStep()
step.focusView = someCard
step.focusStyle = RoundedRectFocusStyle(
focusCornerRadius: .followFocusView(delta: 2),
focusAreaInsets: UIEdgeInsets(top: -6, left: -6, bottom: -6, right: -6)
)
2) 动态 FocusView:Table/CollectionView不卡壳
UITableView / UICollectionView 复用导致高亮错位?
使用 focusViewProvider 动态获取最新 cell:
swift
let step = GuideStep()
step.focusViewProvider = { [weak self] in
guard let self else { return nil }
var cell = self.tableView.cellForRow(at: targetIndexPath)
if cell == nil {
self.tableView.layoutIfNeeded()
cell = self.tableView.cellForRow(at: targetIndexPath)
}
return cell
}
3) 触摸转发 + 自动完成
在不侵入原有业务逻辑的前提下,高亮按钮依然能触发业务逻辑,同时自动关闭引导:
swift
let step = GuideStep()
step.focusView = myButton
step.forwardsTouchEventsToFocusView = true
step.completer = ControlEventCompleter(control: myButton, event: .touchUpInside)
✅ 设置forwardsTouchEventsToFocusView和completer保证了"引导不侵入原有业务逻辑"。
4) Buddy View:说明视图随便做
继承 GuideBuddyView,自定义 UI + 布局:
swift
final class MyBuddyView: GuideBuddyView {
override func updateLayout(referenceLayoutGuide layoutGuide: UILayoutGuide, focusView: UIView) {
super.updateLayout(referenceLayoutGuide: layoutGuide, focusView: focusView)
// 根据 layoutGuide 布局你的文案 / 按钮 / 箭头
}
}
5) 插件系统:音频 / 埋点 / 持久化
内置 AudioGuidePlugin,可在显示引导时播放音频文件,且可在BuddyView中配合显示音频播放动画(可选功能):
swift
let step = GuideStep()
step.focusView = myCard
step.addAttachment(GuideAudioAttachment(url: audioURL, volume: 0.8))
let controller = GuideController(
hostView: view,
steps: [step],
plugins: [AudioGuidePlugin()]
)
如果想要加埋点、标记"引导是否已显示",可通过自定义
GuidePlugin实现。
Demo 示例一览
- 圆角矩形高亮 + 圆角模式切换
- 圆形高亮 + 半径缩放
- 多步骤引导 + 平滑转场
- 触摸转发 + 自动完成
- 点击外部关闭(dismissesOnOutsideTap)
- 音频 + Lottie 同步演示
- UITableView 动态高亮
架构 & 视图层级
flowchart TB
subgraph Core["核心组件"]
GuideController["GuideController
(流程编排器)"] GuideStep["GuideStep
(步骤配置)"] end subgraph ViewHierarchy["视图层级"] GuideContainerView["GuideContainerView
(透明容器)"] GuideOverlayView["GuideOverlayView
(遮罩 + 触摸转发)"] MaskOverlayView["MaskOverlayView
(遮罩基类)"] GuideBuddyView["GuideBuddyView
(说明视图)"] GuideShadowView["GuideShadowView
(焦点追踪器)"] end subgraph Extensions["扩展机制"] FocusStyle["FocusStyle
(高亮形状)"] GuideAutoCompleter["GuideAutoCompleter
(完成触发器)"] GuidePlugin["GuidePlugin
(生命周期钩子)"] GuideStepAttachment["GuideStepAttachment
(插件数据)"] end GuideController -->|"管理"| GuideStep GuideController -->|"创建并承载"| GuideContainerView GuideController -->|"派发事件"| GuidePlugin GuideContainerView -->|"包含"| GuideOverlayView GuideContainerView -->|"包含"| GuideBuddyView GuideOverlayView -.->|"继承"| MaskOverlayView GuideOverlayView -->|"创建"| GuideShadowView GuideOverlayView -->|"使用"| FocusStyle GuideStep -->|"配置"| GuideBuddyView GuideStep -->|"使用"| FocusStyle GuideStep -->|"通过...触发"| GuideAutoCompleter GuideStep -->|"携带"| GuideStepAttachment
(流程编排器)"] GuideStep["GuideStep
(步骤配置)"] end subgraph ViewHierarchy["视图层级"] GuideContainerView["GuideContainerView
(透明容器)"] GuideOverlayView["GuideOverlayView
(遮罩 + 触摸转发)"] MaskOverlayView["MaskOverlayView
(遮罩基类)"] GuideBuddyView["GuideBuddyView
(说明视图)"] GuideShadowView["GuideShadowView
(焦点追踪器)"] end subgraph Extensions["扩展机制"] FocusStyle["FocusStyle
(高亮形状)"] GuideAutoCompleter["GuideAutoCompleter
(完成触发器)"] GuidePlugin["GuidePlugin
(生命周期钩子)"] GuideStepAttachment["GuideStepAttachment
(插件数据)"] end GuideController -->|"管理"| GuideStep GuideController -->|"创建并承载"| GuideContainerView GuideController -->|"派发事件"| GuidePlugin GuideContainerView -->|"包含"| GuideOverlayView GuideContainerView -->|"包含"| GuideBuddyView GuideOverlayView -.->|"继承"| MaskOverlayView GuideOverlayView -->|"创建"| GuideShadowView GuideOverlayView -->|"使用"| FocusStyle GuideStep -->|"配置"| GuideBuddyView GuideStep -->|"使用"| FocusStyle GuideStep -->|"通过...触发"| GuideAutoCompleter GuideStep -->|"携带"| GuideStepAttachment

安装
Swift Package Manager
- Xcode → File → Add Packages...
- 输入仓库地址:
https://github.com/noodles1024/PolarisGuideKit - 选择 PolarisGuideKit
CocoaPods
ruby
pod 'PolarisGuideKit'
swift
import PolarisGuideKit
注意事项(踩坑清单)
focusView必须是hostView的子视图- 多 Scene / 多 Window 建议显式传
hostView GuideAutoCompleter触发后会结束整个引导(建议用于最后一步)- 动画转场在复杂形状下可关闭动画:
animatesStepTransition = false
项目地址 & 交流
- GitHub:github.com/noodles1024...
- Issues / PR:欢迎一起完善
- 如果觉得有帮助,欢迎 Star ⭐️