Swift协议设计:复用视图控制器多数据源实现可行吗?
方案完全可行,这是iOS开发中复用视图控制器的经典实践
这个思路本质上是基于协议的依赖注入,通过抽象协议统一不同数据源的接口,让SpotViewController只依赖协议而非具体实现,完美实现多页面复用的需求。下面是具体的实现步骤和细节:
1. 定义共享协议
首先我们需要创建一个继承自UITableViewDataSource的协议,同时声明要求实现的spots和title属性。这样既保证了数据源必须满足UITableView的基本要求,又统一了视图控制器需要的额外属性:
import UIKit // 假设Spot是你的数据模型类 struct Spot { let title: String let location: String } protocol SpotDataSourceProtocol: UITableViewDataSource { var spots: [Spot] { get } var title: String { get } }
2. 实现两个数据源类
接下来让PersonalSpotDataSource和ExploreSpotDataSource分别遵循这个协议,各自实现业务逻辑:
PersonalSpotDataSource(个人收藏数据源)
class PersonalSpotDataSource: SpotDataSourceProtocol { var spots: [Spot] var title: String = "我的收藏" init(spots: [Spot]) { self.spots = spots } // 实现UITableViewDataSource的核心方法 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { spots.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "PersonalSpotCell", for: indexPath) as! PersonalSpotCell let spot = spots[indexPath.row] cell.titleLabel.text = spot.title cell.subtitleLabel.text = "已收藏" return cell } }
ExploreSpotDataSource(发现页数据源)
class ExploreSpotDataSource: SpotDataSourceProtocol { var spots: [Spot] var title: String = "发现好去处" init(spots: [Spot]) { self.spots = spots } // 可以根据业务需求实现不同的UITableView逻辑 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { spots.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ExploreSpotCell", for: indexPath) as! ExploreSpotCell let spot = spots[indexPath.row] cell.spotTitle.text = spot.title cell.locationLabel.text = spot.location return cell } }
3. 修改SpotViewController适配协议类型
把原来的具体类型SpotDataSource替换成我们定义的协议SpotDataSourceProtocol,让视图控制器可以接收任何遵循该协议的数据源实例:
class SpotViewController: UIViewController { // 使用协议类型代替具体类,实现解耦 var dataSource: SpotDataSourceProtocol! @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() // 绑定tableView的数据源 tableView.dataSource = dataSource // 通过协议获取标题,无需关心具体数据源 title = dataSource.title // 注册对应cell(根据业务需求,也可以在数据源中处理) tableView.register(PersonalSpotCell.self, forCellReuseIdentifier: "PersonalSpotCell") tableView.register(ExploreSpotCell.self, forCellReuseIdentifier: "ExploreSpotCell") } // 刷新数据的方法,同样通过协议交互 func refreshSpots() { tableView.reloadData() } }
4. 使用示例
在需要创建视图控制器的地方,只需传入对应数据源即可:
// 创建个人收藏页面 let personalSpots = [Spot(title: "街角咖啡店", location: "朝阳区"), Spot(title: "老巷书店", location: "西城区")] let personalDataSource = PersonalSpotDataSource(spots: personalSpots) let personalVC = SpotViewController() personalVC.dataSource = personalDataSource // 创建发现页面 let exploreSpots = [Spot(title: "网红泰餐店", location: "海淀区"), Spot(title: "城市中央公园", location: "东城区")] let exploreDataSource = ExploreSpotDataSource(spots: exploreSpots) let exploreVC = SpotViewController() exploreVC.dataSource = exploreDataSource
方案优势
- 解耦性强:视图控制器不再依赖具体的数据源类,只和协议交互,降低代码耦合度
- 复用性高:同一个
SpotViewController可以在个人页、发现页甚至其他页面复用,只需更换数据源 - 扩展性好:后续新增其他类型的数据源(比如推荐页),只需创建新类遵循协议即可,无需修改视图控制器代码
内容的提问来源于stack exchange,提问作者LoganHenderson
相关产品推荐
相关产品推荐

