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

如何在UICollectionViewController(Swift 4)中实现UISearchBarController?

在UICollectionViewController中集成UISearchController的完整实现

我来帮你把这个搜索功能的实现补全并梳理清楚关键细节,结合你给出的代码片段,整理出可直接运行的完整方案:

1. 完整代码示例

import UIKit

// 先假设你的RecommendArticles模型结构(如果实际结构不同,按需调整即可)
struct RecommendArticles {
    let title: String
    // 可以添加其他属性,比如subtitle、imageURL等
}

class FirstViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating {
    
    // 原始数据源:存放所有文章标题
    var titles: [String] = []
    // 原始模型数组
    var card: [RecommendArticles]?
    
    // 搜索过滤后的数据源:实时更新并刷新列表
    var searchArr: [String] = [] {
        didSet {
            self.collectionView?.reloadData()
        }
    }
    
    // 声明搜索控制器实例
    private let searchController = UISearchController(searchResultsController: nil)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初始化搜索控制器
        setupSearchController()
        // 模拟初始化原始数据(实际项目中替换为接口请求或本地数据)
        initOriginalData()
    }
    
    // MARK: - 初始化原始数据
    private func initOriginalData() {
        card = [
            RecommendArticles(title: "SwiftUI从入门到精通"),
            RecommendArticles(title: "UICollectionView布局实战"),
            RecommendArticles(title: "UISearchController最佳实践"),
            RecommendArticles(title: "iOS性能优化技巧")
        ]
        // 从模型数组中提取标题到titles数组
        titles = card?.map { $0.title } ?? []
        // 初始化搜索数组为全部数据
        searchArr = titles
    }
    
    // MARK: - 配置搜索控制器
    private func setupSearchController() {
        // 设置搜索结果更新代理为当前控制器
        searchController.searchResultsUpdater = self
        // 搜索时是否隐藏导航栏(这里设置为false,保持导航栏可见)
        searchController.hidesNavigationBarDuringPresentation = false
        // 搜索时不模糊背景,让用户仍能看到下方的列表
        searchController.obscuresBackgroundDuringPresentation = false
        // 设置搜索栏占位符文本
        searchController.searchBar.placeholder = "搜索文章标题..."
        // 将搜索栏集成到导航栏
        navigationItem.searchController = searchController
        // 确保搜索控制器的展示范围仅在当前控制器内,避免影响其他页面
        definesPresentationContext = true
    }
    
    // MARK: - UISearchResultsUpdating 代理方法(搜索文本变化时触发)
    func updateSearchResults(for searchController: UISearchController) {
        guard let searchText = searchController.searchBar.text?.trimmingCharacters(in: .whitespacesAndNewlines), !searchText.isEmpty else {
            // 搜索框为空时,恢复显示全部数据
            searchArr = titles
            return
        }
        // 执行过滤逻辑:忽略大小写匹配,提升用户体验
        searchArr = titles.filter {
            $0.lowercased().contains(searchText.lowercased())
        }
    }
    
    // MARK: - UICollectionView 数据源方法
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // 根据搜索状态返回对应数据源的数量
        return searchController.isActive ? searchArr.count : titles.count
    }
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ArticleCell", for: indexPath)
        // 复用cell前清空旧内容(实际项目建议用自定义cell,这里为了演示简化)
        cell.contentView.subviews.forEach { $0.removeFromSuperview() }
        
        // 获取当前要显示的标题
        let currentTitle = searchController.isActive ? searchArr[indexPath.item] : titles[indexPath.item]
        
        // 添加标签显示标题
        let titleLabel = UILabel(frame: cell.contentView.bounds.inset(by: UIEdgeInsets(top: 8, left: 12, bottom: 8, right: 12)))
        titleLabel.text = currentTitle
        titleLabel.numberOfLines = 0
        titleLabel.textColor = .black
        cell.contentView.addSubview(titleLabel)
        
        // 给cell加个边框,方便看效果
        cell.layer.borderWidth = 1
        cell.layer.borderColor = UIColor.lightGray.cgColor
        cell.layer.cornerRadius = 8
        
        return cell
    }
    
    // MARK: - UICollectionViewDelegateFlowLayout 布局方法
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        // 设置cell宽度为屏幕宽度减左右边距,高度固定为80
        return CGSize(width: collectionView.bounds.width - 24, height: 80)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        // 设置section的内边距
        return UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // 注册cell(实际项目建议在viewDidLoad中注册)
        collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "ArticleCell")
    }
}

2. 核心细节说明

  • 数据源分离:用titles存原始数据,searchArr存过滤后的数据,通过searchController.isActive判断当前使用哪个数据源,保证搜索和非搜索状态的内容正确切换。
  • 搜索控制器配置:setupSearchController方法里完成了所有基础配置,其中definesPresentationContext = true是关键,它能确保搜索控制器的弹窗不会渗透到其他控制器。
  • 过滤逻辑优化:对搜索文本做了去除空格处理,并且用小写匹配,避免用户输入大小写导致搜索不到的问题;当搜索框清空时自动恢复全部数据。
  • CollectionView适配:在数据源方法中根据搜索状态返回对应的数据量和内容,确保列表能实时响应搜索结果。

3. 进阶建议

  • 使用模型数组作为搜索数据源:如果你的RecommendArticles有更多属性,建议把searchArr改成[RecommendArticles]类型,这样可以在cell中展示更多信息(比如副标题、图片),而不仅仅是标题。
  • 自定义UICollectionViewCell:实际项目中不要在cellForItemAt里重复创建子视图,自定义cell能提升性能和代码可维护性。
  • 添加搜索防抖:如果搜索逻辑比较复杂(比如网络请求搜索),可以添加防抖处理,避免用户输入时频繁触发搜索,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:31:41