Изменить стандартное поведение прокрутки заголовка раздела UITableView

147

У меня есть UITableView с двумя разделами. Это простой вид таблицы. Я использую viewForHeaderInSection для создания пользовательских представлений для этих заголовков. Все идет нормально.

Режим прокрутки по умолчанию заключается в том, что при обнаружении раздела заголовок раздела остается закрепленным под панелью навигации до тех пор, пока не отобразится следующий раздел.

У меня такой вопрос: могу ли я изменить поведение по умолчанию, чтобы заголовки разделов НЕ оставались на якоре сверху, а прокручивались под панелью навигации с остальными строками разделов?

Я что-то упускаю из виду?

Спасибо.

davidjhinson
источник
1
Это должно быть одним из лучших письменных вопросов на этом сайте! :) Спасибо.
Толстяк
1
Здесь вы
найдете

Ответы:

175

Я решил эту проблему, чтобы отрегулировать в contentOffsetсоответствии с contentInsetв UITableViewControllerDelegate(распространяется UIScrollViewDelegate) , как это:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
       CGFloat sectionHeaderHeight = 40;
   if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
       scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
   } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
       scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
   }
}

Единственная проблема здесь заключается в том, что при прокрутке назад к вершине он немного теряет отскок.


{ПРИМЕЧАНИЕ: «40» должно быть точной высотой ВАШЕГО заголовка секции 0. Если вы используете число, которое больше, чем высота заголовка раздела 0, вы увидите, что это влияет на ощущение пальца (попробуйте как «1000», и вы увидите, что поведение отскока вверху странно). если число соответствует высоте заголовка раздела 0, ощущение пальца кажется либо идеальным, либо почти идеальным.}

awulf
источник
1
Идеальное решение, чем указанное выше.
Пратумка
4
Спасибо! Это идеальное решение. У вас есть решение для раздела нижнего колонтитула?
Туан Нгуен
12
Это решение неправильно обрабатывает нажатие на строку меню, которая должна прокручиваться до верхней части списка.
Рид Эллис
если вам нужно такое же поведение для представления нижнего колонтитула раздела, просто измените последнюю строку следующим образом: scrollView.contentInset = UIEdgeInsetsMake (0, 0, sectionHeaderHeight, 0)
alex
Кажется, этого достаточно: scrollView.contentInset = UIEdgeInsetsMake (scrollView.contentOffset.y> = 0? -ScrollView.contentOffset.y: 0, 0, 0, 0);
MacMark
85

Вы также можете добавить раздел с нулями строк в верхней части и просто использовать нижний колонтитул предыдущего раздела в качестве заголовка для следующего.

voidStern
источник
13
В моем случае, когда у меня есть более 2 разделов, нижний колонтитул будет закреплен внизу ..
Джозеф Лин
3
Это креативно, но вам нужно настроить indexPath, если вы используете NSFetchedResultsController для загрузки таблицы.
XJones
+1, безусловно, ЛУЧШЕЕ решение - мне потребовалось 3 строки для реализации!
Джон
Бриллиант Бриллиант :-)
iphonic
Как это будет работать для нижних колонтитулов? Был бы признателен за помощь! Спасибо
darwindeeds
36

Если бы я делал это, я бы воспользовался тем, что UITableViews в стиле Plain имеют липкие заголовки, а заголовки в стиле Grouped - нет. Я бы, по крайней мере, попытался бы использовать пользовательскую ячейку таблицы, чтобы имитировать появление простых ячеек в сгруппированной таблице.

Я на самом деле не пробовал это, так что это может не сработать, но это то, что я бы предложил сделать.

Колин Барретт
источник
Вероятно, будет работать, но это было опробовано? И кажется, что работа @ voidStern отлично работает !!
Бентфорд
Ответ voidStern не работает для меня, когда у меня более двух разделов. Ответ Колина проще / элегантнее, без необходимости вручную сдвигать номер раздела и добавлять количество разделов.
Джозеф Лин
Советы: используйте tableView.separatorColor = [UIColor clearColor];для имитации простого табличного представления.
Джозеф Лин
3
Я пытался сделать это, но когда я не мог сделать сгруппированные ячейки таблицы похожими на простые, то есть убрать левый и правый поля с обеих сторон ячеек. Как ты это сделал?
Адам Картер
1
Большое спасибо за разъяснение UITableViewStyle, то есть сгруппированный и простой, который решает, будет ли заголовок / нижний колонтитул раздела tableview липким или нет. Большое спасибо @Colin Барретт
Дхавал Х. Нена
28

Я знаю, что это поздно, но я нашел окончательное решение!

