用MVVM寫一個簡單的tableView項目



1.選擇使用閉包方式,進行數據綁定
2.經過監聽ViewModel的屬性觀察器,刷新接口數據,調用閉包
3.最後View/ViewController完成全部UI交互git

思路

閉包的做用,控制交互過程
github

typealias Nothing = ()->()
var reloadTableViewClosure: Nothing?
var showAlertClosure: Nothing?複製代碼

利用屬性觀察器,觸發調用閉包的時機,使UI作出響應
api

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

實現過程

ViewController
bash

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…app

相關文章
相關標籤/搜索