MVVM+RxSwift簡單實踐

一、簡要說明

  • 使用MVVM去解耦model、view,提升複用的可能性,同時極大減輕了controller的負擔,經過中間件viewModel處理數據源、綁定UI、處理邏輯事件。這樣當各類業務冗雜在一塊兒的時候,各業務之間的代碼能夠徹底分離,使代碼清晰。
  • 使用RxSwift,能夠替換掉原有的dataSource,delegate等代理方法,作到異步 Event(事件)序列的響應式編程,方便快捷,而且邏輯更加清晰。
  • 代碼中使用了優秀的開源庫:KingfisherSnapKit

二、運行效果

Simulator Screen Shot - iPhone 7 - 2018-07-12 at 18.17.12.png

三、目錄結構

image.pngimage.png

四、頁面代碼

  • controller
//
//  MainViewController.swift
//  ss
//
//  Created by WES319 on 11/7/18.
//  Copyright © 2018年 周元素. All rights reserved.
//

import UIKit
import RxCocoa
import RxSwift
import SnapKit

class MainViewController: UIViewController {

    var tableView = MainTableView()
    var tableViewModel: MainTableViewModel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.title = "遊戲"
        
        tableViewModel = MainTableViewModel.init(target: self)
        
        view.addSubview(tableView)
        tableView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
    }
}
  • Model
//
//  ProductModel.swift
//  ss
//
//  Created by WES319 on 12/7/18.
//  Copyright © 2018年 周元素. All rights reserved.
//

import UIKit

class ProductModel: NSObject {

    var image: String
    var title: String
    var content: String
    
    required init(image: String, title: String, content: String) {
        self.image = image
        self.title = title
        self.content = content
        super.init()
    }
 
}
  • View
//
//  MainTableView.swift
//  ss
//
//  Created by WES319 on 11/7/18.
//  Copyright © 2018年 周元素. All rights reserved.
//

import UIKit

class MainTableView: UITableView {

    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        
        self.tableFooterView = UIView()
        self.estimatedRowHeight = 100
        self.register(MainTableViewCell.self, forCellReuseIdentifier: MainTableViewCell.cellID)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
//
//  MainTableViewCell.swift
//  ss
//
//  Created by WES319 on 11/7/18.
//  Copyright © 2018年 周元素. All rights reserved.
//

import UIKit
import Kingfisher

class MainTableViewCell: UITableViewCell {

    static let cellID = NSStringFromClass(MainTableViewCell.self)
    
    var mainImageView = UIImageView()
    var titleLabel = UILabel()
    var contentLabel = UILabel()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.separatorInset = UIEdgeInsetsMake(0, 15, 0, 0)
        
        contentView.addSubview(mainImageView)
        mainImageView.snp.makeConstraints { (make) in
            make.top.left.bottom.equalToSuperview().inset(15)
            make.height.equalTo(80).priority(999)
            make.width.equalTo(mainImageView.snp.height).multipliedBy(16.0/9.0)
        }
        
        titleLabel.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.black)
        contentView.addSubview(titleLabel)
        titleLabel.snp.makeConstraints { (make) in
            make.left.equalTo(mainImageView.snp.right).offset(10)
            make.top.equalTo(mainImageView)
            make.right.lessThanOrEqualToSuperview().inset(10)
        }
        
        contentLabel.textColor = UIColor.gray
        contentLabel.font = UIFont.systemFont(ofSize: 13, weight: UIFont.Weight.light)
        contentLabel.numberOfLines = 2
        contentView.addSubview(contentLabel)
        contentLabel.snp.makeConstraints { (make) in
            make.left.equalTo(titleLabel)
            make.top.equalTo(titleLabel.snp.bottom).offset(5)
            make.right.lessThanOrEqualToSuperview().inset(15)
        }
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func update(image: String, title: String, content: String) {
        mainImageView.kf.setImage(with: URL.init(string: image))
        titleLabel.text = title
        contentLabel.text = content
    }
}
  • ViewModel
//
//  MainTableViewModel.swift
//  ss
//
//  Created by WES319 on 11/7/18.
//  Copyright © 2018年 周元素. All rights reserved.
//

import UIKit
import RxSwift

class MainTableViewModel: NSObject {
    
    var dataSource = Observable.just([ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。")
        ,ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。"),ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。"),ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。"),ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。"),ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。"),ProductModel.init(image: "http://pic1.win4000.com/wallpaper/d/5279ae86ae5a6.jpg", title: "仙途", content: "一萬年前,妖星蔽日,須彌山中的魔界封印失效。魔帝伺機率領魔軍涌入人界,在須彌山頂創建天幻城。一時之間生靈塗炭,火光沖天。濃重的妖魔鬼氣蔓延至高高在上的仙佛二界。衆仙家佛祖意識到如若人間淪陷,仙、佛二界也不免血光之災,故如來佛祖與太上老君,聯合仙佛二界高手與魔軍會戰於人間。這場戰爭最終以魔帝的失敗了結,魔帝、魔軍、天幻城被打回魔界,魔界入口被從新封印。")])
    weak var target: MainViewController!
    let disposeBag = DisposeBag()
    
    required init(target: MainViewController) {
        self.target = target
        super.init()
        
        // 綁定數據
        self.dataSource.bind(to: target.tableView.rx.items(cellIdentifier: MainTableViewCell.cellID)) { (_, model, cell: MainTableViewCell) in
            cell.update(image: model.image, title: model.title, content: model.content)
        }.disposed(by: disposeBag)
        
        // tableView點擊事件
        self.target.tableView.rx.modelSelected(ProductModel.self).subscribe(onNext: { (model) in
            print(model.title)
        }).disposed(by: disposeBag)
        self.target.tableView.rx.itemSelected.subscribe(onNext: { (indexPath) in
            print(indexPath.row)
            self.target.tableView.deselectRow(at: indexPath, animated: true)
        }).disposed(by: disposeBag)
    }
}
相關文章
相關標籤/搜索