Как реализовать пользовательскую таблицу представления таблицы Заголовки и нижние колонтитулы с раскадровки
Без использования раскадровки мы могли бы просто перетащить 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; }
Изменить: как изменить заголовок заголовка (комментарий):
- Добавить метку в ячейку заголовка
- установите метку метки на определенное число (например, 123)
- В вашем приложении
tableView:viewForHeaderInSection:
получите ярлык, вызвав:
UILabel *label = (UILabel *)[headerView viewWithTag:123];
- Теперь вы можете использовать метку, чтобы установить новый заголовок:
[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), которое можно резюмировать следующим образом:
- Подclass
UITableViewHeaderFooterView
- Создайте Nib с видом подclassа и добавьте 1 вид контейнера, который содержит все другие представления в верхнем и нижнем колонтитуле
- Зарегистрируйте
viewDidLoad
вviewDidLoad
- Внедрите
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, у вас будет две проблемы:
- авария, связанная с жестов
- вы не повторно используете 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 в ячейке прототипа)
затем установите кнопку на весь вид заголовка и добавьте индикатор раскрытия информации с помощью трюка 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», и вы можете его спроектировать в своем раскадровке.
-
Создание 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) } } }
-
Подключите свой Tableview к этому classу в функции viewDidLoad:
self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
-
Когда вы спросите, для заголовка раздела, деактивируйте 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; }
-
Добавьте ячейку в
StoryBoard
и установитеreuseidentified
-
Код
class TP_TaskViewTableViewSectionHeader: UITableViewCell{ }
а также
-
Использование:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section)) return header }