在移动应用开发中,瀑布流布局因其灵活的视觉呈现方式,成为内容展示类应用的热门选择。今天要向大家推荐一个基于 IGListKit 实现的双列瀑布流 Feed 应用示例 ——RedNote。这个项目不仅展示了现代 iOS 开发的最佳实践,还整合了多种主流第三方库,为开发者提供了一套可直接复用的解决方案。
项目概述
RedNote 是一个模仿小红书风格的 Feed 流应用,采用双列瀑布流布局展示内容卡片,支持动态高度计算、详情页分区展示和下拉刷新等核心功能。项目基于数据驱动的列表架构 IGListKit 构建,结合 SnapKit 进行约束管理,使用 SDWebImage 处理图片加载,通过 MJRefresh 实现刷新功能,整体代码结构清晰,易于扩展。
效果
核心功能特性
RedNote 具备以下关键功能,满足了现代 Feed 流应用的基本需求:
- 双列瀑布流布局:根据内容动态计算高度,实现错落有致的视觉效果
- 智能图片处理:按真实宽高比计算图片显示区域,避免拉伸变形
- 标题优化展示:标题最多显示两行,超出部分自动省略
- 结构化详情页:按 Header/Content/Author/Comments 清晰分区展示内容
- 下拉刷新机制:集成 MJRefresh 实现流畅的刷新体验
- 数据驱动架构:基于 IGListKit 实现高效的列表数据管理
技术栈解析
RedNote 的技术选型体现了 iOS 开发中的主流方案,值得学习和借鉴:
- IGListKit:由 Instagram 开源的数据驱动列表框架,解决了传统 UITableView/UICollectionView 在处理大量动态数据时的性能问题,通过 Diff 算法实现高效的 UI 更新。
- SnapKit:简洁易用的 AutoLayout 封装库,让约束编写变得直观高效,极大提升了界面布局代码的可读性和可维护性。
- SDWebImage:功能强大的图片加载框架,支持异步加载、缓存管理、渐进式加载等特性,确保图片展示的流畅性。
- MJRefresh:轻量级的下拉刷新框架,提供了丰富的刷新动画和自定义选项,集成简单快捷。
- 自定义 WaterfallLayout:针对双列瀑布流场景定制的布局管理器,能够根据内容动态计算每个 item 的高度和位置。
核心代码解析
数据模型设计
FeedModel 作为核心数据模型,不仅包含了内容的基本信息,还实现了 ListDiffable 协议,为 IGListKit 提供数据比对能力:
class FeedModel: NSObject, ListDiffable {
let id: String
let title: String
let content: String
let imageURL: String
let imageWidth: CGFloat
let imageHeight: CGFloat
// 其他属性...
// 计算图片的宽高比,用于动态布局
var imageAspectRatio: CGFloat {
guard imageWidth > 0 else { return 1.2 }
return imageHeight / imageWidth
}
// ListDiffable协议实现
func diffIdentifier() -> NSObjectProtocol {
return id as NSObjectProtocol
}
func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
guard let object = object as? FeedModel else { return false }
return id == object.id &&
title == object.title &&
content == object.content &&
likeCount == object.likeCount &&
commentCount == object.commentCount
}
}
瀑布流布局实现
自定义的 WaterfallLayout 通过代理模式获取每个 item 的高度,实现动态布局:
// FeedSectionController中实现WaterfallLayoutDelegate
func waterfallLayout(_ layout: WaterfallLayout, heightForItemAt indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat {
guard let feedModel = feedModel else { return 250 }
// 计算图片高度
let imageHeight = itemWidth * feedModel.imageAspectRatio
// 计算标题高度(最多两行)
let titleWidth = itemWidth - 16 // 减去左右内边距
let titleFont = UIFont.systemFont(ofSize: 14, weight: .medium)
let titleHeight = feedModel.title.height(withConstrainedWidth: titleWidth, font: titleFont, maxLines: 2)
// 固定元素高度(作者信息栏)
let footerHeight: CGFloat = 40
// 总高度 = 图片高度 + 标题高度 + 间距 + 底部信息栏高度
return imageHeight + titleHeight + 16 + footerHeight
}
详情页分区展示
详情页采用 IGListKit 的多 SectionController 架构,将不同类型的内容分离管理:
// 详情页数据模型
enum DetailSectionType {
case header, content, author, comments
}
class DetailSectionModel: NSObject, ListDiffable {
let type: DetailSectionType
let feedModel: FeedModel?
let comments: [CommentModel]?
// 实现ListDiffable协议...
}
// 详情页数据源实现
func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
guard let sectionModel = object as? DetailSectionModel else {
return ListSectionController()
}
switch sectionModel.type {
case .header:
return DetailHeaderSectionController()
case .content:
return DetailContentSectionController()
case .author:
return DetailAuthorSectionController()
case .comments:
return DetailCommentsSectionController()
}
}
总结
RedNote 项目展示了如何将多个优秀的第三方库有机结合,构建一个高性能、易维护的瀑布流 Feed 应用。通过 IGListKit 的数据驱动架构,配合自定义的瀑布流布局,实现了流畅的内容展示体验。无论是新手学习 iOS 瀑布流实现,还是资深开发者寻找可复用的 Feed 流解决方案,RedNote 都是一个值得深入研究的项目。
项目地址:github.com/linghugoogl…,欢迎 Star 和 Fork!