JFDouYu-Swift(二)

今天 先分享下MVVM

image.png

上图描述了MVVM一个基本结构,看到了什么,是不是发现比MVC架构中多了一个ViewModel,没错,就是这个ViewModel,他是MVVM相对于MVC改进的核心思想。在开发过程中,由于需求的变更或添加,项目的复杂度越来越高,代码量越来越大,此时我们会发现MVC维护起来有些吃力,首先被人吐槽的最多的就是MVC的简写变成了Massive-View-Controller(意为沉重的Controller)
由于Controller主要用来处理各种逻辑和数据转化,复杂业务逻辑界面的Controller非常庞大,维护困难,所以有人想到把Controller的数据和逻辑处理部分从中抽离出来,用一个专门的对象去管理,这个对象就是ViewModel,是Model和Controller之间的一座桥梁。当人们去尝试这种方式时,发现Controller中的代码变得非常少,变得易于测试和维护,只需要Controller和ViewModel做数据绑定即可,这也就催生了MVVM的热潮。

MVVM值得用么?

个人非常推荐使用,并且可以直接在你现有的MVC基础上进行扩展,我们首先来看下优缺点
优点:
1.Controller清晰简洁: ViewModel分离了大部分Controller代码,更加清晰和容易维护。

2.方便测试:开发中大部分Bug来至于逻辑处理,由于ViewModel分离了许多逻辑,可以对ViewModel构造单元测试。
3.开发解耦(举两个例子):
a.一人负责逻辑实现、另一人负责UI实现;
b.敏捷开发时,会发经常发不是等后端做好了接口我们再去开发,不过在没有接口的情况下通常我们可以把Controller和View完成。
缺点:
1.看起来代码会比MVC多点
2.需要对每个Controller实现绑定,如果处理不好,反而会有一种“画虎不成反类犬”的感觉

总结

在我实际使用过程中,MVVM写出的代码量并不比MVC的少,有时反而还会多点,毕竟多了一个数据绑定过程,但逻辑会清晰很多,对于多人开发的团队,还是有不少优势的,缺点和优点相比不值一提,总之推荐使用

[图片上传中...(image.png-7c4251-1600679073207-0)]

接下来举个例子

定义一个ViewModel

/// 可以不继承 : NSObject 使这个 JFGameViewModel 更轻量级
class JFGameViewModel{
    //懒加载 定义一个模型数组 用来保存数据 给控制器 或者外部使用
    lazy var gameModels:[JFGameModel] = [JFGameModel]()
}

extension JFGameViewModel{
     func requestGameData( finishCallBack:@escaping ()->()){
//        let parameters = ["shortName" : "game"]
        JFNetworkTool.requestData(type: .GET, urlString: "http://capi.douyucdn.cn/api/v1/getColumnDetail") { (response) in
            
            guard let response = response as? [String:Any] else { return }
                     
            // as? [String:NSObject] 转成数组 并且数组是字典类型
            guard let dataArray = response["data"] as? [[String:Any]] else { return}
            
            for dict in dataArray{
                self.gameModels.append(JFGameModel(dict: dict))
            }
            finishCallBack()
        }
    }
}

在这个viewmodel里面做网络请求,并懒加载一个模型数据暴露给外界(大部分情况是要给Controller持有的)

在控制器中懒加载一个ViewModel 并持有这个ViewModel

    fileprivate lazy var gameViewModel:JFGameViewModel =  JFGameViewModel()
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return gameViewModel.gameModels.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: KGameCellID, for: indexPath) as! JFCollectionGameCell
        let gameModel = gameViewModel.gameModels[indexPath.item]
        // cell.group = gameModel
        // Cannot assign value of type 'JFGameModel' to type 'AnchorGroup' 模型不匹配

        cell.baseGame = gameModel
        return cell
    }

使用viewModel中的数据 这样VC里面所有的数据都会由ViewModel去请求和加工 使VC更加清爽

