news 2026/8/23 11:55:42

Core Data与CollectionView UI实时同步:CompositionalDiffablePlayground Jokes示例收藏、上下文菜单与骨架屏动画完整实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Core Data与CollectionView UI实时同步:CompositionalDiffablePlayground Jokes示例收藏、上下文菜单与骨架屏动画完整实现

Core Data与CollectionView UI实时同步:CompositionalDiffablePlayground Jokes示例收藏、上下文菜单与骨架屏动画完整实现

【免费下载链接】CompositionalDiffablePlayground.iosExamples showing Compositional Layout, Diffable Data Sources and more!项目地址: https://gitcode.com/gh_mirrors/co/CompositionalDiffablePlayground.ios

CompositionalDiffablePlayground.ios 是一个专门演练 iOS 现代 CollectionView API 的示例工程,核心演示 Compositional Layout(组合式布局)与 Diffable Data Source(可差分数据源)。本文以其中功能最完整的Jokes(笑话列表)示例为主线,拆解Core Data 与 CollectionView UI 实时同步的完整机制,涵盖收藏持久化、长按上下文菜单(朗读/取消收藏)与骨架屏闪烁动画的实现思路,帮助新手理解 Diffable、Compositional 与 Core Data 三者在真实项目中如何协作。

项目概览:现代 CollectionView API 的练习场 🎯

主界面 ViewController.swift 本身也是一个由 Diffable 驱动的 CollectionView,菜单分为两区:

  • Layout 基础示例:列表、简单网格、懒加载网格、吸顶头部、响应式布局、Onboarding 流程等 8 种纯布局玩法;
  • 复杂实战示例:Jokes 笑话列表、Apple Photos 风格画廊(见下方项目自带示例图片)、Instagram 风格个人主页、Indie App 新闻流等。

其中 Jokes 示例是唯一同时串联了网络加载 + Core Data 收藏 + 上下文菜单 + 语音朗读 + 骨架屏的完整场景,因此值得单独拆解。

Core Data 与 CollectionView UI 如何保持实时同步

整个同步链路只由四个角色完成,核心思想是:UI 永远只由 snapshot 驱动,而 snapshot 又永远只从 Core Data 和网络的最新结果构建

角色文件职责
数据库单例Database.swift持有NSPersistentContainer,暴露viewContext,封装saveContext()
数据模型Joke.swiftJoke: NSManagedObject,字段为 id、setup、punchline、created
实体定义contents可视化数据模型中的Joke实体
同步桥JokesViewController.swiftNSFetchedResultsController监听数据库变化

关键在 JokesViewController.swift 末尾这个只有两行的 delegate:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) { datasource.apply(self.snapshot(), animatingDifferences: true) }

只要 Core Data 中Joke记录发生增、删、改,NSFetchedResultsController就会回调它;而apply(_:animatingDifferences:)会自动对比新旧快照,只重绘变化的行,并附带原生插入/移除动画。收藏或删除一条记录,UI 在同一个 runloop 内自动刷新——这就是“实时同步”的全部秘密。

一键收藏:快照驱动的 UI 更新 ⭐

收藏入口在 JokesViewController.swift 的两个方法中:

  • favoriteJoke(_:):先用Joke.byIdFetchRequest(id:)查询是否已存在,防止重复收藏,再新建Joke对象写入并saveContext()
  • unfavoriteJoke(withId:):按 id 取出记录后context.delete(_:),同样触发保存。

snapshot()方法每次都直接从fetchedResultsController.fetchedObjects读取最新的收藏列表,构建出favoriteJokes(count:state:)分节——节标题显示Favorites (n),并可通过ActionButtonHeader按钮整体展开/折叠。

两个细节值得注意:

  1. Joke.Diffablehash(into:)只合并id(见 Joke.swift),保证 Diffable 算法能正确识别“同一条笑话”,内容变化不会误判为增删;
  2. JokeCell.swift 根据数据来源切换星形图标:来自网络的显示空心星(可收藏),来自 Core Data 的显示实心星(已收藏),一眼区分两种状态。

长按上下文菜单:把笑话读出来 🎙️

