在 VIPER 中构建 UITableView

问题描述

想象一下我有一个像这样的 viewForHeaderInSection

注意:目前是用 MVP 编写的,我正在尝试将其重构为 VIPER

func tableView(_ tableView: UITableView,viewForHeaderInSection section: Int) -> UIView? {
        
    guard let header = tableView.dequeueReusableheaderfooterView(withIdentifier: DetailsSectionHeaderView.identifier) as? DetailsSectionHeaderView,let section = PresenterEnum.Sections(rawValue: section),section != .fourth,let presenter = presenter else { 
        return nil 
    }
        
    switch section {
        case .first:
            header.configure(StringConstants.first.localized,showButton: false,isSelected: false)
        case .second:
            let secondarray = presenter.secondarrayCount
            header.configure(StringConstants.second.localized,showButton: secondarray > 3,isSelected: presenter.isExpanded(section.rawValue))
            header.showMoreAction = { [weak self] in
                self?.handleMoreAction(tableView,in: section.rawValue)
            }
        case .third:
            let thirdCount = presenter.thirdArrayCount
            header.configure(StringConstants.third.localized,showButton: thirdArrayCount > 3,isSelected: presenter.isExpanded(section.third))
            header.showMoreAction = { [weak self] in
                self?.handleMoreAction(tableView,in: section.rawValue)
            }
        default:
            return nil
        }
        
    return header
}

private func handleMoreAction(_ tableView : UITableView,in section : Int){
    guard let presenter = presenter else {return}
        
    tableView.beginUpdates()
    let isExpanded = presenter.isExpanded(section)
        
    var indexPaths = [IndexPath]()
        
    for i in presenter.minimumRowCount..<presenter.totalRowCountForSection(section){
        indexPaths.append([section,i])
    }
        
    if isExpanded{
       tableView.deleteRows(at: indexPaths,with: .automatic)
    }else{
       tableView.insertRows(at: indexPaths,with: .automatic)
    }
        
    presenter.updateExpansion(section)
    tableView.endUpdates()
}

UITableViewDelegateUITableViewDataSourceView 的一部分。但它们是被动的,不应真正向 presenter 请求信息。但同时如何显示视图信息属于presenter

根据上面的定义,我无法弄清楚如何重构下面的代码。这似乎不正确,因为关于该部分应该显示什么的逻辑在视图中以及如何处理 showMoreAction

所以我的问题是: 构建上述两种方法以使其更符合 VIPER 标准的“正确”方法是什么?

解决方法

1 创建允许视图和演示者传递数据的协议, 示例:

protocol PresenterProtocol where Self: UIViewController{
   var table: UITableView {get}
}
  1. 在Presenter中创建弱视图控制器引用,符合Presenter协议, 例子:

    class Presenter: NSObject {
    
      weak var viewController: PresenterProtocol{
         didSet{
           connectVC()
         }
      }
    
      func connectVC(){
         self.viewController.table.delegate = self
         self.viewController.table.dataSource = self
      }
    }
    
  2. 现在,当您将 tableview 委托和数据源分配给演示者时,您可以进行扩展,例如:

     extension Presenter: UITableViewDataSource,...
     // cell for row methods .. 
    
  3. 那么你应该改变你的视图(ViewController)

     class ViewController: UIViewController{
         @IBOutlet weak var tableView: UITableView?
         var presenter: Presenter?
    
    
         viewDidLoad(){
            super.viewDidLoad()
            presenter = Presenter()
            presenter.viewController = self
         }
     }
    

所以你将你的 vc 分配给了演示者的 viewController 现在你需要的只是让你的 VC 符合 PresenterProtocol

      extension ViewController: PresenterProtocol{
        var table: UITableView {
            return self.tableView
        }
      }

现在你的 VC 是干净的,所有的逻辑都包含在演示者中,当然可以更容易,但我只是展示了正确的方向