美文网首页Swift
JFDouYu-Swift(二)

JFDouYu-Swift(二)

作者: 土鳖不土 | 来源:发表于2020-10-08 11:27 被阅读0次

    今天 先分享下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(二)

        本文链接:https://www.haomeiwen.com/subject/kmwpyktx.html