2. Refresh the interface data by listening to the property observer of the ViewModel, and invoke closure 3. Finally, the View/ViewController completes all UI interactions
Train of thought
The function of closures to control the interaction process
typealias Nothing = ()->()
var reloadTableViewClosure: Nothing?
var showAlertClosure: Nothing?Copy the code
Use the property viewer to trigger the invocation of the closure to make the UI respond
var cellViewModels: [CellViewModel] = [CellViewModel]() {
didSet {
reloadTableViewClosure?()
}
}
var alertMessage: String? {
didSet {
showAlertClosure?()
}
}Copy the code
The implementation process
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] inDispatchQueue.main.async { self? .tableview.reloadData() } } viewmodel.showAlertClosure = { [weak self]in
DispatchQueue.main.async {
if letmessage = 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 {
return160}}Copy the code
ViewModel
import UIKit
struct CellViewModel {
let titleText: String
let descText: String
let imageUrl: String
letdateText: String } typealias Nothing = (()->()) class ViewModel: {var reloadTableViewClosure: Nothing? NSObject {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 leterror = 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 = "Click \ (index) row)"
}
}
func processFetchedPhoto(photos: [Photo]) {
var num = [CellViewModel]()
for photo inphotos { num.append(createCellViewModel(photo: Photo))} // Refresh tableView cellViewModels = num} Instead, it uses the ViewModel to take over data and interact with the View layer. 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"
returnCellViewModel( titleText: photo.name, descText: desc, imageUrl: photo.image_url, dateText: dateFormatter.string(from: Photo. Created_at))}}Copy the code
GitHubDemo: github.com/marst123/ta…