Что вы хотите сделать, если у вас есть 10 разделов, пусть dataSource возвращает 20. Используйте четные числа для заголовков разделов и нечетные числа для содержимого разделов. что-то вроде этого

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }else {
        return 5;
    }
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        return [NSString stringWithFormat:@"%i", section+1];
    }else {
        return nil;
    }
}

Вуаля! : D

LocoMike
источник
отличная идея. но если у вас есть заголовки разделов, я подозреваю, что это испортит их.
roocell
Зачем? Вы можете сделать то же самое с заголовками разделов. Просто запомните ноль для нечетных чисел и название для четного числа.
LocoMike
1
да - этот метод прекрасно работает. Это было много бухгалтерии (вероятно, связано с тем, как я должен получить названия разделов), но это сработало. Фокус в том, что numberOfRowsInSection и cellForRowAtIndexPath используют if (section% 2! = 0 && section! = 0), тогда как titleForHeaderInSection и другие индексные функции раздела используют if (section% 2 == 0)
roocell
составитель должен выбрать это как ответ. Было бы замечательно, если бы был способ создать подкласс uitableview для автоматической привязки заголовков разделов.
Roocell
Это сработало лучше для меня. Сначала я попробовал решение @ Colin, но оно не работало хорошо с сильно настроенными, UITableViewкоторые я использую.
Куби
15

Есть несколько вещей, которые нужно сделать, чтобы решить эту проблему не хакерским способом:

  1. Установите стиль представления таблицы на UITableViewStyleGrouped
  2. Установите вид таблицы backgroundColorна[UIColor clearColor]
  3. Установите для backgroundViewкаждой ячейки табличного представления пустое представление с помощьюbackgroundColor [UIColor clearColor]
  4. При необходимости установите табличное представление rowHeightсоответствующим образом или переопределите, tableView:heightForRowAtIndexPath:если отдельные строки имеют разную высоту.
Нил Галл
источник
(+1) @Neil, я попробовал ваш ответ, но, к сожалению, UITableViewStyleGroupedячейки таблицы имеют дополнительные поля, которые изменяют их выравнивание с заголовком. Это проблема, когда вы действительно хотите изобразить таблицу из нескольких столбцов и использовать заголовок для отображения заголовков столбцов (а затем выровнять отдельные записи таблицы с этими заголовками).
брейнджем
15

Первоначально опубликовано здесь , быстрое решение с использованием IB. То же самое можно сделать программно, хотя и довольно просто.

Вероятно, более простой способ добиться этого (с помощью IB):

Перетащите UIView на свой TableView, чтобы сделать его заголовком.

  1. Установите высоту представления этого заголовка на 100 пикселей
  2. Установите для tableView contentInset (top) значение -100
  3. Заголовки разделов теперь будут прокручиваться, как и любая обычная ячейка.

Некоторые люди говорили, что это решение скрывает первый заголовок, однако я не заметил такой проблемы. Это отлично сработало для меня и было самым простым решением, которое я видел до сих пор.

доктор
источник
Обратите внимание, что вам, вероятно, следует настроить вид на использование цвета фона clearcolor, в противном случае, если вы прокрутите верхнюю часть, вы увидите белый отскок.
Док
3
Это действительно скрывает мой первый заголовок
Лина
Лина, на какой версии iOS ты работаешь? Является ли представление UITableViewController или UIView с UITableView в нем? Я не уверен, почему для некоторых людей это решение скрывает первый заголовок, поэтому я пытаюсь найти какие-либо несоответствия.
Док
Это решение идеально. Я ищу бесконечный верхний и нижний вид фона ячейки - это полностью завершает это. Ницца!
Тейлор Холлидей,
Это решение отлично. Проще сделать, чем другие ответы в этой теме. На мой взгляд, это должен быть принятый ответ.
Джон Роджерс
15

Я не был доволен решениями, описанными здесь до сих пор, поэтому я попытался объединить их. В результате получается следующий код, вдохновленный @awulf и @cescofry. Это работает для меня, потому что у меня нет реального заголовка табличного представления. Если у вас уже есть заголовок табличного представления, возможно, вам придется настроить высоту.

// Set the edge inset
self.tableView.contentInset = UIEdgeInsetsMake(-23.0f, 0, 0, 0);

// Add a transparent UIView with the height of the section header (ARC enabled)
[self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 23.0f)]];
Томас Кекейсен
источник
Это было самое простое решение, которое я нашел.
Зорайр
Это сработало для меня на iOS9.2, поэтому, несмотря на более старый ответ, оно все еще действует. И это не скрывало заголовок первого раздела. Работает, и работает отлично.
idStar
Отлично. Если у вашего табличного представления уже есть заголовок, просто увеличьте его высоту на смещение, затем увеличьте верхнюю константу ограничения содержимого заголовка, чтобы сместить содержимое вниз в исходное положение
Jason
14

