Як реалізувати власні заголовки та колонтитули спеціальних таблиць із таблицею розкадрів


176

Не використовуючи дошку розкадрувань, ми могли просто перетягнути UIViewна полотно, викласти його та встановити в методах tableView:viewForHeaderInSectionабо tableView:viewForFooterInSectionделегувати.

Як це досягти за допомогою StoryBoard, де ми не можемо перетягнути UIView на полотно

Відповіді:


91

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

Отже viewDidLoad, зателефонуйте registerNib:forHeaderFooterViewReuseIdentifier:або registerClass:forHeaderFooterViewReuseIdentifier:. Тоді в viewForHeaderInSection, дзвоніть tableView:dequeueReusableHeaderFooterViewWithIdentifier:. Ви не використовуєте прототип комірки з цим API (це або представлення на основі NIB, або перегляд, створений програмним шляхом), але це новий API для відкладених заголовків і колонтитулів.


13
зітхайте Прикро, що ці два чисті відповіді щодо використання NIB замість прото-комірки наразі
набрали

5
Різниця полягає в тому, що за допомогою хака від Tieme ви можете зробити свій дизайн в одній і тій же розкадровці, а не використовувати окремий Xib
CedricSoubrie

Чи можете ви якимось чином уникнути використання окремого файлу NIB і використовувати замість нього розкадровку?
Foriger

@Foriger - Не на даний момент. Це дивний пропуск у видах таблиць розповідей, але він є. Використовуйте цей невдалий хак з прототипами комірок, якщо ви хочете, але особисто я просто змирився з роздратуванням NIB для перегляду заголовків / колонтитулів.
Роб

2
Просто кажучи, що це "старе", недостатньо сильний, ІМХО. Прийнята відповідь - химерний спосіб подолати той факт, що ви не можете мати прототипові комірки для перегляду заголовків і колонтитулів. Але я думаю, що це просто неправильно, тому що він припускає, що скасування заголовків і колонтитулів працює так само, як і відмивання комірок (що, присутність більш нового API для заголовків / колонтитулів, може бути не так). Прийнята відповідь - це творче вирішення, яке було зрозумілим ще в iOS 5, але в iOS 6 та пізніших версіях краще використовувати API, явно розроблений для повторного використання заголовка / колонтитулу.
Роб

384

Просто використовуйте комірку-прототип як заголовок розділу та / або колонтитул.

  • додайте додаткову клітинку і помістіть у неї потрібні елементи.
  • встановити ідентифікатор на щось конкретне (у моєму випадку 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"];

7
Пізно на вечірку, але щоб відключити натискання на перегляд заголовка розділу, просто поверніть cell.contentView у viewForHeaderInSection спосіб (не потрібно додавати будь-які власні UIView).
paulvs

3
@PaulVon Я намагався зробити це в своєму останньому проекті, але якщо ви це зробите, просто спробуйте натиснути клавішу на один із ваших заголовків, і вона вийде з ладу
Hons

13
В iOS 6.0 dequeueReusableHeaderFooterViewWithIdentifierвводиться, і зараз він надає перевагу цій відповіді. Але при правильному використанні зараз вживається більше кроків. У мене є путівник @ samwize.com/2015/11/06/… .
samwize

3
Не створюйте розділHeaderViews за допомогою UITableViewCells, це створить несподівану поведінку. Використовуйте натомість UIView.
AppreciateIt

4
Будь ласка, ніколи не використовуйте UITableViewCellяк перегляд заголовка. Вам буде дуже важко налагоджувати візуальні глюки - заголовок іноді зникає через те, як скасовуються комірки, і ви будете шукати годинами, чому це, поки ви UITableViewCellне зрозумієте, що не належить до UITableViewзаголовка.
raven_raven

53

В iOS 6.0 і новіших версіях все змінилося з новим dequeueReusableHeaderFooterViewWithIdentifierAPI.

Я написав посібник (тестований на iOS 9), який можна узагальнити як такий:

  1. Підклас UITableViewHeaderFooterView
  2. Створіть Nib за допомогою подання підкласу та додайте 1 перегляд контейнера, який містить усі інші представлення у заголовку / колонтитулі
  3. Зареєструйте гніздо в viewDidLoad
  4. Реалізуйте viewForHeaderInSectionта використовуйте dequeueReusableHeaderFooterViewWithIdentifierдля повернення колонтитула / колонтитула

2
Дякую за цю відповідь, яка НЕ ​​є злом, а правильний спосіб робити речі. Також дякую, що познайомив мене з UITableViewHeaderFooterVIew. До речі, путівник просто чудовий! :)
Roboris

Ласкаво просимо. Впровадження заголовка / колонтитулу для перегляду таблиці або колекції Apple не дуже добре документує. Лише вчора я застряг у заголовку getter, який показували під час проектування через storyboad .
samwize

1
Це обов'язково має бути найкраще відповідь!
Бен Вільямс

Чи можете ви пояснити, як це зробити за допомогою розкадровки замість xib? Роблячи це, я успішно скасовую, але мої UILabels є недійсними.
bunkerdive

22

Я отримав це, працюючи в iOS7, використовуючи прототип комірки в розгортці. У моєму користувальницькому перегляді заголовка розділу є кнопка, яка запускає пошкодження, встановлене в таблиці розмов.

Почніть з рішення Тіме

Як вказує pedro.m, проблема з цим полягає в тому, що при натисканні на заголовок розділу вибирається перша комірка в розділі.

Як зазначає Пол Фон, це виправлено шляхом повернення contentView комірки замість усієї комірки.

Однак, як зазначає Хонс, тривале натискання на зазначений заголовок розділу призведе до збою програми.

Рішення полягає в тому, щоб видалити будь-які жестові розпізнавачі з 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; }

