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

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

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

Ответ 1

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

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

(see screenhot)

-(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]; 
  1. Теперь вы можете использовать метку для установки нового названия:
    [label setText:@"New Title"];

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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

Решение состоит в том, чтобы удалить любые указатели gestureRecognizers из 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; }

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

Если вам нужна быстрая реализация, следуйте инструкциям принятого ответа, а затем в 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
}

Ответ 8

Когда вы возвращаете элемент содержимого CellView, у вас будет две проблемы:

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

Решение:

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

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

Зарегистрируйте класс в вашем 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;
}

Ответ 9

Я делал следующее, чтобы создавать заголовки/нижние колонтитулы лениво:

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

Ответ 10

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

Итак, после того, как вы собрали свой собственный заголовок сокета со всеми причудливыми вещами 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;
}  

Ответ 11

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

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

Button In Interface 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]];
    }

    //  /questions/39988/how-to-implement-custom-table-view-section-headers-and-footers-with-storyboard/289535#289535
    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];

    // /questions/25248/use-table-view-disclosure-indicator-style-for-uibutton-ios/184054#184054
    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 <UIKit/UIKit.h>

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

ButtonWithArgument.m

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

Ответ 12

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

  • Создайте класс 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 к этому классу в функции 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;
    }
    

Ответ 13

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

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

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

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

Ответ 14

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

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])
    }
}

Ответ 15

  • Добавьте ячейку в StoryBoard и установите reuseidentified

    sb

  • Код

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    и

    link

  • Применение:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
        return header
    }