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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:48:32