You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift:如何在导航栏标题位置添加UICollectionView(仿iOS短信应用)

看起来你正在尝试复刻iOS短信App的导航栏标题横向滚动效果,这个思路非常靠谱!我帮你补全并优化代码,同时点明几个关键细节,让你的实现更贴近原生效果:

在UINavigationBar中添加UICollectionView模仿Messages应用

1. 补全并优化初始化代码

先修正你代码里的拼写小错误,同时完善初始化逻辑(重点解决布局依赖的问题):

// 如果是在导航控制器中,建议用已有的导航栏而不是新建
let navigationBar = self.navigationController?.navigationBar ?? UINavigationBar()
var collectionView: UICollectionView!
let layout = UICollectionViewFlowLayout()

// 短信App是横向滚动标题,所以设置滚动方向为水平
layout.scrollDirection = .horizontal
layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) // 匹配原生左右边距
layout.minimumInteritemSpacing = 8 // 标题之间的间距

// 初始化CollectionView,先给个临时frame,后续用自动布局约束
collectionView = UICollectionView(frame: navigationBar.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = .clear // 透明背景和导航栏融合
collectionView.showsHorizontalScrollIndicator = false // 隐藏滚动条,和原生一致
collectionView.isScrollEnabled = true

// 添加到导航栏并设置自动布局(*这步非常关键*,避免frame依赖导致的布局错乱)
navigationBar.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    collectionView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor),
    collectionView.trailingAnchor.constraint(equalTo: navigationBar.trailingAnchor),
    collectionView.topAnchor.constraint(equalTo: navigationBar.topAnchor),
    collectionView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)
])

2. 核心功能实现(数据源、代理与样式)

要让这个CollectionView真正工作,还需要实现数据源、代理,并复刻原生的选中样式:

注册Cell与设置代理

在viewDidLoad中添加:

// 自定义Cell的话可以替换成你的自定义类
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "TitleCell")
collectionView.dataSource = self
collectionView.delegate = self

实现数据源方法

extension YourViewController: UICollectionViewDataSource {
    // 替换成你的标题数组
    let titleList = ["消息", "置顶", "最近删除"]
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return titleList.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TitleCell", for: indexPath)
        cell.contentView.subviews.forEach { $0.removeFromSuperview() } // 避免复用重复添加
        
        // 添加标题标签
        let titleLabel = UILabel(frame: cell.contentView.bounds)
        titleLabel.text = titleList[indexPath.item]
        titleLabel.textAlignment = .center
        titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
        titleLabel.textColor = indexPath.item == collectionView.indexPathsForSelectedItems?.first?.item ? .systemBlue : .label
        cell.contentView.addSubview(titleLabel)
        
        // 模仿原生选中时的蓝色下划线
        let underline = UIView(frame: CGRect(x: 0, y: cell.bounds.height - 2, width: cell.bounds.width, height: 2))
        underline.backgroundColor = .systemBlue
        underline.isHidden = indexPath.item != collectionView.indexPathsForSelectedItems?.first?.item
        cell.contentView.addSubview(underline)
        
        return cell
    }
}

实现代理方法(动态计算Item宽度+选中逻辑)

extension YourViewController: UICollectionViewDelegateFlowLayout {
    // 根据标题文本长度动态计算Item宽度,比固定尺寸更贴合原生
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let title = titleList[indexPath.item]
        let textWidth = title.size(withAttributes: [.font: UIFont.systemFont(ofSize: 17, weight: .semibold)]).width
        return CGSize(width: textWidth + 32, height: navigationBar.frame.height) // 加上左右内边距
    }
    
    // 选中标题时的逻辑:切换选中状态+滚动到居中位置
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
        collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems) // 更新选中样式
    }
}

3. 适配优化细节

  • 屏幕旋转适配:监听屏幕旋转,刷新布局避免Item尺寸错乱:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    coordinator.animate(alongsideTransition: { _ in
        self.collectionView.collectionViewLayout.invalidateLayout()
    })
}
  • 自定义Cell封装:建议把标题标签和选中样式封装成自定义UICollectionViewCell,避免在数据源方法里重复写UI代码。

内容的提问来源于stack exchange,提问作者Alex Ritter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:06:18