Связывание нескольких пользовательских UITableViewCell с одним XIB - PullRequest
0 голосов
/ 22 января 2019

Вопрос

Я создаю пользовательский UITableViewCell для повторного использования, а затем создаю его подкласс для повторного использования. Как настроить эти различные подклассы для ссылки на один и тот же файл xib?

Фон

Давайте назовем мой пользовательский UITableViewCell PickerTableViewCell. Эта ячейка включает UIPickerView, а также все реализации того, как выглядит и ведет себя окно выбора. Когда я хочу использовать эту ячейку, единственное, что мне нужно дать - это данные для представления выбора. Поэтому я создаю подкласс PickerTableViewCell, а затем просто создаю нужный источник данных и назначаю его представлению выбора. Пока все это работает хорошо.

Вот соответствующие части PickerTableViewCell:

class PickerTableViewCell: UITableViewCell {

    var picker: UIPickerView! = UIPickerView()
    var pickerDataSource: PickerViewDataSource!

    override func awakeFromNib() {
        super.awakeFromNib()

        self.picker = UIPickerView(frame: CGRect(x: 0, y: 40, width: 0, height: 0))
        self.picker.delegate = self

        self.assignPickerDataSource()
    }

    // Must be overriden by child classes

    func assignPickerDataSource() {
        fatalError("Must Override")
    }

Вот пример подкласса:

class LocationPickerTableViewCell: PickerTableViewCell {

    override func assignPickerDataSource() {
        self.pickerDataSource = LocationPickerDataSource()
        self.picker.dataSource = self.pickerDataSource
    }
}

Задача

Так как я использую эти ячейки повсеместно, с разными источниками данных, я создал файл xib, который определяет, как ячейка выглядит как PickerTableViewCell.xib, и назначил его классу PickerTableViewCell. В контроллерах представления, для которых я хочу его использовать, я регистрирую ячейку с табличным представлением внутри viewDidLoad(). Затем внутри func tableView(_:, cellForRowAt) я снимаю подкласс, который хочу вот так:

let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! LocationPickerTableViewCell
return cell

Вот здесь и возникает проблема. Созданная ячейка - это PickerTableViewCell, а не ее подкласс LocationPickerTableViewCell. Это приводит к фатальной ошибке, которую я поместил в родительский класс, который переопределяется дочерним классом.

Единственный способ решить эту проблему - создать отдельный файл XIB для каждого подкласса, который я хочу создать, и назначить его соответствующему подклассу. Хотя это решение действительно работает, неправильно иметь все эти файлы xib, которые практически идентичны (за исключением того, к какому классу они назначены), внутри моего проекта.

Есть ли способ, которым я могу преодолеть эту проблему, и чтобы все эти ячейки ссылались на один и тот же файл xib?

Спасибо! :)

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Не используйте подклассы для назначения различных источников данных.

Подход 1: Назначить pickerDataSource в tableView(_:cellForRowAt:)

В контроллере табличного представления необходимо назначить pickerDataSource

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! PickerTableViewCell
    cell.pickerDataSource = LocationPickerDataSource()

    return cell
}

Выполните дополнительную работу, необходимую после назначения pickerDataSource, с помощью didSet.

class PickerTableViewCell: UITableViewCell {

    var picker: UIPickerView! = UIPickerView()
    var pickerDataSource: PickerViewDataSource! {
        didSet {
            self.picker.dataSource = self.pickerDataSource
        }
    }

    …
}

Подход 2. Расширение PickerTableViewCell всеми необходимыми способами.

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

extension PickerTableViewCell {

    func setupLocationPickerDataSource() {
        self.pickerDataSource = LocationPickerDataSource()
        self.picker.dataSource = self.pickerDataSource
    }

}

, затем в tableView(_:cellForRowAt:)

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! PickerTableViewCell
    cell.setupLocationPickerDataSource()

    return cell
}
0 голосов
/ 22 января 2019

Добавить представление, загруженное xib, к UITableViewCell классам, в которых вы хотите его использовать.

  1. Создайте свой xib согласно вашему дизайну, в вашем примере PickerTableViewCell.xib

  2. Создайте UITableViewCell подклассы, в которых вы хотите использовать это представление. Для этого я использую FirstTableViewCell & SecondTableViewCell.

в конструкторе ячейки таблицы загрузите xib и добавьте его в ячейку таблицы.

let nib = Bundle.main.loadNibNamed("PickerTableViewCell", owner: nil, options: nil)
        if let view = nib?.first as? UIView{
            self.addSubview(view)
        } 

если у xib есть @IBOutlet, то получить их с помощью функции viewWithTag и присвоить переменным класса

if let label = self.viewWithTag(1) as? UILabel{
            self.label = label
        }

override reuseIdentifier var каждого tableviewCell подкласс с другим именем

override var reuseIdentifier: String?{
    return "FirstTableViewCell"
}

Теперь Вы можете использовать эти классы, где хотите, для этого выполните следующие шаги:

зарегистрировать этот подкласс tableviewCell с xib с tableview:

tableView.register(FirstTableViewCell.self, forCellReuseIdentifier:"PickerTableViewCell")

теперь в cellForRowAt indexPath методе использовать его.

var cell = tableView.dequeueReusableCell(withIdentifier: "FirstTableViewCell", for: indexPath) as? FirstTableViewCell
if cell == nil {
    cell = FirstTableViewCell()
}
cell?.label?.text = "FirstTableViewCell"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...