如何在内部将模型数据发送到单元?

时间:2019-06-09 00:10:22

标签: ios swift mvvm

我有以下代码在表格视图中显示数据。

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? ArticleTalbeViewCell else {
        fatalError("ArticleTableViewCell not found")
    }

    let articleVM = self.articleListVM.articleAtIndex(indexPath.row)
//        cell.viewModel = articleVM
        cell.titlelabel?.text = articleVM.title
        cell.descriptionLabel?.text = articleVM.description
        return cell
    }
}

现在,我的

代码
cell.titlelabel?.text = articleVM.title
cell.descriptionLabel?.text = articleVM.description

做得好。

cell.viewModel = articleVM是好的做法吗?

想象一下我必须将日期多次设置到单元格吗?这种方法cell.viewModel = articleVM将节省几行。

UITableViewCell的代码如下:

class ArticleTalbeViewCell: UITableViewCell {

    var titlelabel:UILabel?
    var descriptionLabel:UILabel?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.setupUI()

    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        fatalError("init(coder:) has not been implemented")
    }

    private func setupUI() -> () {
        let label1 = UILabel()
        label1.numberOfLines = 0
        let label2 = UILabel()
        label2.numberOfLines = 0
        label2.textColor = UIColor.lightGray
        label2.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: NSLayoutConstraint.Axis.vertical)

        titlelabel = label1
        descriptionLabel = label2
        let staview = UIStackView()
        staview.axis = .vertical

        staview.addArrangedSubview(label1)
        staview.addArrangedSubview(label2)
        staview.spacing = 8
        staview.translatesAutoresizingMaskIntoConstraints = false // !important
        self.contentView.addSubview(staview)

        staview.topAnchor.constraint(equalTo: self.contentView.topAnchor,constant: 5).isActive = true
        staview.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -5).isActive = true
        staview.leftAnchor.constraint(equalTo: self.contentView.leftAnchor, constant: 5).isActive = true
        staview.rightAnchor.constraint(equalTo: self.contentView.rightAnchor, constant: -5).isActive = true
    }
}

ArticleListViewModel代码如下:

struct ArticleListViewModel {
    let articles:[Article]
}

extension ArticleListViewModel {

    var numbeOfSections: Int {
        return 1
    }

    func numOfRowsInSection(_ section: Int) -> Int {
        return self.articles.count
    }

    func articleAtIndex(_ index: Int) -> ArticleViewModel {
        let article = self.articles[index]
        return ArticleViewModel(article)
    }
}

struct ArticleViewModel {
    private let article: Article
}

extension ArticleViewModel {
    init(_ article: Article) {
        self.article = article
    }
}

extension ArticleViewModel {
    var title: String {
        return self.article.title ?? "null"
    }

    var description: String {
        return self.article.description ?? "null"
    }
}

ArticleList代码如下:

import Foundation

struct ArticleList: Decodable {
    let articles: [Article]
}

struct Article: Decodable {
    let title: String?
    let description: String?
    let chines: String?
}

如何编辑“单元格”代码以实施cell.viewModel = articleVM

2 个答案:

答案 0 :(得分:1)

首先,我认为给予cell.viewModel = articleVM不会带来太多改变。由于您已经创建了 ArticleListViewModel ArticleViewModel ,因此,如果您为表视图单元格创建了这些虚拟机,则将是相同的。但是,我可以为您提供一些在使用MVVM方法时所使用的建议。 您可以在表格视图单元格内添加新属性,然后使用属性观察器为其赋予一个值。

cell.article = articleVM

因此,您的cellForRowAt方法不会太长,几乎就像将视图模型提供给单元格一样。

private var titlelabel:UILabel?
private var descriptionLabel:UILabel?

var article: ArticleViewModel? {
    didSet {
        guard let article = article else { return }
        titlelabel?.text = article.title
        descriptionLabel?.text = article.description
    }
}

据我了解,您的第二个问题是您是否具有日期变量,并且cellForRowAt方法内部的格式占用太多空间并且看上去很丑陋。这就是为什么需要在视图模型中进行业务逻辑的原因。您的表格视图单元格仅负责显示它而不设置其格式。希望对您来说很清楚。如果您有任何疑问可以询问。

答案 1 :(得分:0)

您必须使用cell模型(即tableView(_:cellForRowAt:)本身中配置cell,而不是在ArticleTalbeViewCell方法中配置dataSource >

class ArticleTalbeViewCell: UITableViewCell {
    private var titlelabel: UILabel?
    private var descriptionLabel: UILabel?

    //rest of the code...

    func configure(with article: Article) {
        self.titlelabel?.text = article.title
        self.descriptionLabel?.text = article.description
    }
}

在上面的代码中,

1。。无需将titlelabeldescriptionLabel暴露在class ArticleTalbeViewCell之外。因此,将它们都标记为private

2。。您只需要与configure(with:)实例一起调用Article方法。

为什么使用方法而不是Article类型的属性?

ArticleTalbeViewCell配置一次之后,除非且除非在其他地方需要,否则无需将模型对象保存在cell内。

tableView(_:cellForRowAt:)方法中,您只需在特定的configure(with:)上与cell实例一起在article上调用indexPath方法。

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? ArticleTalbeViewCell else {
        fatalError("ArticleTableViewCell not found")
    }

    let article = self.articleListVM.articleAtIndex(indexPath.row)
    cell.configure(with: article)
    return cell
}

此外,我认为不需要创建单独的ArticleViewModel。它没有做任何特别的事情。它只是Article上的一个额外包装。

您可以简单地从Article中的articleAtIndex(_:)方法返回extension ArticleListViewModel类型,即

func articleAtIndex(_ index: Int) -> Article {
    let article = self.articles[index]
    return article
}

为什么要在自定义UITableViewCell中进行配置?

这是因为您的tableView可能包含多个自定义UITableViewCells。在cell中添加所有tableView(_:cellForRowAt:)的配置将使代码繁重且无法读取。最好由cell本身而不是cell处理ViewController的特定配置。