前言
TTGSnackbar 最早只是我用 Swift 写的一个 Android Snackbar 风格提示条:显示一段文案,带一个 Action,几秒后自动消失。
这次整理到 2.0.0,目标变成了一个更完整的 iOS 消息反馈组件:不仅能显示顶部/底部消息,还要支持语义样式、队列管理、自定义 View、Swift concurrency、无障碍、触觉反馈和现代 Apple 平台要求。

Github: https://github.com/zekunyan/TTGSnackbar
更新
这次 2.0.0 可以理解成几个方向的补齐:
- 平台升级:当前开发目标为 Swift 5.9、Xcode 15+、iOS 16.0+。
- 语义样式:内置
.default、.info、.success、.warning、.error、.loading。 - 配置对象:新增
TTGSnackbarConfiguration,更适合现代 Swift 调用。 - Swift Concurrency:可以
await用户点击、滑动、dismiss、展示失败等结果。 - 队列管理:
TTGSnackbarManager支持排队、替换当前、相同消息去重。 - 体验细节:Dynamic Type、VoiceOver、Reduce Motion、haptics、生命周期暂停/恢复计时器。
- 自定义能力:支持自定义内容 View,也可以放到自定义容器里展示。

快速使用
最简单的用法还是没有变:
import TTGSnackbar
let snackbar = TTGSnackbar(message: "TTGSnackbar!", duration: .short)
snackbar.show()

如果消息需要一个恢复入口,比如删除文件后的 Undo:
let snackbar = TTGSnackbar(
message: "File deleted",
duration: .middle,
actionText: "Undo"
) { snackbar in
restoreFile()
snackbar.dismiss()
}
snackbar.show()

这个 API 继续保留,是为了兼容老项目,也因为它确实足够直接。对一个提示控件来说,简单场景不应该被复杂 API 绑架。
语义样式
以前做不同状态的提示,通常是自己设置背景色、文字色、icon。写多了之后就会发现,业务里其实反复出现的是几类固定语义:成功、警告、错误、加载中。
所以新版加了 TTGSnackbarStyle:
let snackbar = TTGSnackbar(message: "Saved successfully", duration: .middle)
snackbar.style = .success
snackbar.show()

加载中的场景也可以直接用 .loading,配合 .forever 手动结束:
let snackbar = TTGSnackbar(message: "Uploading...", duration: .forever)
snackbar.style = .loading
snackbar.actionText = "Cancel"
snackbar.actionBlock = { snackbar in
cancelUpload()
snackbar.dismiss()
}
snackbar.show()

Configuration API
属性式配置比较直观,但是当参数多起来以后,会出现“创建对象、设置一堆属性、最后 show”的散落代码。2.0.0 里增加了 TTGSnackbarConfiguration,把一次展示需要的信息收拢到一个 value object:
let snackbar = TTGSnackbar(configuration: .init(
message: "File deleted",
duration: .long,
style: .warning,
actionText: "Undo",
actionBlock: { snackbar in
undoDelete()
snackbar.dismiss()
}
))
snackbar.show()
这不是为了替代旧 API,而是给复杂一点的调用场景一个更稳定的入口。比如产品里做统一消息中心、错误处理器、网络状态提示时,用 configuration 传递会清楚很多。
async / await
Snackbar 其实是一个临时 UI,但它经常和业务决策有关:用户点了 Undo 还是直接忽略?展示失败要不要降级?被队列丢弃算不算完成?
新版可以直接等待结果:
let result = await TTGSnackbar.show(configuration: .init(
message: "File deleted",
duration: .long,
style: .warning,
actionText: "Undo"
))
switch result {
case .action:
undoDelete()
case .dismissed:
break
case .dropped:
print("A manager policy dropped the snackbar")
case .failedToPresent:
print("No window or container was available")
default:
break
}
这块实现上用的是 continuation,把 action、second action、tap、swipe、dismiss、manager drop、presentation failure 都统一成 TTGSnackbarResult。调用方不用再在几个 block 之间跳来跳去,读起来会顺很多。
自定义内容
有些提示不只是“一行文字 + 一个按钮”,比如同步状态、表单错误、网络诊断,就需要自己塞一个 View 进去:
let contentView = UIStackView()
contentView.axis = .vertical
contentView.spacing = 4
contentView.isLayoutMarginsRelativeArrangement = true
contentView.layoutMargins = UIEdgeInsets(top: 16, left: 18, bottom: 16, right: 18)
let titleLabel = UILabel()
titleLabel.text = "Custom content view"
contentView.addArrangedSubview(titleLabel)
let snackbar = TTGSnackbar(customContentView: contentView, duration: .forever)
snackbar.shouldActivateLeftAndRightMarginOnCustomContentView = true
snackbar.show()

这个设计里,库继续负责安全区域、展示位置、动画和 dismiss,内容内部怎么布局交给业务。边界要分清,不然控件会越来越像一个小页面 =。=
总结
TTGSnackbar 2.0.0 最大的变化不是“样式变好看了”,而是从一个简单提示条,补成了一个更适合现代 iOS 项目的反馈组件。
简单场景仍然可以一行 show();复杂场景可以用 configuration、manager 和 async result 做统一管理。这个平衡比较重要:提示控件要轻,但也不能在真正接到业务流程时马上露怯。