简体   繁体   English

实现不同的单元格类型

[英]Implementing Different Cell Types

I need some help figuring out how I can provide each row of a UITableView with a custom cell, ie first cell has a "Name" title, the second cell has an "Email" title etc. 我需要一些帮助来弄清楚如何为UITableView每一行提供自定义单元格,即第一个单元格具有“名称”标题,第二个单元格具有“ Email”标题等。

How can I achieve this using protocols and following MVVM at the same time? 如何使用协议并同时遵循MVVM来实现这一目标?

My current solution is as follows: 我当前的解决方案如下:

I have 1 ViewModel, 1 Controller and 1 UITableViewCell atm. 我有1个ViewModel,1个控制器和1个UITableViewCell atm。

struct ProfileNameViewModel
{

}

extension ProfileNameViewModel: TextPresentable
{
    var text: String
    {
        return "Name"
    }
}


import UIKit

class ProfileController: UIViewController
{
    // VARIABLES
    let profileView = ProfileView()
    let profileTableView: UITableView =
    {
        let tableView = UITableView()
        tableView.tableFooterView = UIView(frame: .zero)
        tableView.translatesAutoresizingMaskIntoConstraints = false

        return tableView
    }()

    private var profileCellViewControllers = [UIViewController]()
    let testVC = UIViewController()

    // FUNCTIONS
    override func viewDidLoad()
    {
        super.viewDidLoad()

        view.backgroundColor = UIColor.white

        navigationItem.title = "Profile"

        // profileTableView initialization
        setupProfileTableView()
    }

    private func setupProfileTableView()
    {
        profileTableView.delegate = self
        profileTableView.dataSource = self

        profileTableView.register(ProfileTableViewCell<ProfileNameViewModel>.self, forCellReuseIdentifier: ProfileTableViewCell<ProfileNameViewModel>.reuseIdentifier)

        view.addSubview(profileTableView)

        profileTableView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
        profileTableView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        profileTableView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        profileTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
    }

    private func setupProfileViewLayout()
    {
        profileView.setupAnchors(top: view.safeAreaLayoutGuide.topAnchor,
                                 bottom: view.safeAreaLayoutGuide.bottomAnchor,
                                 left: view.leftAnchor,
                                 right: view.rightAnchor)
    }
}

extension ProfileController: UITableViewDataSource, UITableViewDelegate
{
    // Table view header setup
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
    {
        if (section == 0)
        {
            return "Profile"
        }

        return "Foo"
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
    {
        return 50
    }

    // Table view sections setup
    func numberOfSections(in tableView: UITableView) -> Int
    {
        return 2
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        if (section == 1)
        {
            return 3
        }

        return 5
    }

    // Table view cell setup
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: ProfileTableViewCell<ProfileNameViewModel>.reuseIdentifier, for: indexPath) as! ProfileTableViewCell<ProfileNameViewModel>

        let viewModel = ProfileNameViewModel()
        cell.configure(withDelegate: viewModel)

        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            print(indexPath.row)
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
    {
        return 50
    }
}


import UIKit

class ProfileTableViewCell<T>: UITableViewCell where T: TextPresentable
{

    // VARIABLES
    let containerView = UIView()
    var arrowImageView = UIImageView()
    var cellTitle: UILabel!
    var userInformationText: UILabel!

    private var delegate: T?

    // FUNCTIONS
    func configure(withDelegate viewModel: T)
    {
        // Setup relavant views
        cellTitle = UILabel()
        userInformationText = UILabel()
        arrowImageView = setupImageView(imageName: "testImage", contentMode: .scaleAspectFit, imageTintColor: .black)
        setupContainer()

        delegate = viewModel

        cellTitle?.text = viewModel.text
        cellTitle.font = viewModel.textFont
        cellTitle.textColor = viewModel.textColor

        userInformationText.text = "Test"
    }

