iOS 中的标记配置模式

476 阅读2分钟

iOS 开发中,有一种常用的模式,标记配置,mark & configure

步骤是:

  • 标记, mark

  • 状态恢复,reset

  • 配置, configure

本文通过三个例子,来说明

前两个例子,是选中状态

例子一,按钮点击

三个按钮,选中一个,其余两个取消选中

在这里插入图片描述

常规做法

操作指定,reset 剩余

代码的维护成本大

class ViewController: UIViewController {
    
    @IBOutlet weak var lhs: StateBtn!
    
    @IBOutlet weak var mid: StateBtn!
    
    @IBOutlet weak var rhs: StateBtn!


    
    func setup(){
        
        lhs.addTarget(self, action: #selector(btnOne), for: .touchUpInside)
        mid.addTarget(self, action: #selector(btnTwo), for: .touchUpInside)
        rhs.addTarget(self, action: #selector(btnThree), for: .touchUpInside)
    }
    
    
    @objc
    func btnOne(){
        lhs.beSelected = true
        mid.beSelected = false
        rhs.beSelected = false
    }
    
    
    @objc
    func btnTwo(){
        lhs.beSelected = false
        mid.beSelected = true
        rhs.beSelected = false
    }

    
    @objc
    func btnThree(){
        lhs.beSelected = false
        mid.beSelected = false
        rhs.beSelected = true
    }
}


模式后

统一重置后,再操作

代码维护成本,有减轻


@objc
    func btnOne(){
        resetAll()
        lhs.beSelected = true
        
    }
    
    
    @objc
    func btnTwo(){
        resetAll()
        mid.beSelected = true
    }

    
    @objc
    func btnThree(){
        resetAll()
        rhs.beSelected = true
    }
    
    
    func resetAll(){
        lhs.beSelected = false
        mid.beSelected = false
        rhs.beSelected = false
    }

例子二, table View

更加明显一些

三个 cell ,选中一个,其余两个 cell 取消选中

在这里插入图片描述

常规做法:

操作在 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) ,

选中后,捞出前一个 cell, 捞出现在选中的 cell,

对两个 cell 修改状态

可能的问题是,捞出前一个 cell, 崩掉

因为可能前一个 cell 不在屏幕上,进入了复用池,没有啦

现在选中的 cell, 肯定在屏幕上,肯定存在


class ViewController{
    @IBOutlet weak var table: UITableView!
    func setup(){
        table.register(UINib(nibName: TableCell.k, bundle: nil), forCellReuseIdentifier: TableCell.k)
        table.delegate = self
        table.dataSource = self
        table.rowHeight = 60
        table.reloadData()
    }
    
    
    
}



extension ViewController: UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 操作上一个
        if let last = lastSelectedItem{
            guard last != indexPath.row else{
                return
            }
            if let cell = tableView.cellForRow(at: IndexPath(row: last, section: 0)) as? TableCell{
                cell.beSelected = false
            }
        }
        // 操作当前
        if let cell = tableView.cellForRow(at: indexPath) as? TableCell{
            cell.beSelected = true
        }
        lastSelectedItem = indexPath.row
    }
    
    
}


extension ViewController: UITableViewDataSource{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }
    
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let item = tableView.dequeueReusableCell(withIdentifier: TableCell.k, for: indexPath)
        return item
    }
    
    
}


模式后

之前记录的是上一次选择的 row, 这里记录的是当前选中的 row

逻辑更加简单

分两个阶段处理,

  • 选中代理中,记录,并刷新列表

  • 刷新列表,会调用配置方法,

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

里面根据数据,选择状态

没有上一步的隐患

效果增强,就用 deep diff

class ViewController: UIViewController {
    
    @IBOutlet weak var table: UITableView!
    
    var selectedItem: Int? = nil
    
}

extension ViewController: UITableViewDelegate{
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedItem = indexPath.row
        tableView.reloadData()
    }
    
    
}


extension ViewController: UITableViewDataSource{
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }
    
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let item = tableView.dequeueReusableCell(withIdentifier: TableCell.k, for: indexPath)
        if let cel = item as? TableCell{
            if let row = selectedItem, row == indexPath.row{
                cel.beSelected = true
            }
            else{
                cel.beSelected = false
            }
        }
        return item
    }
    
    
}


例子三,MBProgressHUD

bar 进度条

- (void)barDeterminateExample {
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];

    // 标记
    hud.mode = MBProgressHUDModeDeterminateHorizontalBar;
}

标记后,触发


- (void)setMode:(MBProgressHUDMode)mode {
    if (mode != _mode) {
        _mode = mode;
        [self updateIndicators];
    }
}

走更新


- (void)updateIndicators {
	 if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
        // reset 原来的
        [indicator removeFromSuperview];
        // 使用新的
        indicator = [[MBBarProgressView alloc] init];
        [self.bezelView addSubview:indicator];
    }

  // 二次标记
  [self setNeedsUpdateConstraints];
  
}

重新做布局

#pragma mark - Layout

- (void)updateConstraints {


   // ...
}

github repo