图1学习swift差不多两三个月了,其中看swift的官方文档看了差不多一个月,接下来的时间就在写swift的Demo,虽然开发起来顺风顺水,可是总感觉在走OC中的老路,在MVC下基于类开发程序。没有领悟到swift基于协议开发的精髓。昨天看到了Natasha的文章面向协议的 MVVM 架构介绍,觉得豁朗开朗,赶紧在自己的Demo中试了一下。废话不多说,开始正题。
这是一个很简单的页面,包含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里我使用的是自己写的数据,你只要把数据替换一下就可以。我们这个程序是适配不同屏幕的,所以对屏幕适配有问题的,可以参考一下。
网友评论