    private func setupContainer()
    {
        addSubview(containerView)
        containerView.addSubview(cellTitle)
        containerView.addSubview(userInformationText)
        containerView.addSubview(arrowImageView)

        // Container view for each cell
        containerView.setupAnchors(top: topAnchor,
                               bottom: bottomAnchor,
                               left: leftAnchor,
                               right: rightAnchor,
                               padding: .init(top: 3, left: 0, bottom: 3, right: 0))

        // Views inside the container view
        cellTitle.setupAnchors(top: nil,
                           bottom: containerView.bottomAnchor,
                           left: containerView.leftAnchor,
                           right: nil,
                           padding: .init(top: 0, left: 15, bottom: 7, right: 0))

        arrowImageView.setupAnchors(top: nil,
                                bottom: containerView.bottomAnchor,
                                left: nil,
                                right: containerView.rightAnchor,
                                padding: .init(top: 0, left: 0, bottom: 13, right: 5),
                                size: .init(width: 10, height: 10))

        userInformationText.setupAnchors(top: nil,
                                     bottom: containerView.bottomAnchor,
                                     left: nil,
                                     right: arrowImageView.leftAnchor,
                                     padding: .init(top: 0, left: 0, bottom: 7, right: 10))
    }

    private func setupImageView(imageName: String, contentMode: UIViewContentMode, imageTintColor: UIColor) -> UIImageView
    {
        let arrowImage: UIImage = UIImage(named: imageName)!
        let arrowImageView: UIImageView = UIImageView(image: arrowImage)
        arrowImageView.contentMode = contentMode
        arrowImageView.imageTintColor(color: imageTintColor)

        return arrowImageView
    }
}

I was trying to follow the example provided by this link to implement a proper solution, however, only one cell is added and I can't wrap my head around how to implement a second cell at following rows. 我试图按照链接提供的示例来实现适当的解决方案,但是,仅添加了一个单元,并且我无法在接下来的几行中全神贯注于如何实现第二个单元。 Am I supposed to create a new ViewModel for each separate cell that I would like to use? 我是否应该为要使用的每个单独的单元格创建一个新的ViewModel? If that is the case, could you please provide an example? 如果是这样,请您举个例子吗?

Try to use a "box" to contain your cell generation code. 尝试使用“框”包含您的细胞生成代码。 Don't register cell id before the cell is needed. 在需要单元格之前不要注册单元格ID。 It's a bit difficult to come up with a working example so please take the following code as an example: 给出一个可行的示例有点困难,因此请以以下代码为例:

import UIKit

protocol CellBox {

    func getCell(for tableView: UITableView) -> UITableViewCell
}

struct MyCellBox: CellBox {

    private let id = "my_cell_id"

    func getCell(for tableView: UITableView) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: id) {
            return cell
        } else {
            return UITableViewCell(style: .default, reuseIdentifier: id)
        }
    }
}

class MyDataSource: NSObject, UITableViewDataSource {

    var boxes: [CellBox] = []
    // This is just a hack for compiling...
    // ...the idea is that you need to have a reference to your real table view somewhere
    let tableView = UITableView()

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

        return boxes.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        return boxes[indexPath.row].getCell(for: tableView)
    }
}

I do recommend that you shrink the question to a more manageable example, however as an improvement to the above MyCellBox , there is a chance that this is close to what you want: 我确实建议您将问题缩小到一个更易于管理的示例,但是作为对上述MyCellBox的改进,有可能使该问题与您想要的接近:

struct MyCellBox: CellBox {

    private let id = "my_cell_id"

    func getCell(for tableView: UITableView) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: id) {
            return cell
        } else {
            let viewModel = MyViewModel(text: "WOW", font: UIFont(name: "Whatever", size: 10)!, color: UIColor.black)
            let cell = ProfileTableViewCell<MyViewModel>()
            cell.configure(withDelegate: viewModel)
            return UITableViewCell(style: .default, reuseIdentifier: id)
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM