Swift教程17-淡化MVC,使用MVVM框架开发轻巧便于维护的iOS/android app

MVVM是微软提出一种移动开发框架,旨在针对传统的MVC框架,解决传统的MVC框架的控制器的臃肿问题.

M:

Model模型,也就是数据模型;比如一条微博,对应的所有字段合成一条微博整体,这个整体就是Model

V:

View视图,只用来显示的视图,如 iOS的UIView,Cell;当然在 iOS中 Storyboard中,view总是和控制器关联,这并不是严格的view

如果我们纯手写代码定义一个view那么就是一个比较严格的view了

VM:

ViewModel视图模型,是将一个 View 和 Model进行绑定;起着桥梁的作用

例如一个 微博  cell ,对应的数据绑定到一个 Model,将获得的数据绑定到Model个各个属性上.

传统的MVC中的  C代表 控制器,负责逻辑处理 和 数据绑定,更新视图;代码过于复杂,维护会比较困难;

而MVVM框架则是,给 Controller控制器减负;

把网络请求,数据绑定,更新视图,都剥离出去;

是在 Controller中做的仅仅是简单的跳转和部分代理

比较符合MVVM开发框架的是 使用TableView的控制器;Cell自定义;定义所需的Model;定义ViewModel调用对应的接口

下面来示范一个MVVM框架开发,带有网络请求的Demo

目录说明:

Resourse:存放图片,音频视频文件,IB文件

Controllers:存放视图控制器

Vender:开源框架,或者自己封装的组件

UtilTools:封装的常用代码,常量定义,配置文件等

ViewModel:根据不同的View获取不同的数据并且绑定到Model中

View:自定义个视图如 cell或者纯手写的view

Model:数据模型,就是View所需的所有字段的整体

实现效果:

一个tableView上显示多条动态包括,图片,文字;

技术要点:

MVVM框架

AFNetworking网络请求

MBProgressHUD等待提示

自定义cell

Block闭包传值,封装代码

实现过程:

1.导入Oc的相关框架

在Swift项目中新建一个Oc文件,系统询问你是否新建一个 桥接Oc的文件,选择是,生成一个  Header.h文件;在其中导入 对应的Oc的 头文件即可使用

如图,工程中有一个 SwiftDemo-Bridging-Header.h文件

导入AF等框架

SwiftDemo-Bridging-Header.h

//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "AFNetworking.h"
#import "MBProgressHUD.h"
#import "UIImageView+AFNetworking.h"

2.使用Storyboard创建一个自定义的cell绑定到cell类,标识符是 TestCell

绑定的TestCell.swift类

//
//  TestCell.swift
//  SwiftDemoUsingAF
//
//  Created by MBinYang on 15/4/8.
//  Copyright (c) 2015年 cc.huanyouwang. All rights reserved.
//

import UIKit

class TestCell: UITableViewCell
{

    @IBOutlet var imageView2: UIImageView!
    @IBOutlet var textLabel2: UILabel!
    @IBOutlet var location: UILabel!
    override func awakeFromNib()
    {
        super.awakeFromNib()

   }

    override func setSelected(selected: Bool, animated: Bool)
    {
        super.setSelected(selected, animated: animated)

   }

   /**
    *配置Cell的内容方法
  */
    func configCellWithStatusModel(model:StatusModel!)
    {
        self.imageView2.setImageWithURL(NSURL(string: model.imageURL), placeholderImage: UIImage(named: "placeHolder"))
        self.textLabel2.text = model.text
        self.location.text = model.location
    }

}

Cell上包含一个 imageView和两个label

3.常量定义,类似Oc的宏定义

StringConstant.swift

常量以小写k开头,其余大写;可以配上下划线

Swift的常量,使用 let定义常量;可以在其他地方使用

//
//  StringConstant.swift
//  SwiftDemo
//
//  Created by MBinYang on 15/4/8.
//  Copyright (c) 2015年 cc.huanyouwang. All rights reserved.
//

import Foundation
import UIKit

