要实现 iOS 抖音首页导航栏的“一键分两列”功能(通常指将单列内容切换为双列瀑布流布局),需结合自定义导航栏控件与布局动态切换逻辑。以下是关键实现步骤和技术要点,基于 iOS 原生开发框架(Swift/Objective-C):
一、核心功能需求分析
- 导航栏按钮触发
在导航栏右侧添加功能按钮(如“分列”图标),点击后切换内容布局(单列 ↔ 双列)。 - 内容布局动态切换
内容区域(如UICollectionView
)需支持单列与双列布局的无缝切换。 - 状态持久化
用户退出后再进入时,需保留上次的布局状态。
二、实现步骤与代码示例
1. 自定义导航栏按钮
- 在
viewDidLoad
中添加导航栏按钮,并绑定点击事件:// Swift 示例 let layoutButton = UIBarButtonItem(image: UIImage(named: "grid_icon"), style: .plain, target: self, action: #selector(toggleLayout)) navigationItem.rightBarButtonItem = layoutButton
2. 动态切换集合视图布局
- 使用
UICollectionViewFlowLayout
动态调整列数:private var isSingleColumn = false // 默认双列 @objc func toggleLayout() { isSingleColumn.toggle() updateCollectionViewLayout() } private func updateCollectionViewLayout() { let layout = UICollectionViewFlowLayout() if isSingleColumn { layout.itemSize = CGSize(width: view.bounds.width, height: 200) // 单列宽度全屏 } else { let columnWidth = (view.bounds.width - 15) / 2 // 双列,考虑间距 layout.itemSize = CGSize(width: columnWidth, height: 300) } collectionView.setCollectionViewLayout(layout, animated: true) }
3. 保存布局状态
- 使用
UserDefaults
持久化用户选择:private func saveLayoutPreference() { UserDefaults.standard.set(isSingleColumn, forKey: "isSingleColumnMode") } // 在 viewDidLoad 中读取状态 override func viewDidLoad() { super.viewDidLoad() isSingleColumn = UserDefaults.standard.bool(forKey: "isSingleColumnMode") updateCollectionViewLayout() }
4. 处理导航栏适配问题
- 状态栏高度适配:全面屏(如 iPhone X 及以上)需额外处理安全区域:
if #available(iOS 11.0, *) { collectionView.contentInsetAdjustmentBehavior = .never let statusBarHeight = UIApplication.shared.statusBarFrame.height collectionView.contentInset = UIEdgeInsets(top: statusBarHeight + 44, left: 0, bottom: 0, right: 0) }
- 导航栏透明效果:若需抖音式全屏沉浸感,隐藏导航栏底部阴影线:
// Objective-C 示例(通过查找导航栏底部分割线并隐藏) - (UIImageView *)findNavBarBottomLine:(UIView *)view { if ([view isKindOfClass:UIImageView.class] && view.bounds.size.height <= 1.0) { return (UIImageView *)view; } for (UIView *subview in view.subviews) { UIImageView *imageView = [self findNavBarBottomLine:subview]; if (imageView) return imageView; } return nil; }
三、技术要点与优化
-
性能优化
- 使用
invalidateLayout()
替代重新创建layout
对象,减少内存开销。 - 复用单元格(
dequeueReusableCell
)避免滚动卡顿。
- 使用
-
动效增强
- 添加布局切换的过渡动画:
UIView.animate(withDuration: 0.3) { self.collectionView.performBatchUpdates({ self.collectionView.setCollectionViewLayout(newLayout, animated: true) }) }
- 添加布局切换的过渡动画:
-
多列布局的间距处理
- 通过
minimumInteritemSpacing
和minimumLineSpacing
控制双列间距:layout.minimumInteritemSpacing = 5 layout.minimumLineSpacing = 10
- 通过
-
次级导航多列支持参考
- 若需实现类似“次级导航多列展开”效果(如电商类目),可参考易营宝的多列次级导航方案:
- 开启自定义导航面板,设置多列样式。
- 通过
列数 = 次级导航数 / 行数
动态计算布局。
- 若需实现类似“次级导航多列展开”效果(如电商类目),可参考易营宝的多列次级导航方案:
四、完整流程示意图
用户点击导航栏按钮
↓
触发 toggleLayout 方法
↓
更新 isSingleColumn 状态
↓
根据状态创建单列/双列 FlowLayout
↓
执行 collectionView.setCollectionViewLayout(animated: true)
↓
保存状态至 UserDefaults
注意事项
- 全面屏适配:iPhone X 及以上机型需额外处理底部
Home Bar
区域(增加safeAreaInsets.bottom
)。 - 全局样式统一:若需与其他页面导航栏样式一致,建议通过
UINavigationBar.appearance()
全局配置。 - 跨版本兼容:iOS 11 后
automaticallyAdjustsScrollViewInsets
失效,需改用contentInsetAdjustmentBehavior
。
可通过调整 UICollectionViewLayout
参数快速实现布局切换,结合状态持久化与动效优化,即可复现抖音式“一键分列”体验。