用MVVM写一个简单的tableView项目

1,143 阅读2分钟



1.选择使用闭包方式,进行数据绑定
2.通过监听ViewModel的属性观察器,刷新接口数据,调用闭包
3.最后View/ViewController完成所有UI交互

思路

闭包的作用,控制交互过程

typealias Nothing = ()->()
var reloadTableViewClosure: Nothing?
var showAlertClosure: Nothing?

利用属性观察器,触发调用闭包的时机,使UI做出响应

var cellViewModels: [CellViewModel] = [CellViewModel]() {
	didSet {
		reloadTableViewClosure?()
	}
}
var alertMessage: String? {
	didSet {
		showAlertClosure?()
	}
}

实现过程

ViewController

import UIKit
import SDWebImage
class ViewController: UIViewController {

    @IBOutlet weak var tableview: UITableView!
    lazy var viewmodel: ViewModel = {
        return ViewModel()
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        createui()
        createvm()
    }
    func createui() {
        self.view.backgroundColor = .black
        tableview.register(UINib(nibName: "ViewCell", bundle: nil), forCellReuseIdentifier: "viewcell")
    }
    func createvm() {
        
        viewmodel.reloadTableViewClosure = { [weak self] in
            DispatchQueue.main.async {
                self?.tableview.reloadData()
            }
        }
        viewmodel.showAlertClosure = { [weak self] in
            DispatchQueue.main.async {
                if let message = self?.viewmodel.alertMessage {
                    self?.showAlert( message )
                }
            }
        }
        
        viewmodel.initData()
    }
    func showAlert( _ message: String ) {
        let alert = UIAlertController(title: "Alert", message: message, preferredStyle: .alert)
        alert.addAction( UIAlertAction(title: "Ok", style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}

//MARK: TableViewDelegate
extension ViewController: UITableViewDelegate,UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewmodel.numberCells
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "viewcell", for: indexPath) as? ViewCell else {
            fatalError("Cell not exists in storyboard")
        }
        cell.config = viewmodel.dataViewModel(indexPath)

        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        viewmodel.promptMessage(indexPath)
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 160
    }
}

ViewModel

import UIKit
struct CellViewModel {
    let titleText: String
    let descText: String
    let imageUrl: String
    let dateText: String
}
typealias Nothing = (()->())
class ViewModel: NSObject {
    //整体思路就是通过属性观察器和闭包实现数据的双向绑定,在viewModel中处理逻辑
    //使用闭包来控制交互过程
    var reloadTableViewClosure: Nothing?
    var showAlertClosure: Nothing?
    //
    var cellViewModels: [CellViewModel] = [CellViewModel]() {
        didSet {
            reloadTableViewClosure?()
        }
    }
    var alertMessage: String? {
        didSet {
            showAlertClosure?()
        }
    }
    //
    var numberCells: Int {
        return cellViewModels.count
    }
    
    func initData(api: APIService = APIService()) {
        api.fetchPopularPhoto { [weak self] (success, photos, error) in
            if let error = error {
                self?.alertMessage = error.rawValue
            } else {
                self?.processFetchedPhoto(photos: photos)
            }
        }
    }
    
    func dataViewModel(_ index: IndexPath) -> CellViewModel {
        return cellViewModels[index.row]
    }

    func promptMessage(_ index: IndexPath) {
        if index.row%2 == 0 {
            alertMessage = "点击\(index.row)"
        }
    }

    func processFetchedPhoto(photos: [Photo]) {
        var num = [CellViewModel]()
        for photo in photos {
            num.append(createCellViewModel(photo: photo))
        }
        //刷新tableview
        cellViewModels = num
    }
    
    //这样的处理是为了保证model不参与直接交互,而是采用viewmodel接管数据进行view层的交互
    func createCellViewModel( photo: Photo ) -> CellViewModel {
        
        //Wrap a description
        var descTextContainer: [String] = [String]()
        if let camera = photo.camera {
            descTextContainer.append(camera)
        }
        if let description = photo.description {
            descTextContainer.append( description )
        }
        let desc = descTextContainer.joined(separator: " - ")
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        
        return CellViewModel( titleText: photo.name,
                                       descText: desc,
                                       imageUrl: photo.image_url,
                                       dateText: dateFormatter.string(from: photo.created_at) )
    }  
}

GitHubDemo: github.com/marst123/ta…