let kGETSTATUSURL = "http://lovemyqq.sinaapp.com/getState.php"
let kACCESSTOKEN = "2.00HZE3aF0GvJSM551ca8e0920NF13N"
let kUID = "5117873025"
let kACCESSTOKENKEY = "access_token"
let kUIDKEY = "uid"
let kPARAMDIC = [kACCESSTOKENKEY : kACCESSTOKEN,kUIDKEY : kUID]
let kREDCOLOR = UIColor.redColor
let kIMAGEKEY = "image"
let kDATEKEY = "date"
let kLOCATIONKEY = "address"
let kCONTENTKEY = "content"
let kRESULTKEY = "result"
let kSTATEKEY = "state"

//其他复杂类型,可以使用函数定义
//func RGBA (r:CGFloat, g:CGFloat, b:CGFloat, a:CGFloat)
//{
//    return UIColor (red: r/255.0, green: g/255.0, blue: b/255.0, alpha: a)
//}

//使用方法如下
//var theColor : UIColor = RGBA (255, 255, 0, 1)

4.封装AF为一个常用的方法,便于ViewModel调用

AFRequest.swift

//
//  AFRequest.swift
//  SwiftDemo
//
//  Created by MBinYang on 15/4/8.
//  Copyright (c) 2015年 cc.huanyouwang. All rights reserved.
//

import UIKit

public typealias SuccessBlock = (obj:AnyObject)->Void!
class AFRequest: NSObject
{
    var mainURL:String!
    var paramDict:[String:String]!
    var successBlock:SuccessBlock!
    var manager:AFHTTPRequestOperationManager
    var hud:MBProgressHUD!

    init(mainURL:String,paramDict:[String:String],successBlock:SuccessBlock)
    {
        self.mainURL = mainURL
        self.paramDict = paramDict
        self.successBlock = successBlock
        self.manager = AFHTTPRequestOperationManager()
        super.init()
    }

    func startRequestWithHUDOnView(view:UIView!)
    {
        self.hud = MBProgressHUD.showHUDAddedTo(view, animated: true)
        self.hud.labelText = "正在请求..."

       var op =  self.manager.GET(self.mainURL,
            parameters: self.paramDict,
            success: {  (operation: AFHTTPRequestOperation!,
                responseObject: AnyObject!) in

                var arr: AnyObject! = NSJSONSerialization.JSONObjectWithData(responseObject as NSData, options: NSJSONReadingOptions.AllowFragments, error: nil)

                println(arr!)
                unowned var unSelf:AFRequest = self
               unSelf.successBlock(obj: arr!)
               unSelf.hud.customView = nil
               unSelf.hud.labelText = "加载成功!"
               unSelf.hud.hide(false, afterDelay: 3)
            },
            failure: {  (operation: AFHTTPRequestOperation!,
                error: NSError!) in
                println("请求错误Error: " + error.localizedDescription)
                unowned var unSelf:AFRequest = self
                unSelf.hud.hide(true)
        })

        op.responseSerializer = AFHTTPResponseSerializer()
        op.start()
    }

}

5.根据View/Cell所需的数据,封装一个Model类.

如 封装一个 和 Cell数据字段对应的Model类  StatusModel状态模型

StatusModel.swift

包含,属性,和存取器

//
//  StatusModel.swift
//  SwiftDemoUsingAF
//
//  Created by MBinYang on 15/4/8.
//  Copyright (c) 2015年 cc.huanyouwang. All rights reserved.
//

import UIKit

class StatusModel: NSObject
{

    var imageURL:String!
    var text:String!
    var location:String!

  override  init()
    {
        //Todo
    }

    init(imageURL:String!,text:String,location:String)
    {
        self.imageURL = imageURL
        self.text = text
        self.location = location
    }

    var configModel:(String!,String!,String!)
    {
        set
        {
            self.imageURL = newValue.0
            self.text = newValue.1
            self.location = newValue.2
        }
        get
        {
            return (self.imageURL,self.text,self.location)
        }
    }

    //省略set的 简写的 get
    var urlOfImg:String
    {
        return self.imageURL
    }

}

6.ViewModel根据View所需数据,调用对应的接口;并且把数据绑定到Model中

解析JSON数据,绑定到Model中

StatusViewModel.swift

//
//  StatusViewModel.swift
//  SwiftDemo
//
//  Created by MBinYang on 15/4/8.
//  Copyright (c) 2015年 cc.huanyouwang. All rights reserved.
//

import UIKit

public class StatusViewModel: NSObject
{

