#ios #swift #uitableview #uicollectionview
#iOS #swift #uitableview #uicollectionview
Вопрос:
Здравствуйте. Я создаю приложение для бронирования авиабилетов, которое имеет сложную UITableViewCell. По сути, это простая карточка с тенью, которая имеет множество стековых просмотров. Первый stackview, который вы видите на изображении, предназначен для меток. Он горизонтальный и динамический. Следующий stackview показывает рейсы. Он имеет сложный пользовательский вид, но для простоты он показан с зеленой рамкой. Он также динамический, поэтому для него мне нужен отдельный stackview. Следующий stackview предназначен для авиакомпаний, которые могут обрабатывать это бронирование. Я называю их операторами. Он также динамический, поэтому я создаю для них еще один stackview. И все эти представления стека находятся внутри некоторого основного stackview. Вы можете спросить, почему я создал отдельные представления стека вместо одного? Потому что метки выше могут быть скрыты. А также интервалы во всех stackviews разные.
-
Это действительно сложный дизайн. Я последовал описанному выше подходу и создал UITableViewCell. Но производительность действительно плохая. Причина проста: я делаю слишком много вещей в cellForRowAt.
configure
Метод UITableViewCell вызывается каждый раз, когда ячейка удаляется из очереди. Это означает, что я должен каждый раз очищать свой stackview и только после этого добавлять свои представления. Я думаю, что это действительно влияет на производительность. Я не рассказываю о других операторах if / else внутри ячейки. Первый вопрос: как я могу увеличить производительность прокрутки UITableViewCell в этом случае? -
Некоторые разработчики считают, что 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 «операторов», как вы думаете, пользователь хочет прокрутить их все, чтобы перейти к следующей строке?