Как реализовать пользовательскую таблицу представления таблицы Заголовки и нижние колонтитулы с раскадровки

Без использования раскадровки мы могли бы просто перетащить UIView на canvas, выложить его, а затем установить его в tableView:viewForHeaderInSection или tableView:viewForFooterInSection .

Как это сделать с помощью StoryBoard, где мы не можем перетащить UIView на canvas

Просто используйте ячейку прототипа в качестве заголовка раздела и / или нижнего колонтитула.

  • добавьте дополнительную ячейку и поместите в нее нужные элементы.
  • установите идентификатор для чего-то определенного (в моем случае SectionHeader)
  • реализовать метод tableView:viewForHeaderInSection: или tableView:viewForFooterInSection: метод
  • используйте [tableView dequeueReusableCellWithIdentifier:] чтобы получить заголовок
  • реализовать метод tableView:heightForHeaderInSection:

(см. экранный снимок)

 -(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSString *CellIdentifier = @"SectionHeader"; UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (headerView == nil){ [NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"]; } return headerView; } 

Изменить: как изменить заголовок заголовка (комментарий):

  1. Добавить метку в ячейку заголовка
  2. установите метку метки на определенное число (например, 123)
  3. В вашем приложении tableView:viewForHeaderInSection: получите ярлык, вызвав:
  UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. Теперь вы можете использовать метку, чтобы установить новый заголовок:
  [label setText:@"New Title"]; 

Я знаю, что этот вопрос был для iOS 5, но для будущих читателей обратите внимание, что эффективный iOS 6 теперь может использовать dequeueReusableHeaderFooterViewWithIdentifier вместо dequeueReusableCellWithIdentifier .

Поэтому в viewDidLoad вызовите либо registerNib:forHeaderFooterViewReuseIdentifier: либо registerClass:forHeaderFooterViewReuseIdentifier: Затем в viewForHeaderInSection вызовите tableView:dequeueReusableHeaderFooterViewWithIdentifier: Вы не используете прототип ячейки с этим API (это либо представление на основе NIB, либо программно созданное представление), но это новый API для перечеркнутых верхних и нижних колонтитулов.

В iOS 6.0 и выше все изменилось с помощью нового API dequeueReusableHeaderFooterViewWithIdentifier .

Я написал руководство (проверено на iOS 9), которое можно резюмировать следующим образом:

  1. Подclass UITableViewHeaderFooterView
  2. Создайте Nib с видом подclassа и добавьте 1 вид контейнера, который содержит все другие представления в верхнем и нижнем колонтитуле
  3. Зарегистрируйте viewDidLoad в viewDidLoad
  4. Внедрите viewForHeaderInSection и используйте dequeueReusableHeaderFooterViewWithIdentifier чтобы вернуть верхний / нижний колонтитул

Я получил его в iOS7, используя прототип ячейки в раскадровке. У меня есть кнопка в моем заголовке заголовка пользовательского раздела, который запускает segue, который настроен в раскадровке.

Начните с решения Tieme

Как отмечает pedro.m, проблема заключается в том, что нажатие на заголовок раздела вызывает выбор первой ячейки в секции.

Как отмечает Пол Вон, это фиксируется, возвращая содержимое содержимого cellView вместо целой ячейки.

Однако, как отмечает Hons, длительное нажатие на заголовок секции приведет к сбою приложения.

Решение состоит в том, чтобы удалить любые указатели gesture из contentView.

 -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSString *CellIdentifier = @"SectionHeader"; UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; while (sectionHeaderView.contentView.gestureRecognizers.count) { [sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]]; } return sectionHeaderView.contentView; } 

Если вы не используете жесты в своих заголовках заголовков секций, этот маленький взлом, похоже, делает это.

Если вы используете раскадровки, вы можете использовать ячейку прототипа в представлении таблицы для компоновки вашего заголовка. Установите уникальный идентификатор и viewForHeaderInSection, вы можете удалить ячейку с этим идентификатором и передать его в UIView.

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

Создайте новый пустой файл classа интерфейса. Перетащите UIView на canvas, по желанию.

Загрузите ручку вручную, назначьте соответствующий раздел заголовка / нижнего колонтитула в методах делегата viewForHeaderInSection или viewForFooterInSection.

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

Если вам нужна быстрая реализация, следуйте инструкциям принятого ответа, а затем в UITableViewController реализуйте следующие методы:

 override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let cell = self.tableView.dequeueReusableCell(withIdentifier: "CustomHeader") as! CustomHeaderUITableViewCell return cell } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 75 } 

Когда вы вернетесь в contentView, у вас будет две проблемы:

  1. авария, связанная с жестов
  2. вы не повторно используете contentView (каждый раз при вызове viewForHeaderInSection вы создаете новую ячейку)

Решение:

Класс Wrapper для заголовка таблицы \ footer. Это просто контейнер, унаследованный от UITableViewHeaderFooterView , который содержит ячейку внутри

https://github.com/Magnat12/MGTableViewHeaderWrapperView.git

Зарегистрируйте class в UITableView (например, в viewDidLoad)

 - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"]; } 

В UITableViewDelegate:

 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"]; // init your custom cell ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell; if (!cell) { cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"]; view.cell = cell; } // Do something with your cell return view; } 