    func requestStatusList(view:UIView!,blk:SuccessBlock)
    {

    var af = AFRequest(mainURL: kGETSTATUSURL, paramDict: kPARAMDIC)
        { (obj) -> Void! in

            var dicO = obj as Dictionary<String,AnyObject>
            var arr1: AnyObject? = dicO[kRESULTKEY]
            var  arrreal = arr1 as Array<Dictionary<String,Dictionary<String,String>>>
            var modelArray = Array<StatusModel>()

            /** 解析得到的JSON数据为Model数组*/
            for dict in arrreal
            {
                var dic = dict[kSTATEKEY]! as Dictionary<String,String>
                var sModel = StatusModel()
                sModel.imageURL = dic[kIMAGEKEY]!
                sModel.text = dic[kCONTENTKEY]!
                sModel.location = dic[kLOCATIONKEY]!
                modelArray.append(sModel)

            }
            println(modelArray[0])
           blk(obj: modelArray)

            return Void()
        }
    af.startRequestWithHUDOnView(view)

   }

}

7.TableViewController 控制器中调用  ViewModel显示数据

//
//  TableViewController.swift
//  SwiftDemo
//
//  Created by MBinYang on 15/4/8.
//  Copyright (c) 2015年 cc.huanyouwang. All rights reserved.
//

import UIKit

class TableViewController: UITableViewController
{

    var modelArray = Array<StatusModel>()