image.png
image.png
 fileprivate lazy var topHeaderView:JFCollectionHeaderView = {
        let topView = JFCollectionHeaderView.collectionHeaderView()
        topView.frame = CGRect(x: 0, y: -(KHeaderViewH + KGameViewH), width: kScreenWidth, height: KHeaderViewH)
        topView.titleLabel.text = "常用"
        topView.iconImageView.image = UIImage(named: "Img_orange")
        topView.moreBtn.isHidden = true
        return topView
    }()

懒加载头部的视图

 override func setupUI(){
        
        contentView = collectionView
        
        super.setupUI()
        view.addSubview(collectionView)

        collectionView.addSubview(topHeaderView)

        collectionView.addSubview(gameView)

        collectionView.contentInset = UIEdgeInsets(top: KHeaderViewH + KGameViewH, left: 0, bottom: 0, right: 0)
        
    }

headerView添加在collectionView上
因为头部视图要随着collectionView的滚动而滚动

collectionView.contentInset = UIEdgeInsets(top: KHeaderViewH + KGameViewH, left: 0, bottom: 0, right: 0)

目的是计算好headerView的大小

在collectionView里面的代理方法和数据源方法进行 设置数据和UI

image.png
extension GameViewController:UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return gameViewModel.gameModels.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: KGameCellID, for: indexPath) as! JFCollectionGameCell
        let gameModel = gameViewModel.gameModels[indexPath.item]
        // cell.group = gameModel
        // Cannot assign value of type 'JFGameModel' to type 'AnchorGroup' 模型不匹配

        cell.baseGame = gameModel
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: KHeaderViewID, for: indexPath) as! JFCollectionHeaderView
//               let group = recommendVM.anchorGroups[indexPath.section]
//               headerView.anchorGroup = group
        headerView.titleLabel.text = "全部"
        headerView.iconImageView.image = UIImage(named: "Img_orange")
        headerView.moreBtn.isHidden = true
               
        return headerView
        
    }
}

这些基本上和OC的那套差不多

抽取基类


class BaseViewController: UIViewController {
    
    var contentView:UIView?
    
    
    fileprivate lazy var imageView:UIImageView = { [unowned self] in
        let imageView = UIImageView(image: UIImage(named: "img_loading_1"))
        imageView.center = self.view.center
        //数组中不能试可选类型
        imageView.animationImages = [UIImage(named: "img_loading_1")!,UIImage(named: "img_loading_2")!]
        imageView.animationDuration  = 0.5
        //LONG_MAX 非常大的整形
        imageView.animationRepeatCount = LONG_MAX
        
        //顶部和底部随父控件的 拉伸而拉伸
        imageView.autoresizingMask = [.flexibleTopMargin,.flexibleBottomMargin]
        
        return imageView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }
    
    func setupUI(){
        view.backgroundColor = UIColor.white
        contentView?.isHidden = true
        view.addSubview(imageView)
        imageView.startAnimating()
    }
    
    func loadDataFinished(){
        imageView.stopAnimating()
        imageView.isHidden = true
        contentView?.isHidden = false
        
    }
}

1、暴露一个contentView给外部 填充
2、loadDataFinished 暴露数据加载完成 刷新UI

好了 接下来

归纳下Swift一些常见且高频注意的点:

枚举类型

image.png

image.png
image.png

结构体

image.png
image.png

image.png

改变成员的属性,如果在函数红星改变成员的属性,那么该函数前必须加上mutating

加下滑"_" 代表参数可以 label 可以省略
image.png
image.png
image.png

类的基本使用

image.png

类里面定义属性 必须要对属性进行初始化

创建一个类用()创建 其实就是在调用构造函数

为什么 在类里面定义一个属性 必须要 对这个属性进行初始化

答案:应为类初始化的时候 会自动调用 初始化构建函数 而在这个是初话构建函数里面 会要求 里面的每一个属性进行初始化

image.png
image.png

好了今天就和大家分享到这
附上源码
源码
如果有啥问题希望大家一起纠正,非常感谢。
或者大家有想要的分享方式。我后续也会开始准备。
再见。

你可能感兴趣的:(JFDouYu-Swift(二))