长按菜单由 JokesViewController.swift 的contextMenuConfigurationForItemAt实现,逻辑简洁:

  • 先通过datasource.itemIdentifier(for:)反查当前 item,骨架屏占位项(.loading)直接返回 nil 不显示菜单
  • 固定提供Hear动作:用AVSpeechSynthesizer朗读setup + punchline
  • 仅当 item 属于收藏分节时,额外追加Unfavorite动作,调用前面提到的删除逻辑。

由于删除会走 Core Data 保存 → FRC 回调 →apply(snapshot)的全链路,菜单里的“取消收藏”同样享受自动 diff 动画,无需手写任何刷新代码。

![CollectionView 骨架屏加载示例的项目内置头像示例图](https://raw.gitcode.com/gh_mirrors/co/CompositionalDiffablePlayground.ios/raw/7bbc7612548ffe6d8b11906fdf376ccdd0987153/CompositionalDiffablePlayground/CompositionalDiffablePlayground/Assets.xcassets/mockAvatar.imageset/Screenshot 2021-01-16 at 14.25.55.png?utm_source=gitcode_repo_files)

骨架屏闪烁动画:20 行代码做出 shimmer 效果 ⚡

数据未返回时,snapshot()会向.jokes分节填入 8 个Item.loading(UUID)占位项(每个 UUID 独立,避免复用同一身份),cell 分支中调用showLoading()(见 JokeCell.swift):

  1. 创建一个CAGradientLayer,颜色为透明 → 10% 黑色 → 透明,宽度放大 3 倍并左移,作为“光带”;
  2. CABasicAnimation动画locations属性从[0.0, 0.1, 0.2][0.8, 0.9, 1.0]duration = 1.1repeatCount = .infinity,得到循环扫过的 shimmer 效果;
  3. prepareForReuse()中执行shimmerLayer?.removeFromSuperlayer()防止 cell 复用时残留动画

这套“占位 item + 渐变光带动画 + 复用时清理”的组合,可以直接迁移到任何 Diffable 列表。

关键文件路径速查表

功能模块文件路径
笑话列表控制器(快照/FRC 同步/上下文菜单)ViewControllers/JokesViewController.swift
骨架屏 shimmer 动画单元格Views/JokeCell.swift
网络笑话 DTOModel/DTO/JokeDTO.swift
共享协议(setup/punchline)Protocols/JokeProtocol.swift
Core Data 实体与排序请求Database/Joke.swift
持久层单例Database/Database.swift
实体模型文件Database.xcdatamodel/contents
网络请求(official-joke-api)Networking/SimpleNetworkHelper.swift
通用排序请求扩展Database/Managed.swift

获取项目并运行 🚀

执行以下命令克隆仓库,用 Xcode 打开工程,选择 iOS 13+ 模拟器运行即可(Jokes 需要网络访问笑话 API):

git clone https://gitcode.com/gh_mirrors/co/CompositionalDiffablePlayground.ios

运行后在主页第二个分节点击Jokes list即可体验完整效果:进入时先看到 8 格 shimmer 骨架屏,加载完成后逐行动画填充;点星形收藏会实时出现在顶部 Favorites 分节;长按任意一条笑话可朗读或取消收藏。对照文中四个核心文件阅读源码,是掌握Core Data 与 CollectionView 实时同步的最佳路径。

【免费下载链接】CompositionalDiffablePlayground.iosExamples showing Compositional Layout, Diffable Data Sources and more!项目地址: https://gitcode.com/gh_mirrors/co/CompositionalDiffablePlayground.ios

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 11:51:22

数学建模实战:线性回归的核心假设、特征工程与模型诊断全解析

1. 从“拍脑袋”到“算数据”&#xff1a;线性回归在数学建模中的真实定位 如果你参加过数学建模比赛&#xff0c;或者看过一些优秀论文&#xff0c;可能会发现一个有趣的现象&#xff1a;很多看起来高大上的问题&#xff0c;最后都“回归”到了一个看似简单的模型——线性回归…

作者头像 李华
网站建设 2026/8/23 11:51:15

Vortigern 样式方案拆解:CSS Modules + PostCSS-Assets 完整配置指南

Vortigern 样式方案拆解&#xff1a;CSS Modules PostCSS-Assets 完整配置指南 【免费下载链接】vortigern A universal boilerplate for building web applications w/ TypeScript, React, Redux, Server Side Rendering and more. 项目地址: https://gitcode.com/gh_mirro…

作者头像 李华