Для создания заголовков / нижних колонтитулов я делал следующее:

  • Добавить controller представления формы для заголовка / нижнего колонтитула раздела в раскадровку
  • Обрабатывать все содержимое заголовка в controllerе представления
  • В controllerе табличного представления предусмотрены изменяемые массивы controllerов представлений для заголовков / [NSNull null] колонтитулов раздела, [NSNull null] с помощью [NSNull null]
  • В viewForHeaderInSection / viewForFooterInSection, если диспетчер представлений еще не существует, создайте его с помощью storyboardsViewControllerWithIdentifier, запомните его в массиве и верните представление диспетчера представлений

Вы должны использовать решение viewWithTag: в качестве базы, но забудьте о viewWithTag: и другие подозрительные подходы, вместо этого попробуйте перезагрузить свой заголовок (перезагрузив этот раздел).

Поэтому после того, как вы AutoLayout свой собственный заголовок AutoLayout со всеми фантастическими материалами AutoLayout , просто удалите его и верните ContentView после настройки, например:

 -(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSString *CellIdentifier = @"SectionHeader"; SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; sectionHeaderCell.myPrettyLabel.text = @"Greetings"; sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent return sectionHeaderCell.contentView; } 

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

Я добавил элемент подclassа Button из UIButton (имя подclassа «ButtonWithArgument») в ячейку прототипа заголовка и удалил текст заголовка (жирным шрифтом «Заголовок» является другой UILabel в ячейке прототипа)

Кнопка в интерфейсе Builder

затем установите кнопку на весь вид заголовка и добавьте индикатор раскрытия информации с помощью трюка Avario

 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSString *CellIdentifier = @"PersonGroupHeader"; UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if(headerView == nil) { [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]]; } // https://stackoverflow.com/a/24044628/3075839 while(headerView.contentView.gestureRecognizers.count) { [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]]; } ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4]; button.frame = headerView.bounds; // set tap area to entire header view button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside]; // https://stackoverflow.com/a/20821178/3075839 UITableViewCell *disclosure = [[UITableViewCell alloc] init]; disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator; disclosure.userInteractionEnabled = NO; disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px (button.bounds.size.height - 20) / 2, 20, 20); [button addSubview:disclosure]; // configure header title text return headerView.contentView; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 35.0f; } -(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer; { NSLog(@"header tap"); NSInteger section = ((NSNumber *)sender.argument).integerValue; // do something here } в - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSString *CellIdentifier = @"PersonGroupHeader"; UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if(headerView == nil) { [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]]; } // https://stackoverflow.com/a/24044628/3075839 while(headerView.contentView.gestureRecognizers.count) { [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]]; } ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4]; button.frame = headerView.bounds; // set tap area to entire header view button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside]; // https://stackoverflow.com/a/20821178/3075839 UITableViewCell *disclosure = [[UITableViewCell alloc] init]; disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator; disclosure.userInteractionEnabled = NO; disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px (button.bounds.size.height - 20) / 2, 20, 20); [button addSubview:disclosure]; // configure header title text return headerView.contentView; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 35.0f; } -(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer; { NSLog(@"header tap"); NSInteger section = ((NSNumber *)sender.argument).integerValue; // do something here } 

ButtonWithArgument.h

 #import  @interface ButtonWithArgument : UIButton @property (nonatomic, strong) NSObject *argument; @end 

ButtonWithArgument.m

 #import "ButtonWithArgument.h" @implementation ButtonWithArgument @end 

У меня были проблемы в сценарии, когда Header никогда не использовался повторно, даже делая все необходимые шаги.

Так что в качестве примечания к сведению всем, кто хочет достичь ситуации с пустыми разделами (0 строк), следует предупредить, что:

dequeueReusableHeaderFooterViewWithIdentifier не будет повторно использовать заголовок, пока вы не вернете хотя бы одну строку

Надеюсь, поможет

Что относительно решения, в котором заголовок основан на массиве вида:

 class myViewController: UIViewController { var header: [UILabel] = myStringArray.map { (thisTitle: String) -> UILabel in let headerView = UILabel() headerView.text = thisTitle return(headerView) } 

Далее в делегате:

 extension myViewController: UITableViewDelegate { func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { return(header[section]) } } 

Вот ответ Виталия Гоженко в Свифт.
В итоге вы создадите UITableViewHeaderFooterView, который содержит UITableViewCell. Этот UITableViewCell будет «dequeuable», и вы можете его спроектировать в своем раскадровке.

  1. Создание classа UITableViewHeaderFooterView

     class CustomHeaderFooterView: UITableViewHeaderFooterView { var cell : UITableViewCell? { willSet { cell?.removeFromSuperview() } didSet { if let cell = cell { cell.frame = self.bounds cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth] self.contentView.backgroundColor = UIColor .clearColor() self.contentView .addSubview(cell) } } } 
  2. Подключите свой Tableview к этому classу в функции viewDidLoad:

     self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID") 
  3. Когда вы спросите, для заголовка раздела, деактивируйте CustomHeaderFooterView и вставьте в него ячейку

     func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView if view.cell == nil { let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell") view.cell = cell; } // Fill the cell with data here return view; } 
  1. Добавьте ячейку в StoryBoard и установите reuseidentified

    С.Б.

  2. Код

     class TP_TaskViewTableViewSectionHeader: UITableViewCell{ } 

    а также

    ссылка

  3. Использование:

     func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section)) return header } 
Давайте будем гением компьютера.