Мне нравится решение @Cristik, некоторое время назад у меня была такая же проблема, и мое решение следует тем же принципам; Итак, это то, что я предлагаю, исходя из моих требований:
Чтобы сделать его более общим, элементы таблицы не должны наследоваться от класса, специализированного для расширяющейся функциональности, вместо этого должен быть протокол, определяющий необходимые свойства.
Не должно быть ограничений на количество уровней, которые мы можем расширять. Таким образом, в таблице может быть опция, дополнительная опция, дополнительная опция и т. Д.
Табличное представление должно отображать или скрывать ячейки с использованием любой из обычных анимаций (без reloadData
).
Действие развертывания не обязательно должно быть привязано к пользователю, выбирающему ячейку, например, ячейка может иметь UISwitch.
Упрощенная версия реализации (https://github.com/JuanjoArreola/ExpandableCells) выглядит следующим образом:
Сначала протокол:
protocol CellDescriptor: class {
var count: Int { get }
var identifier: String! { get }
}
Нерасширяемая ячейка всегда имеет счет 1:
extension CellDescriptor {
var count: Int { return 1 }
}
Затем протокол расширяемой ячейки:
protocol ExpandableCellDescriptor: CellDescriptor {
var active: Bool { get set }
var children: [CellDescriptor] { get set }
subscript(index: Int) -> CellDescriptor? { get }
func indexOf(cellDescriptor: CellDescriptor) -> Int?
}
В Swift замечательно то, что мы можем написать часть реализации в расширении протокола, и все соответствующие классы могут использовать реализацию по умолчанию, поэтому мы можем написать реализацию count
subscript
и indexOf
, а также пару других полезных функций, подобных этой:
extension ExpandableCellDescriptor {
var count: Int {
var total = 1
if active {
children.forEach({ total += $0.count })
}
return total
}
var countIfActive: Int {
...
}
subscript(index: Int) -> CellDescriptor? {
...
}
func indexOf(cellDescriptor: CellDescriptor) -> Int? {
...
}
func append(cellDescriptor: CellDescriptor) {
children.append(cellDescriptor)
}
}
Полная реализация находится в файле CellDescriptor.swift.
Кроме того, в том же файле есть класс с именем CellDescriptionArray
, который реализует ExpandableCellDescriptor
и не отображает отдельную ячейку.
Теперь любой класс может соответствовать предыдущим протоколам без необходимости наследовать от определенного класса, для примера кода в github я создал пару классов: Option
и ExpandableOption
, вот как выглядит ExpandableOption
:
class ExpandableOption: ExpandableCellDescriptor {
var delegate: ExpandableCellDelegate?
var identifier: String!
var active: Bool = false {
didSet {
delegate?.expandableCell(self, didChangeActive: active)
}
}
var children: [CellDescriptor] = []
var title: String?
}
А это один из подклассов UITableViewCell:
class SwitchTableViewCell: UITableViewCell, CellDescrptionConfigurable {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var switchControl: UISwitch!
var cellDescription: CellDescriptor! {
didSet {
if let option = cellDescription as? ExpandableOption {
titleLabel.text = option.title
switchControl.on = option.active
}
}
}
@IBAction func activeChanged(sender: UISwitch) {
let expandableCellDescriptor = cellDescription as! ExpandableCellDescriptor
expandableCellDescriptor.active = sender.on
}
}
Обратите внимание, что вы можете настроить ячейку и ее класс так, как вам нравится, вы можете добавлять изображения, метки, переключатели и т. Д .; никаких ограничений и изменений в протоколах не требуется.
Наконец, в TableViewController мы создаем дерево опций:
var options = CellDescriptionArray()
override func viewDidLoad() {
super.viewDidLoad()
let account = ExpandableOption(identifier: "ExpandableCell", title: "Account")
let profile = Option(identifier: "SimpleCell", title: "Profile")
let isPublic = ExpandableOption(identifier: "SwitchCell", title: "Public")
let caption = Option(identifier: "SimpleCell", title: "Anyone can see this account")
isPublic.append(caption)
account.append(profile)
account.append(isPublic)
options.append(account)
let group = ExpandableOption(identifier: "ExpandableCell", title: "Group")
group.append(Option(identifier: "SimpleCell", title: "Group Settings"))
options.append(group)
...
}
Остальная часть реализации теперь очень проста:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return options.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let option = options[indexPath.row]!
let cell = tableView.dequeueReusableCellWithIdentifier(option.identifier, forIndexPath: indexPath)
(cell as! CellDescrptionConfigurable).cellDescription = option
(option as? ExpandCellInformer)?.delegate = self
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
guard let option = options[indexPath.row] else { return }
guard let expandableOption = option as? ExpandableOption else { return }
if expandableOption.identifier == "ExpandableCell" {
expandableOption.active = !expandableOption.active
}
}
func expandableCell(expandableCell: ExpandableCellDescriptor, didChangeActive active: Bool) {
guard let index = options.indexOf(expandableCell) else { return }
var indexPaths = [NSIndexPath]()
for row in 1..<expandableCell.countIfActive {
indexPaths.append(NSIndexPath(forRow: index + row, inSection: 0))
}
if active {
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade)
} else {
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade)
}
}
Это может показаться большим количеством кода, но большая его часть написана только один раз, большая часть информации, необходимой для правильного отображения табличного представления, существует в файле CellDescriptor.swift, код конфигурации ячейки существует внутри подклассов UITableViewCell, и относительно немного кода в самом TableViewController.
Надеюсь, поможет.
person
juanjo
schedule
10.01.2016