如何基于MVC架构正确清理重复的UISearchController配置代码?
现有实现的问题与优化方案
你当前实现的核心问题
- 类继承冗余:
SearchBarManager继承UIViewController完全无实际必要,该类本身不需要参与视图层级、生命周期管理,也不需要作为界面跳转的承载节点,创建这类无用实例只会增加不必要的内存开销。 - 存在逻辑bug:代码中
definesPresentationContext = true这行操作的是SearchBarManager自己的实例属性,而非实际承载搜索界面的目标控制器(你的TableViewController/CollectionViewController),会导致UISearchController的上下文匹配异常,出现搜索界面消失逻辑不符合预期的问题。 - 代理绑定错位风险:原代码中把搜索栏的delegate设为当前控制器,在独立的Manager类中无法正确绑定目标控制器为代理,会导致搜索栏的输入、点击等事件回调全部失效。
- 职责边界错位:从MVC分层看,导航项绑定搜索控制器、设置控制器本身的展示上下文属于视图控制器的固有职责,强行把这部分逻辑抽成独立的视图类,反而打乱了原本的分层逻辑。
更轻量化的简化方案
最适配当前场景的方式是通过UIViewController扩展抽取公共重复逻辑,不需要额外创建管理类:
- 首先封装所有重复出现的通用配置,把差异化参数做可选传入:
extension UIViewController { /// 通用UISearchController基础配置 func configureBaseSearchController( _ searchController: UISearchController, placeholder: String, scopeTitles: [String]? = nil, defaultScopeIndex: Int = 0 ) { // 两个控制器完全重复的公共逻辑 searchController.obscuresBackgroundDuringPresentation = false searchController.searchBar.placeholder = placeholder navigationItem.searchController = searchController navigationItem.hidesSearchBarWhenScrolling = false // 这里的self直接指向实际的目标控制器,完全匹配原代码的行为 definesPresentationContext = true // 差异化可选配置 if let scopeTitles = scopeTitles { searchController.searchBar.scopeButtonTitles = scopeTitles searchController.searchBar.selectedScopeButtonIndex = defaultScopeIndex } } }
- 原有控制器中的调用代码可以简化为单行动态参数传入:
- 相册列表(原TableViewController)中直接替换原配置方法:
configureBaseSearchController(searchController, placeholder: "Search Albums") // 保留控制器自身专属的代理绑定逻辑 searchController.searchBar.delegate = self - 照片列表(原CollectionViewController)中直接替换原配置方法:
configureBaseSearchController( searchController, placeholder: "Search Photos", scopeTitles: ["1 Column", "2 Columns", "3 Columns"], defaultScopeIndex: 2 ) // 同样保留自身专属的代理绑定逻辑 searchController.searchBar.delegate = self
- 相册列表(原TableViewController)中直接替换原配置方法:
方案优势
- 完全规避原实现的逻辑bug:
definesPresentationContext、搜索栏代理都正确绑定到实际展示页面的控制器,行为和原手写代码100%一致 - 公共逻辑只维护一份:后续如果需要统一调整搜索控制器的公共配置(比如修改滚动时隐藏搜索栏的策略、统一调整搜索栏外观样式),只需要修改扩展内的一处代码,不需要逐个修改每个控制器
- 不破坏MVC职责边界:这部分配置本身就属于视图控制器管理自身导航视图的职责,扩展封装没有出现职责越界
- 无额外内存开销:不需要创建独立的Manager实例,所有方法直接在目标控制器上调用
如果后续搜索的业务处理逻辑(比如输入文本过滤、scope切换后的列表刷新)也出现跨控制器重复,再考虑把代理逻辑抽成独立的工具类即可,纯配置类的重复逻辑用扩展抽取是成本最低、最易维护的方案。
内容的提问来源于stack exchange,提问作者Nico Cobelo
相关产品推荐
相关产品推荐

