在MVVM模式下基于协议开发Swift程序

学习swift差不多两三个月了,其中看swift的官方文档看了差不多一个月,接下来的时间就在写swift的Demo,虽然开发起来顺风顺水,可是总感觉在走OC中的老路,在MVC下基于类开发程序。没有领悟到swift基于协议开发的精髓。昨天看到了Natasha的文章面向协议的 MVVM 架构介绍,觉得豁朗开朗,赶紧在自己的Demo中试了一下。废话不多说,开始正题。

图1

这是一个很简单的页面,包含4个label,3个button和一个uiview的分割线,运行起来的效果如下:

图2

在MVC下,正常的Cell里的代码一般是在awakeFromNib里设置label和button文字的颜色和字号,并且在传递完model的值之后设置label和button的数据。(下面的rgbColorWithHexString是UIColor的一个分类方法,显示16进制的颜色,AutoChangeSize是根据不同的屏幕放大乘以不同的倍数。这样写出来的程序在不同的设备上都运行良好)

override func awakeFromNib() {

super.awakeFromNib()

docImageView.layer.cornerRadius = docImageView.width / 2.0

docImageView.layer.masksToBounds = true

docName.textColor = UIColor.rgbColorWithHexString("#333333")

docName.font = UIFont.systemFontOfSize(CGFloat.AutoChangeSize(16))

levelLabel.textColor = UIColor.rgbColorWithHexString("#333333")

levelLabel.font = UIFont.systemFontOfSize(CGFloat.AutoChangeSize(13))

articleTitle.textColor = UIColor.rgbColorWithHexString("#333333")

articleTitle.font = UIFont.systemFontOfSize(CGFloat.AutoChangeSize(13))

articleDetail.textColor = UIColor.rgbColorWithHexString("#666666")

articleDetail.font = UIFont.systemFontOfSize(CGFloat.AutoChangeSize(12))

}

但是如果把文字大小和颜色都放在cell的类中,那如果需要一个和这个一样的cell,只是字号和字体颜色做下改变,并且增加一些点击事件。那么我们就不能很好的复用代码,当然可以考虑继承,也可以解决这个问题。但是既然是写swift,我们就把基于协议的特性发挥出来。

首先我们根据最终需要展示的样式,自定义一个xib,拖好控件,(最终如图1所示)这里的cell是动态高度的,只有content的高度是需要动态改变,所以我们需要把content多设置一个距离父控件右边15的约束,并且把numberOfline设置为0.最重要的一点是把Content Hugging Priority的Vertical的值设置为249. 关于具体的动态cell高度的设置请看我的这篇文章这里

然后创建一个DrugAdviceTableViewCell类,继承自UITableViewCell。并且拖好控件

@IBOutlet weak var time: UILabel!

@IBOutlet weak var title: UILabel!

@IBOutlet weak var content: UILabel!

@IBOutlet weak var buyNumber: UILabel!

@IBOutlet weak var buyButton: UIButton!

@IBOutlet weak var shopLocation: UIButton!

@IBOutlet weak var phoneNumber: UIButton!

然后我们需要的是在这个类里定义两个协议

protocol DrugAdviceTableViewCellDataSource

{

var time : String { get }

var title : String { get }

var content : String { get }

var url_title : String { get }

var store_title : String { get }

var buyNumber : String { get }

var phoneNumber : String { get }

}

protocol DrugAdviceTableViewCellDelegate

{

var timeFont: UIFont { get }

var titleFont: UIFont { get }

var contentFont: UIFont { get }

var timeColor: UIColor { get }

var titleColor: UIColor { get }

var contentColor: UIColor { get }

var buttonColor: UIColor { get }

}

这里利用了苹果定义tableView协议时的思想,dataSource是必须实现的协议,因为没有数据不知道该显示什么。delegate是可选协议,如果不现实我们应该有默认的实现效果。因为swift没有OC中的optional关键字,所以我们需要对DrugAdviceTableViewCellDelegate协议进行一个协议扩展。

extension DrugAdviceTableViewCellDelegate

{

var timeFont: UIFont { return SystemFont(AutoChangeSize(14)) }

var titleFont: UIFont { return SystemBordFont(AutoChangeSize(18)) }

var contentFont: UIFont { return SystemFont(AutoChangeSize(16)) }

var timeColor: UIColor { return UIColor.rgbColorWithHexString(BlackText666Color) }

var titleColor: UIColor { return UIColor.rgbColorWithHexString(BlackText333Color) }

var contentColor: UIColor { return UIColor.rgbColorWithHexString(BlackText333Color) }

var buttonColor: UIColor { return UIColor.rgbColorWithHexString("#2087fb") }

}

在cell里我们已经定义好的协议,接下来我们需要实现的就是一个ViewModel,在ViewModel里我们需要实现协议。

import UIKit

struct DrugAdviceViewModel: DrugAdviceTableViewCellDataSource {

var model: DrugAdviceModel? {

didSet {

time = model!.time!

title = (model?.title)!

content = (model?.content)!

url_title = (model?.url_title)!

store_title = (model?.store_title)!

buyNumber = model!.buyNumber!

phoneNumber = model!.phoneNumber!

}}  var time = ""

var title = ""

var content = ""

var url_title = ""

var store_title = ""

var buyNumber = ""

var phoneNumber = ""

}

