Должен ли я использовать динамический stackview внутри UITableViewCell или использовать UICollectionView?

#ios #swift #uitableview #uicollectionview

#iOS #swift #uitableview #uicollectionview

Вопрос:

введите описание изображения здесь

Здравствуйте. Я создаю приложение для бронирования авиабилетов, которое имеет сложную UITableViewCell. По сути, это простая карточка с тенью, которая имеет множество стековых просмотров. Первый stackview, который вы видите на изображении, предназначен для меток. Он горизонтальный и динамический. Следующий stackview показывает рейсы. Он имеет сложный пользовательский вид, но для простоты он показан с зеленой рамкой. Он также динамический, поэтому для него мне нужен отдельный stackview. Следующий stackview предназначен для авиакомпаний, которые могут обрабатывать это бронирование. Я называю их операторами. Он также динамический, поэтому я создаю для них еще один stackview. И все эти представления стека находятся внутри некоторого основного stackview. Вы можете спросить, почему я создал отдельные представления стека вместо одного? Потому что метки выше могут быть скрыты. А также интервалы во всех stackviews разные.

  1. Это действительно сложный дизайн. Я последовал описанному выше подходу и создал UITableViewCell. Но производительность действительно плохая. Причина проста: я делаю слишком много вещей в cellForRowAt. configure Метод UITableViewCell вызывается каждый раз, когда ячейка удаляется из очереди. Это означает, что я должен каждый раз очищать свой stackview и только после этого добавлять свои представления. Я думаю, что это действительно влияет на производительность. Я не рассказываю о других операторах if / else внутри ячейки. Первый вопрос: как я могу увеличить производительность прокрутки UITableViewCell в этом случае?

  2. Некоторые разработчики считают, что UITableView должен быть уничтожен. UICollectionView правит миром. Хорошо, но могу ли я использовать UICollectionView с этим дизайном? Да, конечно, но над картой будет одна UICollectionViewCell, и я просто не избегаю проблемы. Другое решение — создать отдельную UICollectionViewCell для метки (см. На изображении), полета и оператора. Это определенно повысит производительность. Но как я могу заставить их всех жить внутри card?

PS Что находится внутри моего метода cellForRowAt? Существует только один configure метод и присваивание значений закрытию. Но метод configure довольно сложный. Он получает некоторый протокол, который имеет множество вычисляемых свойств. Я передаю реализацию этого протокола configure методу. Протокол такой:

 protocol Booking {
  var flights: [Flight] { get }
  var operators: [Operator] { get }
  var labels: [Label] { get }
  var isExpanded: Bool { get set }
}
  

Реализация этого протокола также сложна. Существует множество функций отображения и операторов if / else. Некоторые манипуляции со строками. Итак, это вызывает проблему? Как я могу это решить? Избегая вычисления свойств и просто передавая свойства (полеты, операторы) в реализацию?

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

1. Не показывая, насколько «сложны» ваши ячейки, трудно помочь. Одна из возможностей: вместо того, чтобы каждый раз удалять / повторно добавлять вложенные представления, удаляйте только дополнительные или добавляйте новые вложенные представления. Кроме того, если у вас будет, скажем, максимум 10 «операторов», вы можете создать 10 меток операторов (или представлений, какими бы они ни были) при создании экземпляра ячейки, а затем отображать или скрывать по мере необходимости. И, в зависимости от того, что вы считаете действительно плохой производительностью, вам может потребоваться пересмотреть свой подход к дизайну.

2. У меня есть метод configure в моей ячейке, где я передаю массив рейсов, массив операторов, например. Затем я добавляю их в stackview. Итак, как я могу узнать, какие из них уже добавлены? Проверьте длину arrangedSubviews? Но UITableView вызывает cellForRowAt для повторного использования ячеек, поэтому я полагаю, что некоторые представления будут пустыми, если я проверю длину.

Ответ №1:

Как я уже сказал в своем комментарии, не видя полной информации, трудно помочь. И для начала это довольно широкий вопрос.

Однако это может оказать вам некоторую помощь…

Рассмотрим два класса ячеек. В каждом из них «базовые» элементы добавляются при создании ячейки — эти элементы будут существовать независимо от фактических данных ячейки:

  • ваш «основной» вид стека
  • ваш вид стека «labels»
  • ваш вид стека «перелетов»
  • ваш вид стека «операторы»

Чтобы упростить ситуацию, давайте просто подумаем о представлении стека «операторы», и мы скажем, что каждая «строка» представляет собой одну метку.

То, что вы можете делать сейчас, когда устанавливаете данные в ячейке, примерно так…

В функции инициализации ячейки:

     // create your main and 3 sub-stackViews
  

Затем, когда вы устанавливаете данные из cellForRowAt :

     // remove all labels from operator stack
    operatorStack.arrangedSubviews.forEach {
        $0.removeFromSuperview()
    }

    // add new label for each operator
    thisBooking.operators.forEach { op in
        let v = UILabel()
        v.font = .systemFont(ofSize: 15)
        v.text = op.name
        operatorStack.addArrangedSubview(v)
    }
    
  

Итак, каждый раз, когда вы удаляете ячейку из cellForRowAt очереди и устанавливаете ее данные, вы удаляете все представления «operator» из представления стека, а затем заново создаете и повторно добавляете их.

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

В функции инициализации ячейки:

     // create your main and 3 sub-stackViews

    // add 10 labels to operator stack
    //  when cell is created
    for _ in 1...10 {
        let v = UILabel()
        v.font = .systemFont(ofSize: 15)
        operatorStack.addArrangedSubview(v)
    }
  

Затем, когда вы устанавливаете данные из cellForRowAt :

     // set all labels in operator stack to hidden
    operatorStack.arrangedSubviews.forEach {
        $0.isHidden = true
    }
    
    // fill and unhide labels as needed
    for (op, v) in zip(thisBooking.operators, operatorStack.arrangedSubviews) {
        guard let label = v as? UILabel else { fatalError("Setup was wrong!") }
        label.text = op.name
        label.isHidden = false
    }
  

Таким образом, мы создаем и добавляем «представления оператора» только один раз — при создании ячейки. Когда он удаляется из очереди / используется повторно, мы просто скрываем неиспользуемые представления.

Опять же, поскольку вы говорите, что у вас «действительно сложный дизайн», нужно учитывать гораздо больше… и, как я уже упоминал, вам, возможно, придется пересмотреть весь свой подход.

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

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

1. Ситуация такова, что я не знаю, сколько операторов я могу получить с сервера. Требования проекта заключаются в том, что он должен быть динамичным. Конкретная сумма неизвестна. Тогда, как бы вы подошли к этому?

2. @neo — опять же, не зная всего вашего проекта, сложно сказать. В дизайне приложения есть нечто большее, чем «мне нужно показать все эти данные». Возможно, вы пытаетесь показать слишком много всего одновременно — вам также нужно подумать о том, что пользователь захочет увидеть. В качестве примера (не связанного напрямую с тем, что вы делаете): предположим, я хочу показать 5000 записей из базы данных? Кто-нибудь захочет прокрутить таблицу из 5000 строк? Вероятно, нет. Итак … если в вашей «ячейке» может быть 100 «операторов», как вы думаете, пользователь хочет прокрутить их все, чтобы перейти к следующей строке?