Мне нужна помощь в выяснении того, как я могу предоставить каждой строке UITableView настраиваемую ячейку, т.е. первая ячейка имеет заголовок «Имя», вторая ячейка - заголовок «Электронная почта» и т. д.
Как я могу добиться этого, используя протоколы и одновременно следуя MVVM?
Мое текущее решение выглядит следующим образом:
У меня есть 1 ViewModel, 1 контроллер и 1 атм UITableViewCell.
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
}
}
Я пытался следовать примеру, предоставленному ссылкой это, чтобы реализовать правильное решение, однако добавляется только одна ячейка, и я не могу понять, как реализовать вторую ячейку в следующих строках. Должен ли я создать новую модель представления для каждой отдельной ячейки, которую я хотел бы использовать? Если это так, не могли бы вы привести пример?
Конечно, я могу это сделать. Дайте мне немного времени, и я отредактирую код до рабочего примера.
Круто Я правда думаю, что вы задаете важный вопрос. Проблема, с которой вы сталкиваетесь, является повторяющейся в сообществе iOS, и можно создавать представления таблиц, которые принимают общие ячейки, выразительно соединенные вместе. Если вы видите мой ответ, есть пример, который предлагает способ сделать это. И это не проблема, связанная строго с MVVM как архитектурой, это более общая проблема.





Попробуйте использовать «коробку», чтобы содержать код генерации вашей ячейки. Не регистрируйте идентификатор ячейки до того, как ячейка понадобится. Придумать рабочий пример немного сложно, поэтому возьмите в качестве примера следующий код:
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)
}
}
Я действительно рекомендую вам сократить вопрос до более управляемого примера, однако в качестве улучшения вышеупомянутого 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)
}
}
}
Могу я порекомендовать вам отредактировать код для более ясного примера? Отсутствуют части, из-за которых ваш код не компилируется и, в то же время, не имеет отношения к вопросу. Сведите все к чему-то более простому, что компилирует.