Правильно ли хранить ссылку на объект Model в UITableViewCell, который представляет в нем? - PullRequest
0 голосов
/ 21 марта 2019

Хотелось бы узнать, правильно ли хранить ссылку на модель, которую в ней представляет UITableViewCell.

Причина, по которой я спрашиваю, связана с необходимостью знать модель в случае нажатия кнопки внутри нее.

Есть ли лучший (a.k.a: желательный) способ сделать это?

Пример:

class Person {
    var name: String
    var lastName: String
    var age: Int
}

protocol PersonCellDelegate: NSObjectProtocol {
    // should the second parameter be the model that the cell represents?
    func songCell(_ cell: PersonCell, didClickAtEditButtonOfPerson person: Person)
}

class PersonCell: UITableViewCell {

    @IBOutlet private weak var nameLabel: UILabel!
    @IBOutlet private weak var lastNameLabel: UILabel!
    @IBOutlet private weak var ageLabel: UILabel!

    @IBOutlet private weak var editButton: UIButton!

    // does the cell need to store its reference?
    var person: Person! {
        didSet {
            nameLabel.text = person.name
            // ...
        }
    }

    weak var delegate: PersonCellDelegate?

    // ...
}

Ответы [ 6 ]

1 голос
/ 21 марта 2019

Ячейка табличного представления является представлением.Чем меньше он знает о логике приложения, тем лучше.

Вы можете извлечь используемую сущность, используя метод indexPath(for:):

protocol MyTableViewCellDelegate: AnyObject {
    func myTableViewCellDidSomething(_ cell: MyTableViewCell)
}

class MyTableViewCell: UITableViewCell {
    weak var delegate: MyTableViewCellDelegate?
}

class ViewController: UITableViewController, MyTableViewCellDelegate {

    var personList: [Person] = []

    func myTableViewCellDidSomething(_ cell: MyTableViewCell) {
        guard let indexPath = tableView.indexPath(for: cell) else { return }
        let person = personList[indexPath.row]
        // ...
    }
}
0 голосов
/ 22 марта 2019

Вы спрашиваете:

нужно ли ячейке хранить свою ссылку?

Нет. Фактически это блокирует вас в ссылочной семантике, и вы могли бы рассмотреть семантику значений для объекта Person. Я также думаю, что это мутит модель собственности. Кому сейчас принадлежит этот Person объект?

И даже если вы были привержены эталонной семантике и хотели использовать этот шаблон для обнаружения изменений Person, будьте осторожны, так как ваш шаблон didSet - это только половина решения. Тип Person является изменяемым, и вы обнаруживаете, когда объект заменяется новым Person объектом, но не обнаруживается, когда изменяются отдельные свойства Person. Если вы собираетесь пойти по этой дороге didSet с изменяемыми ссылочными типами, вы также можете добавить KVO для соответствующих свойств.

Этот шаблон влечет за собой довольно тесную связь объектов вида и объектов модели. Как и предполагали другие, вы могли бы рассмотреть другие способы решения этой проблемы и / или уменьшения нагрузки на контроллер представления.

Если вы ищете автоматическое обновление ячейки, когда объект Person видоизменяется (и, возможно, наоборот), вы можете рассмотреть шаблоны привязки, например, предлагаемые библиотеками, такими как RxSwift , Бонд и т. Д.

Я бы также отослал вас к презентации Дейва Делонга A Better MVC , которая проведет вас через соображения, если вы не хотите отказываться от MVC, но выясните способы работы с ним или Среда Шаблоны архитектуры iOS , представляющая собой введение в другие варианты.

0 голосов
/ 21 марта 2019

Это зависит от:

Если вы можете переместить ячейки в табличном представлении (вручную или нажатием кнопки), используя insertRows и deleteRows, то это почти единственный способ (наряду с протоколом / делегатом) иметь возможность чтобы получить индексный путь ячейки эффективно без перезагрузки всего табличного представления.

В виде таблицы, где ячейки не перемещаются, не передавайте модель в ячейку. Вы можете использовать обратные вызовы, которые фиксируют путь индекса и даже элемент модели.

0 голосов
/ 21 марта 2019

Итак, нет единственно верного пути, поэтому я могу просто сказать вам, если бы я поступил.

Если бы я не перемещал ячейки, у меня сохранялось бы свойство модели. В классе ячеек не следует устанавливать свойства торговых точек, так как ячейки можно использовать повторно. Вы просто сообщаете контроллеру, что источник данных изменен, и вам следует перезагрузить строки / определенную строку.

var person: Person! // instead, tell controller, that person has been changed

Далее я бы оставил шаблон делегата и использовал бы переменные замыкания. Это делает код более быстрым (в будущем вы можете искать RxSwift).

class PersonCell: UITableViewCell {

    var personChanged: (Person) -> Void = { _ in }

    var person: Person!

    func foo() {
        // change person's properties
        personChanged(person)
    }

    func setCell() {
        nameLabel.text = person.name
    }



}

Затем установите все вещи, такие как метка text в методе cellForRowAt UITableViewDelegate. Также не забудьте установить закрытие ячейки и объявить, что должно произойти после смены человека

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = // ...
    cell.person = people[indexPath.row]
    cell.setCell()
    cell.personChanged = { [weak self] person in 
        guard let self = self else { return }
        self.people[indexPath.row] = person
        self.tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    return cell
}
0 голосов
/ 21 марта 2019

Я использую его в некоторых случаях, особенно, как вы делаете здесь, в пользовательской ячейке. Я не полагаюсь на UItableViewCell для хранения данных, которые у меня есть в модели, поскольку ячейка может быть повторно использована, когда она находится за пределами экрана

0 голосов
/ 21 марта 2019

В строгом MVC представление не должно обращаться к модели напрямую.

Когда пользователь нажимает кнопку в ячейке, вызывает метод делегата.Пусть делегат (обычно это контроллер представления) обрабатывает событие щелчка (например, изменение модели).

После обновления модели контроллер обновит представление при необходимости.

...