Якщо ви не використовуєте жестів у переглядах заголовка розділу, цей маленький хакер, здається, це зробить.


2
Ти маєш рацію. Я щойно перевірив це та його роботу, тому я оновив свою посаду ( hons82.blogspot.it/2014/05/uitableviewheader-done-right.html ), що містить усі можливі рішення, які я знайшов під час мого дослідження. Дуже багато для цього виправлення
Hons

Гарна відповідь ... Спасибі Людина
Jogendra.Com

13

Якщо ви використовуєте мовленнєві дошки, ви можете використовувати прототип комірки в таблиці, щоб розташувати подання заголовка. Встановіть унікальний ідентифікатор та переглядForHeaderInSection, ви можете видалити клітинку з цим ідентифікатором та передати її на UIView.


12

Якщо вам потрібна швидка реалізація цього, виконайте вказівки щодо прийнятої відповіді, а потім у вас UITableViewController реалізуйте такі методи:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return tableView.dequeueReusableCell(withIdentifier: "CustomHeader")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

2
Чомусь це не спрацювало для мене, доки я також не змінив висотуForHeaderInSection.
Ентальпі

Це досить старий код. ви повинні опублікувати ще одну відповідь!
JZ.

Я застряг, бо не знав, що мені доведеться перекривати висотуForHeaderInSection. Дякуємо @Entalpi
guijob

1
@JZ. У прикладі Swift 3 ви декетуєте за допомогою таких self.tableView.dequeueReusableHeaderFooterView та Swift 2: self.tableView.dequeueReusableCellWithIdentifier чи є різниця?
Врутін Ратод

1
Це врятувало мою недопалку! додавання висоти headerCell робить його видимим.
CRey

9

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

Створіть новий, порожній файл класу інтерфейсу. Перетягніть UIView на полотно, розклавши за бажанням.

Завантажте ручку вручну, призначте відповідний розділ колонтитулів / колонтитулів у режимі viewForHeaderInSection або viewForFooterInSection.

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


Добре яблуко зробило, якщо ви використовуєте осередок раскадровки як метод заголовка :) stackoverflow.com/questions/9219234/#11396643
Tieme

2
За винятком того, що працює лише для прототипу комірок, а не статичних комірок.
Жан-Деніс Муйс

1
Одне дуже просте рішення - використовувати Pixate ; ви можете зробити досить багато налаштувань, не отримуючи посилання на заголовок. Все, що вам потрібно реалізувати - tableView:titleForHeaderInSectionце однолінійний.
Джон Старр Девар

5
Це справжнє рішення ... на жаль, це не на вершині, тому мені знадобилося деякий час, щоб знайти це. Я підсумував проблеми, які виникли у мене hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons

Простіше, ніж це, просто перетягніть.
Рікардо

5

Коли ви повернетеся до CellView contentView, у вас виникнуть дві проблеми:

  1. крах, пов’язаний з жестами
  2. ви не повторно використовуєте contentView (кожен раз viewForHeaderInSection, коли ви телефонуєте, створюєте нову клітинку)

Рішення:

Клас обгортки для заголовка таблиці \ нижнього колонтитулу. Це просто контейнер, успадкований від 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;
}

2

Раніше я робив такі дії, щоб створювати подані заголовки / колонтитули ліниво:

  • Додайте контролер подання вільної форми для заголовка / нижнього колонтитулу розділу до розкладної
  • Обробляйте всі речі для заголовка в контролері перегляду
  • У контролері подання таблиці забезпечують змінний масив контролерів перегляду для заголовків / колонтитулів секцій, переповнених [NSNull null]
  • У переглядіForHeaderInSection / viewForFooterInSection, якщо контролер перегляду ще не існує, створіть його за допомогою розгортки instantiateViewControllerWithIdentifier, запам'ятайте його в масиві і поверніть подання контролерів подання

2

Я опинився в біді за сценарієм, коли Header ніколи не використовувався, навіть роблячи всі належні кроки.

Тож як підказка для всіх, хто хоче домогтися ситуації показу порожніх розділів (0 рядків), попередити:

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

Сподіваюся, це допомагає


1

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

Я додав підклас Кнопки з UIButton (назва підкласу "ButtonWithArgument") до комірки прототипу заголовка та видалив текст заголовка (жирний текст "Заголовок" - це ще один UILabel у комірці прототипу)

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

потім встановіть кнопку на весь перегляд заголовка та додайте показник розкриття інформації з хитрістю Аваріо

- (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 <UIKit/UIKit.h>

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

Кнопка з аргументом.m

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

1

Ви повинні використовувати рішення 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;
}  

1

Як щодо рішення, де заголовок заснований на масиві представлення:

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

1
  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
    }
    

2
Це не правильний спосіб додати заголовок
Manish

2
то що таке?
Pramod Шукла

1

Схожий на відповідь Laszlo, але ви можете повторно використовувати ту саму прототипну комірку як для комірок таблиці, так і для комірки заголовка розділу. Додайте перші дві функції нижче до свого підкласу UIViewController

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell") as! DataCell
    cell.data1Label.text = "DATA KEY"
    cell.data2Label.text = "DATA VALUE"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

// Example of regular data cell dataDelegate to round out the example
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as! PlayerCell

    cell.data1Label.text = "\(dataList[indexPath.row].key)"
    cell.data2Label.text = "\(dataList[indexPath.row].value)"
    return cell
}

0

Ось відповідь @Vitaliy Gozhenko , у Swift.
Підводячи підсумки, ви створите UITableViewHeaderFooterView, який містить UITableViewCell. Цей UITableViewCell буде "підлягає зменшенню", і ви можете спроектувати його у своїй розкадровці.

  1. Створіть клас 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. Підключіть свій перегляд таблиці до цього класу у функції 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;
    }
    
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.