Изменить размер заголовка UITableView и содержать UITextView (iOS7 + AutoLayout)
Я немного боролся с этим сейчас и хочу убедиться, что делаю это правильно. Для справки, я использую AutoLayout с раскадровками, и это приложение только для iOS7.
У меня есть UITableView с представлением заголовка, и пользовательский интерфейс для HeaderView подключен в раскадровке. Я оставил заголовок в раскадровке, и он содержит несколько элементов, одним из которых является прокручиваемый UITextView.
Вот скриншот того, как это выглядит в раскадровке:
Я затемнил фон заголовка до более темного серого цвета, чтобы он выделялся.
Текст UITextView может быть динамическим, поэтому его высота должна меняться в зависимости от размера текста. Но самое сложное в том, что представление заголовка таблицы также должно регулировать ее высоту, в зависимости от размера этого текста. Я пробовал несколько разных вещей с ограничениями, но на самом деле это не работает должным образом (если я не отключаю AutoLayout и не изменяю размеры программно, чего я действительно хотел бы избежать).
Конечно, я хочу, чтобы это было как можно более чисто с минимальным количеством кода. Я не привязан к использованию заголовка табличного представления, хотя он хорошо работает с моими потребностями, так как я хочу, чтобы он и его текстовое представление прокручивались с фактическими деталями ниже. Но все это, как говорится, если есть более простой подход к выполнению этого (то есть с помощью uilabel), я с удовольствием изменю основную структуру.
Есть мысли о подходе? Не ищу кого-то, кто протянет мне руку через ответ; по возможности, просто ищем хорошие отправные точки. Заранее спасибо!
4 ответа
Некоторое время назад я нашел новое решение, возможно, оно нуждается в настройке для анимации и является экспериментальным, но в некоторых случаях это нормально, поэтому попробуйте:
- Добавьте headerView к UITableView.
- Добавьте подпредставление в headerView, назовем его оболочкой.
- Заставьте высоту обертки быть приспособленной с ее подпредставлениями (через Auto Layout).
- Когда autolayout закончил макет, установите в headerView высоту оболочки. (увидеть
-updateTableViewHeaderViewHeight
) - Переустановите headerView. (увидеть
-resetTableViewHeaderView
)
Вот некоторый код:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self updateTableViewHeaderViewHeight];
}
/**
tableView's tableViewHeaderView contains wrapper view, which height is evaluated
with Auto Layout. Here I use evaluated height and update tableView's
tableViewHeaderView's frame.
New height for tableViewHeaderView is applied not without magic, that's why
I call -resetTableViewHeaderView.
And again, this doesn't work due to some internals of UITableView,
so -resetTableViewHeaderView call is scheduled in the main run loop.
*/
- (void)updateTableViewHeaderViewHeight
{
// get height of the wrapper and apply it to a header
CGRect Frame = self.tableView.tableHeaderView.frame;
Frame.size.height = self.tableHeaderViewWrapper.frame.size.height;
self.tableView.tableHeaderView.frame = Frame;
// this magic applies the above changes
// note, that if you won't schedule this call to the next run loop iteration
// you'll get and error
// so, I guess that's not a clean solution and could be wrapped in @try@catch block
[self performSelector:@selector(resetTableViewHeaderView) withObject:self afterDelay:0];
}
// yeah, guess there's something special in the setter
- (void)resetTableViewHeaderView
{
// whew, this could be animated!
[UIView beginAnimations:@"tableHeaderView" context:nil];
self.tableView.tableHeaderView = self.tableView.tableHeaderView;
[UIView commitAnimations];
}
Все это работает незаметно после первоначального прохода автоматического размещения. Позже, если вы измените содержимое оболочки так, чтобы оно получало другую высоту, по какой-то причине это не сработает (угадайте, что для раскладки UILabel требуется несколько проходов с автоматическим размещением или что-то в этом роде). Я решил это с помощью планирования setNeedsLayout для представления ViewController в следующей итерации цикла выполнения.
Я создал пример проекта для этого: TableHeaderView + Autolayout.
Я бы предложил установить высоту программно. Вы можете вычислить высоту текстового представления с boundingRectWithSize:options:context
Затем вы просто устанавливаете фрейм tableViewHeader.
Как сказано выше, в UITableView высота заголовка не контролируется автоматическим макетом, а
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
Высота нижнего колонтитула задается
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
И, наконец, высота ячейки задается
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
Табличное представление загружает ячейки из раскадровок, файлов XIB или кода. Он использует описанные выше методы, чтобы установить размер фрейма для верхних и нижних колонтитулов и ячеек, а также размер их представлений в соответствии с этим пространством.
Важно понимать, что автоматическое расположение не изменит эти размеры, оно только обрабатывает правила, используемые для размещения представлений внутри их контейнеров.
Если ваши ячейки / колонтитулы используют статические размеры, то это очень просто. Вы просто блокируете высоту всех ваших подпредставлений, настраиваете размер ячейки таким же, какой вы возвращаете в heightFor... и все выглядит так, как должно.
Динамические ячейки / верхние / нижние колонтитулы требуют немного больше работы.
По сути, вас просят сообщить табличному виду размер вашей ячейки / верхнего / нижнего колонтитула, прежде чем вы на самом деле сделали это и оценили все подпредставления.
Вы можете быстро проверить это, установив точку останова в методах cellForRowAtIndexPath и heightForRowAtIndexPath, сначала вызывается высота.
Для динамических высот ячеек таблицы обычным трюком является создание отдельной ячейки в viewDidLoad и ее сохранение для вычисления высот. Затем, когда вы достигнете heightForRowAtIndexPath или heightForHeaderInSection, используйте готовую ячейку для ввода значений, которые вы будете использовать в последней ячейке, для UILabels используйте sizeWithFont (до iOS 7) или boundingRectWithSize (iOS 7.0+) и sizeThatFits для UITextViews. (Для любого, кто читает это в будущем, убедитесь, что эти методы не были заменены чем-то более новым)
Поэтому поместите нужный текст в пустую ячейку, высоту надписей и текстовых представлений, добавьте пробелы между ними и верните окончательный размер, когда вы закончите.
Затем в cellForRowAtIndexPath или headerForSection переделайте установку значений, но в фактической ячейке, которая будет отображаться.
Если ваши расчеты высоты соответствуют вашим настройкам автоматического макета, то все будет идеально вписываться в пространство, которое предназначено для этого.
Если вы хотите изменить высоту ячейки или заголовка после того, как она уже нарисована, вам нужно обновить табличное представление, выполнив что-то вроде:
[self.tableView beginUpdates];
// change the data that will cause cell height to be different in heightForCellAtIndexPath or which will change a header or footer height calculation
[self.tableView endUpdates];
Это заставит просмотр таблицы проверить размеры, которые он имеет, и обновит только ячейки или верхний / нижний колонтитул, если они были изменены.
Представление заголовка табличного представления дается табличному представлению через метод tableView:viewForHeaderInSection: Delegate.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
// create your header view here (read the docs on this method, there are some specific requirements)...
}
Я не знаю, возможно ли создать вид заголовка в раскадровке; Я предполагаю, что вам придется использовать отдельный файл XIB или сделать это программно.
Другими словами, я думаю, что вы движетесь к созданию ограничений в коде, если вам нужно предоставить представления заголовков табличного представления.
С другой стороны, может быть, вам не нужен вид заголовка табличного представления. Возможно, то, что вам нужно, это просто подпредставление над табличным представлением, которое не перемещается, когда пользователь прокручивает таблицу. Для этого вам нужно заново проработать иерархию представлений в IB. Прямо сейчас ваше так называемое представление заголовка находится внутри представления таблицы. Вы этого не хотите.
Или, поскольку похоже, что вы используете статические ячейки, вы можете сделать так, чтобы верхняя статическая ячейка приобрела вид вашего так называемого представления заголовка.