Просто измените стиль TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

UITableViewStyle Документация :

UITableViewStylePlain - простое табличное представление. Любые верхние или нижние колонтитулы отображаются в виде встроенных разделителей и плавают при прокрутке табличного представления.

UITableViewStyleGrouped - табличное представление, разделы которого представляют отдельные группы строк. Верхние и нижние колонтитулы раздела не плавают.

Артур С
источник
отличный ответ, работал для меня. не нужно обходиться
Шамс Ахмед
9

Выберите сгруппированный стиль TableView

Выберите стиль сгруппированного табличного представления в Инспекторе атрибутов вашего tableView в раскадровке.

Арджун Шукла
источник
5

Установите заголовок таблицы с прозрачным видом с той же высотой заголовка в разрезе. Также инициируйте просмотр таблицы с помощью y-frame в -height.

self.tableview = [[UITableView alloc] initWithFrame:CGRectMake(0, - height, 300, 400)];

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)] autorelease];
[self.tableView setTableHeaderView:headerView];
cescofry
источник
4

Я нашел альтернативное решение, используйте первую ячейку каждого раздела вместо реального раздела заголовка, это решение не выглядит таким чистым, но работает настолько хорошо, что вы можете использовать определенную ячейку прототипа для раздела заголовков и в методе cellForRowAtIndexPath запросите indexPath.row == 0, если true, используйте ячейку прототипа раздела заголовка, иначе используйте вашу ячейку прототипа по умолчанию.

AlexBB
источник
Если вы все еще хотите работать с indexPath.row& indexPath.section, убедитесь, что вы возвращаете высоту 0.0fдля, - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionчтобы ваши заголовки были невидимы.
Суп
4

Измените ваш стиль TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

Согласно документации Apple для UITableView:

UITableViewStylePlain - простое табличное представление. Любые верхние или нижние колонтитулы отображаются в виде встроенных разделителей и плавают при прокрутке табличного представления.

UITableViewStyleGrouped - табличное представление, разделы которого представляют отдельные группы строк. Верхние и нижние колонтитулы раздела не плавают. Надеюсь, что это небольшое изменение поможет вам ..

Акс
источник
2

Теперь, когда сгруппированный стиль выглядит в основном так же, как простой стиль в iOS 7 (с точки зрения плоскостности и фона), для нас самым лучшим и простым (т.е. наименее хакерским) решением было просто изменить стиль табличного представления на сгруппированный. Поддавливание с помощью contentInsets всегда было проблемой, когда мы интегрировали прокручиваемую навигационную панель вверху. При использовании стиля сгруппированного табличного представления он выглядит одинаково (с нашими ячейками), а заголовки разделов остаются неизменными. Нет странностей прокрутки.

Грег Комбс
источник
Это лучшее решение, особенно если ячейки являются пользовательскими, и, кажется, просто прекращается «удержание» заголовка раздела, когда свиток достигает вершины.
переработанная сталь
1

Назначьте отрицательную вставку для вашего tableView. Если у вас есть заголовки секций высотой 22px, и вы не хотите, чтобы они были липкими, сразу после перезагрузки Data add:

self.tableView.contentInset = UIEdgeInsetsMake(-22, 0, 0, 0); 
self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height+22); 

Работает как шарм для меня. Работает и для нижних колонтитулов, просто назначьте отрицательную вставку внизу.

Майк А
источник
Это просто отрубает первый заголовок ??
cannyboy
У меня отлично сработало !! Спасибо
Даниэль
0

Я добавляю таблицу в представление прокрутки, и это, кажется, работает хорошо.

Dolbex
источник
0

Проверьте мой ответ здесь . Это самый простой способ реализовать заголовки не плавающих разделов без каких-либо взломов.

Сумит Анантвар
источник
0

Ответ @ LocoMike лучше всего подходил для моего tableView, однако он сломался и при использовании нижних колонтитулов. Итак, это исправленное решение при использовании верхних и нижних колонтитулов:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return (self.sections.count + 1) * 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section % 3 != 1) {
        return 0;
    }
    section = section / 3;
    ...
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return 0;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return 0;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    int section = indexPath.section;
    section = section / 3;
    ...
}
Marmor
источник
0

Swift версия ответа @awulf, которая прекрасно работает!

func scrollViewDidScroll(scrollView: UIScrollView) {
    let sectionHeight: CGFloat = 80
    if scrollView.contentOffset.y <= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake( -scrollView.contentOffset.y, 0, 0, 0)
    }else if scrollView.contentOffset.y >= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeight, 0, 0, 0)
    }
}
Джефф
источник
-2

Я узнал, что просто установка свойства tableHeaderView делает это, то есть:

 tableView.tableHeaderView = customView;

и это все.

Soni
источник