extension DrugAdviceViewModel: DrugAdviceTableViewCellDelegate {

}

这里面的model是controller请求下来数据并转化为模型,传递给ViewModel的。至于为什么不在ViewModel里请求数据,因为我觉得请求下来的数据是一个数组,需要根据indexPath.row 根据不同行数显示不同的数据,所以还是放在controller里,保存为array,再根据indexPath.row传递给ViewModel比较好,也比较方便。

现在我们需要在controller里请求数据,并且需要根据不同的cell创建不同的ViewModel(ViewModel里的model的数据是不同的),并且把ViewModel传递给cell显示。

func getDurgAdvices()

{

AFNetWorkUtil.GET("recipe/notice", parameters: ["user_id" : GlobalInfo.sharedInfo.userInfo!.uid!, "page" : NSNumber(float: 1)], succeed: { (dataTask, responseObject) in

let datas: NSArray = responseObject["data"] as! NSArray

for dict in datas {

let model: DrugAdviceModel = DrugAdviceModel.parse(dict: dict as! NSDictionary)

self.durgAdvices?.append(model)

}self.tableView.reloadData()})                                                                   { (dataTask, error) in}}

// MARK: - tableViewDataSource

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return durgAdvices!.count

}

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

let cell = tableView.dequeueReusableCellWithIdentifier("DrugAdvice", forIndexPath: indexPath) as! DrugAdviceTableViewCell

var viewModel = DrugAdviceViewModel()

viewModel.model = durgAdvices![indexPath.row]

cell.configure(withDataSource: viewModel, delegate: viewModel)

return cell

}

cell.configure(withDataSource: viewModel, delegate: viewModel)这个方法负责把ViewModel传递给cell。所以我们需要在cell里定义这个方法。最终cell的实现为:

import UIKit

protocol DrugAdviceTableViewCellDataSource

{

var time : String { get }

var title : String { get }

var content : String { get }

var url_title : String { get }

var store_title : String { get }

var buyNumber : String { get }

var phoneNumber : String { get }

}

protocol DrugAdviceTableViewCellDelegate

{

var timeFont: UIFont { get }

var titleFont: UIFont { get }

var contentFont: UIFont { get }

var timeColor: UIColor { get }

var titleColor: UIColor { get }

var contentColor: UIColor { get }

var buttonColor: UIColor { get }

}

extension DrugAdviceTableViewCellDelegate

{

var timeFont: UIFont { return SystemFont(AutoChangeSize(14)) }

var titleFont: UIFont { return SystemBordFont(AutoChangeSize(18)) }

var contentFont: UIFont { return SystemFont(AutoChangeSize(16)) }

var timeColor: UIColor { return UIColor.rgbColorWithHexString(BlackText666Color) }

var titleColor: UIColor { return UIColor.rgbColorWithHexString(BlackText333Color) }

var contentColor: UIColor { return UIColor.rgbColorWithHexString(BlackText333Color) }

var buttonColor: UIColor { return UIColor.rgbColorWithHexString("#2087fb") }

}

class DrugAdviceTableViewCell: UITableViewCell

{

@IBOutlet weak var time: UILabel!

@IBOutlet weak var title: UILabel!

@IBOutlet weak var content: UILabel!

@IBOutlet weak var buyNumber: UILabel!

@IBOutlet weak var buyButton: UIButton!

@IBOutlet weak var shopLocation: UIButton!

@IBOutlet weak var phoneNumber: UIButton!

private var dataSource: DrugAdviceTableViewCellDataSource?

private var delegate: DrugAdviceTableViewCellDelegate?

func configure(withDataSource dataSource: DrugAdviceTableViewCellDataSource, delegate: DrugAdviceTableViewCellDelegate?)

{

self.dataSource = dataSource

self.delegate = delegate

time.text = dataSource.time

title.text = dataSource.title

content.text = dataSource.content

buyNumber.text = dataSource.buyNumber

buyButton.setTitle(dataSource.url_title, forState: .Normal)

shopLocation.setTitle(dataSource.store_title, forState: .Normal)

phoneNumber.setTitle(dataSource.phoneNumber, forState: .Normal)

time.font = delegate?.timeFont

title.font = delegate?.titleFont

content.font = delegate?.contentFont

buyNumber.font = delegate?.contentFont

buyButton.titleLabel?.font = delegate?.contentFont

shopLocation.titleLabel?.font = delegate?.contentFont

phoneNumber.titleLabel?.font = delegate?.contentFont

time.textColor = delegate?.timeColor

title.textColor = delegate?.titleColor

content.textColor = delegate?.contentColor

buyNumber.textColor = delegate?.contentColor

buyButton.titleLabel?.textColor = delegate?.buttonColor

shopLocation.titleLabel?.textColor = delegate?.buttonColor

phoneNumber.titleLabel?.textColor = delegate?.buttonColor

}}

最主要的代码就是以上这些,运行后就能得到我们图2所示的效果。具体代码我放在了github地址,因为请求的方法里有一些参数是公司的,所以在分享的Demo里我使用的是自己写的数据,你只要把数据替换一下就可以。我们这个程序是适配不同屏幕的,所以对屏幕适配有问题的,可以参考一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容