Swift — Общие модели для разных ячеек табличного представления

#swift #uitableview #generics

#swift #uitableview #общие модели

Вопрос:

Я работаю над табличным представлением (аналогично Facebook Messenger), в котором у меня разные типы ячеек (изображение, текст, видео и т.д.) Что я пытаюсь заархивировать, так это то, что я хочу объявить список message model , и я настрою TableView так, чтобы ячейка определялась и конфигурировалась самой моделью. Для того, чтобы сделать это, мне нужно каким-то образом сообщить, с model каким классом UITableViewCell он связан. В принципе, я хочу иметь такой протокол:

 protocol ChatMessageDisplayable {
   static var myCellType: UITableViewCell { get } //Defines the UITableViewCell class this model is associated with
   func configure(cell: /*the associated cell type*/) // Let the model itself configure the cell.
}
  

Затем я в своем ViewController объявлю массив

messageModels = [ChatMessageDisplayable]

И моя реализация UITableViewDataSource:

 public func numberOfSections(in tableView: UITableView) -> Int {
    return messageModels.count
}

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let model = messageModel[indexPath.row]


    let cellIdentifier = /* Name of the UITableViewCell this model is associated with */


    let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)


    model.configure(cell: cell)
    return cell
}
  

Могу ли я в любом случае заархивировать эту цель?

Комментарии:

1. Это может быть ответом на ваш вопрос: youtube.com/watch?v=lpTNaBUFkno

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

3. Есть ли какая-либо причина, по которой вы хотите использовать протокол для установки типа ячейки и не использовать оператор if или переключатель внутри cellForRowAt ?

4. Операторы If. Он может стать огромным и нечитаемым по мере увеличения количества ячеек.

5. Затем используйте оператор switch, чтобы установить тип ячейки для разных случаев.

Ответ №1:

Просто представьте, что ваши данные будут похожи:

 class TableViewModel {
    let items: [Any] = [
        User(name: "John Smith", imageName: "user3"),
        "Hi, this is a message text. Tra la la. Tra la la.",
        Bundle.main.url(forResource: "beach@2x", withExtension: "jpg")!,
        User(name: "Jessica Wood", imageName: "user2"),
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
    ]
}
  

Поэтому обычно мы бы реализовали это в tableView(_:cellForRowAt:) методе со многими if let ...

Один из способов предотвратить это — использовать универсальные типы. Универсальное программирование — отличный способ избежать шаблонного кода и помогает определять ошибки во время компиляции.

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

Давайте создадим первый протокол, которому должна соответствовать каждая ячейка.

 protocol ConfigurableCell {
    associatedtype DataType
    func configure(data: DataType)
}

//example of UserCell
class UserCell: UITableViewCell, ConfigurableCell {
    @IBOutlet weak var avatarView: UIImageView!
    @IBOutlet weak var userNameLabel: UILabel!

    func configure(data user: User) {
        avatarView.image = UIImage(named: user.imageName)
        userNameLabel.text = user.name
    }
}
  

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

 protocol CellConfigurator {
    static var reuseId: String { get }
    func configure(cell: UIView)
}

class TableCellConfigurator<CellType: ConfigurableCell, DataType>: CellConfigurator where CellType.DataType == DataType, CellType: UITableViewCell {
    static var reuseId: String { return String(describing: CellType.self) }

    let item: DataType

    init(item: DataType) {
        self.item = item
    }

    func configure(cell: UIView) {
        (cell as! CellType).configure(data: item)
    }
}
  

Теперь нам нужны некоторые корректировки в нашей ViewModel:

 typealias UserCellConfigurator = TableCellConfigurator<UserCell, User>
typealias MessageCellConfigurator = TableCellConfigurator<MessageCell, String>
typealias ImageCellConfigurator = TableCellConfigurator<ImageCell, URL>

class TableViewModel {
    let items: [CellConfigurator] = [
        UserCellConfigurator(item: User(name: "John Smith", imageName: "user3")),
        MessageCellConfigurator(item: "Hi, this is a message text. Tra la la. Tra la la."),
        ImageCellConfigurator(item: Bundle.main.url(forResource: "beach@2x", withExtension: "jpg")!),
        UserCellConfigurator(item: User(name: "Jessica Wood", imageName: "user2")),
        MessageCellConfigurator(item: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."),
    ]
}
  

Вот и все!

Вы можете легко добавлять новые ячейки без необходимости редактировать код вашего ViewController.

Давайте добавим предупреждающую ячейку в наше табличное представление.

1. Соответствует протоколу ConfigurableCell. 2. Добавьте TableCellConfigurator для этой ячейки в класс ViewModel .

 class WarningCell: UITableViewCell, ConfigurableCell {
    @IBOutlet weak var messageLabel: UILabel!

    func configure(data message: String) {
        messageLabel.text = message
    }
}

//cell configurator for WarningCell
TableCellConfigurator<WarningCell, String>(item: "This is a serious warning!")
  

Для получения дополнительной информации перейдите по этой ссылке

Комментарии:

1. Спасибо, я попробую

2. Как вы будете редактировать модель данных из didselectrowatindexpath

3. Как я могу обновить модель данных в ячейке табличного представления