    override func viewDidLoad()
    {
        super.viewDidLoad()

         loadData()

    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()

    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int
    {

        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return modelArray.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {

      var sModel = self.modelArray[indexPath.row]
        var cell = tableView.dequeueReusableCellWithIdentifier("TestCell", forIndexPath: indexPath) as TestCell

        cell.configCellWithStatusModel(sModel as StatusModel)

        return cell
    }

   /**
    * 第一次加载数据时请求
    * 返回为空
    */
    func loadData()
    {
        StatusViewModel().requestStatusList(self.view, blk: { (obj) -> Void! in
            unowned var weak_self:TableViewController = self
            println("这是第一次加载数据的 block\(__FUNCTION__)")
            weak_self.modelArray = obj as Array<StatusModel>
            weak_self.tableView.reloadData()
            return Void()
        })
    }

}

调用ViewModel之后再  reloadData()

当然,ViewModel也可能是 在 View上调用的;比如 Cell上有一个  按钮,点击按钮会进行网络请求,也需要调用 ViewModel

8.总结

我们可以看到 在 TableViewController 中,代码非常的少.原来对应  网络请求,对于 视图更新都是放在 controller中;

使用MVVM后可以大大减少Controller的代码,是的各部分耦合性更低,更便于维护.

9.其他,view上有事件的触发

如果 Cell或者 View上或者有 点击事件,或者其他事件;可以 使用 闭包封装 ;类似于 上面的 AF请求.

Swift系列教程:http://blog.csdn.net/yangbingbinga/article/category/3050845

时间: 2024-08-29 12:41:22

Swift教程17-淡化MVC,使用MVVM框架开发轻巧便于维护的iOS/android app的相关文章

关于前端mvc或mvvm框架数据跟踪变化实现dom双向绑定的原理

一:最早的框架如backbone,实现对数据的变化监测是通过设置数据模型api. 比如其model对象管理的是数据,而修改这些数据就是通过固定的方法(set)来触发事件从而更新dom, <p id="dom">1</p> var Model={ a:1, b:2 } var trigger=function(value){ document.getElementById('dom').html(value) //更新dom操作 } var set=functio

第四十六课:MVC和MVVM的开发区别

实现MVC的目的就是为了让M和V相分离.前端的MVC无法做到View和Model的相分离,而MVVM可以. 我们先来看一个用MVC模式开发的经典例子:(一定要深入了解这种开发的思想,而不是看懂代码) $(function(){ //基本的Todo模型, var Todo = Backbone.Model.extend({ // 设置模型的默认属性 defaults: { content: "empty todo...", done: false }, //确保每一个模型的content

迷你MVVM框架 avalonjs 入门教程(司徒正美)

迷你MVVM框架 avalonjs 入门教程 关于AvalonJs 开始的例子 扫描 视图模型 数据模型 绑定属性与动态模板 作用域绑定(ms-controller, ms-important) 模板绑定(ms-include) 数据填充(ms-text, ms-html) 类名切换(ms-class, ms-hover, ms-active) 事件绑定(ms-on,……) 显示绑定(ms-visible) 插入绑定(ms-if) 双工绑定(ms-duplex) 样式绑定(ms-css) 数据绑

迷你MVVM框架 avalonjs 学习教程1、引入avalon

avalon是国内最强大的MVVM框架,没有之一,虽然淘宝KISSY团队也搞了两个MVVM框架,但都无疾而终.其他的MVVM框架都没几个.也只有外国人与像我这样闲的架构师才有时间钻研这东西.我很早之前就预言,MVVM是前端的终极解决方案.我之前在盛大无线做盛大通行证就深有体会,一个业务逻辑对应十来个不同的界面,分层架构是必不可少的.因此双向绑定作为解药,结合很早就流行的MVC框架,衍生出MVVM这神器. 但这么牛叉的东西,为什么现在才流行起来呢?要不是谷歌振臂高呼,这个一直缩在flex, wps

什么是MVVM,MVC和MVVM的区别,MVVM框架VUE实现原理

MVC和MVVM的qu'bie 1. Mvvm定义MVVM是Model-View-ViewModel的简写.即模型-视图-视图模型.[模型]指的是后端传递的数据.[视图]指的是所看到的页面.[视图模型]mvvm模式的核心,它是连接view和model的桥梁.它有两个方向:一是将[模型]转化成[视图],即将后端传递的数据转化成所看到的页面.实现的方式是:数据绑定.二是将[视图]转化成[模型],即将所看到的页面转化成后端的数据.实现的方式是:DOM 事件监听.这两个方向都实现的,我们称之为数据的双向

迷你MVVM框架 avalonjs1.5 入门教程

avalon经过几年以后,已成为国内一个举足轻重的框架.它提供了多种不同的版本,满足不同人群的需要.比如avalon.js支持IE6等老旧浏览器,让许多靠政府项目或对兼容性要求够高的公司也能享受MVVM的乐趣.avalon.modern.js支持IE10以上版本,优先使用新API,性能更优,体积更少.avalon.mobile.js在avalon.modern的基础提供了触屏事件的支持,满足大家在移动开发的需求.此外,它们分别存在avalon.xxx.shim版本,指无自带加载器版,avalon

MVVM开源框架Knot.js 教程2 - 大幅改变前端框架开发体验的Debugger

Knotjs教程系列 1.CBS初步 2.Knot.js Debugger(本文) ....持续增加中 Knot.js 教程2 - 改变前端框架开发体验的Debugger Debugger只是一个方便开发的附属工具,按道理说是不值得单独为之写一篇文章的.不过Knot.js的Debugger绝对值得一篇文章. 有过框架开发体验的朋友一定多少都有过和框架搏斗的经验.一个小小的设置错误,由于你对框架的不够熟悉,导致出错后完全摸不到头脑.或者被迫在一堆陌生的代码中跟踪尝试找出问题,或者只有上网到处拉人提

简单的介绍下WPF中的MVVM框架

最近在研究学习Swift,苹果希望它迅速取代复杂的Objective-C开发,引发了一大堆热潮去学它,放眼望去各个培训机构都已打着Swift开发0基础快速上手的招牌了.不过我觉得,等同于无C++基础上手学习C#一样,即使将来OC被淘汰,那也是N年之后的事情,如果真的要做IOS开发,趁现在Swift才刚开始,花那么几个月去了解一下OC绝对是一件有帮助的事情. 扯远了,我前几天刚接触到一个叫做mvvm的框架,发现很有意思,带着学习的态度来写点东西,不足之处一起研究.还有一个很重要的原因,我发现不少同

MVVM框架对比

MVVM框架对比 MVC和MVP简介 MVVM Vue.js.Angular.js.Ember.Backbone等框架对比 双向绑定原理 Virtual DOM 前端由于发展比较迅速,框架的更新迭代也比较快,从最初的 backbone.js 到后来的Ember.Knockout.Angular.js, 再到现在的Vue.js.React. MVC和MVP简介 视图(view):用户界面 控制器(controller):业务逻辑 模型(model):数据保存 通信方式如下: view传送指令到co