RedNote:基于 IGListKit 打造高性能双列瀑布流 Feed 应用

118 阅读4分钟

在移动应用开发中,瀑布流布局因其灵活的视觉呈现方式,成为内容展示类应用的热门选择。今天要向大家推荐一个基于 IGListKit 实现的双列瀑布流 Feed 应用示例 ——RedNote。这个项目不仅展示了现代 iOS 开发的最佳实践,还整合了多种主流第三方库,为开发者提供了一套可直接复用的解决方案。

项目概述

RedNote 是一个模仿小红书风格的 Feed 流应用,采用双列瀑布流布局展示内容卡片,支持动态高度计算、详情页分区展示和下拉刷新等核心功能。项目基于数据驱动的列表架构 IGListKit 构建,结合 SnapKit 进行约束管理,使用 SDWebImage 处理图片加载,通过 MJRefresh 实现刷新功能,整体代码结构清晰,易于扩展。

效果

image.png

核心功能特性

RedNote 具备以下关键功能,满足了现代 Feed 流应用的基本需求:

  • 双列瀑布流布局:根据内容动态计算高度,实现错落有致的视觉效果
  • 智能图片处理:按真实宽高比计算图片显示区域,避免拉伸变形
  • 标题优化展示:标题最多显示两行,超出部分自动省略
  • 结构化详情页:按 Header/Content/Author/Comments 清晰分区展示内容
  • 下拉刷新机制:集成 MJRefresh 实现流畅的刷新体验
  • 数据驱动架构:基于 IGListKit 实现高效的列表数据管理

技术栈解析

RedNote 的技术选型体现了 iOS 开发中的主流方案,值得学习和借鉴:

  1. IGListKit:由 Instagram 开源的数据驱动列表框架,解决了传统 UITableView/UICollectionView 在处理大量动态数据时的性能问题,通过 Diff 算法实现高效的 UI 更新。
  2. SnapKit:简洁易用的 AutoLayout 封装库,让约束编写变得直观高效,极大提升了界面布局代码的可读性和可维护性。
  3. SDWebImage:功能强大的图片加载框架,支持异步加载、缓存管理、渐进式加载等特性,确保图片展示的流畅性。
  4. MJRefresh:轻量级的下拉刷新框架,提供了丰富的刷新动画和自定义选项,集成简单快捷。
  5. 自定义 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!