четверг, 11 января 2018 г.

iOS. Swift. Enum with associated values for UITableView, UICollectionView

Статья из категории `Я решил это так, возможно у тебя есть другое решение :)`.

К нам приходит очередная задача - `экран фильтров`, на котором отображаются диапазон цен, категории ( можно добавлять несколько категорий к фильтру ), тип продавца (магазин, частный продавец) и тд.

Хороший вариант использовать UITableView или UICollectionView. Но как отобразить настолько разные данные в таблице?

Для таблицы нам нужен массив элементов и хотелось бы не делать никаких преобразований данных в методе cellForRowAtIndexPath. Так как фильтра содержать достаточно разные данные,  а элемент массива у нас должен быть одного типа, нам нужно как то сконвертировать модель фильтра в понятный формат для таблицы. Лучше всего в данном случае ( но это не точно ) подойдет enum.


Представление модели фильтра через enum для таблицы.

enum Item {
    case price(min: Int, max: Int, cur: Int)
    case seller(type: String)
    case categories(selected: [String], all: [String])
    ...
}


В результате использования enum-a мы можем сделать массив элементов для таблицы.

items = [
    .price(min: 100, max: 5000, cur: 1200), // ценовой диапазон и текущее значение
    .seller(type: "private"),
    .categories(selected: ["a"], all: ["a", "b", "c"]) // категории фильтра 
]


Полный пример выглядит следующим образом:

class FilterViewController {

    enum SellerType {
        case all, shop, private
    }

    enum Item {
        case price(min: Int, max: Int, cur: Int)
        case seller(type: SellerType)
        case categories(selected: [String], all: [String])
        ...
    }

    var items: [Item] = []

    override func viewDidLoad() {
        super.viewDidLoad()

        items = [
            .price(min: 100, max: 5000, cur: 1200),
            .seller(type: .private),
            .categories(selected: ["a"], all: ["a", "b", "c"])
        ]
    }
}

extension FilterViewController: UITableViewDataSource {

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch(items[indexPath.row]) {
        case let .price(min, max, cur):
            let cell = tableView.dequeueReusableCell(withIdentifier: "PriceCell", for: indexPath)
            // отображаем min, max, cur в ячейке

            return cell

        case let .seller(type):
            let cell = tableView.dequeueReusableCell(withIdentifier: "SellerCell", for: indexPath)
            // отображаем type в ячейке (или переключаем switch, segmented control)

            return cell

        case let .categories(selected, all):
            let cell = tableView.dequeueReusableCell(withIdentifier: "CategoriesCell", for: indexPath)
            // отображаем selected, all в ячейке

            return cell
        }
    }
}

В результате наш метод `cellForRow` становится достаточно простым и не нагружен никакой бизнес логикой. Мы можем протестировать создание, обновление нашего массива элементов.

P.S. Оставляем комментарии, темы для рассмотрения. И кликаем +1, f, в, t, что располагаются чуть ниже статьи.