webentwicklung-frage-antwort-db.com.de

So implementieren Sie die Kopf- und Fußzeilen der benutzerdefinierten Tabellensicht mit dem Storyboard

Ohne ein Storyboard zu verwenden, können Sie einfach eine UIView auf die Leinwand ziehen, es anordnen und dann in die Delegat-Methode tableView:viewForHeaderInSection oder tableView:viewForFooterInSection setzen.

Wie schaffen wir das mit einem StoryBoard, bei dem wir keine UIView auf die Leinwand ziehen können

170
Seamus

Ich weiß, dass diese Frage für iOS 5 war, aber für zukünftige Leser ist zu beachten, dass effektives iOS 6 jetzt dequeueReusableHeaderFooterViewWithIdentifier anstelle von dequeueReusableCellWithIdentifier verwenden kann.

Rufen Sie in viewDidLoad entweder registerNib:forHeaderFooterViewReuseIdentifier: oder registerClass:forHeaderFooterViewReuseIdentifier: auf. Rufen Sie dann in viewForHeaderInSectiontableView:dequeueReusableHeaderFooterViewWithIdentifier: auf. Sie verwenden für diese API keinen Zellprototyp (entweder eine NIB-basierte Ansicht oder eine programmgesteuert erstellte Ansicht). Dies ist jedoch die neue API für entkoppelte Kopf- und Fußzeilen.

85
Rob

Verwenden Sie einfach eine Prototypzelle als Kopf- und/oder Fußzeile des Abschnitts. 

  • fügen Sie eine zusätzliche Zelle hinzu und fügen Sie Ihre gewünschten Elemente hinzu. 
  • setze den Bezeichner auf etwas Bestimmtes (in meinem Fall SectionHeader)
  • implementieren Sie die tableView:viewForHeaderInSection:-Methode oder die tableView:viewForFooterInSection:-Methode
  • verwenden Sie [tableView dequeueReusableCellWithIdentifier:], um den Header abzurufen
  • implementieren Sie die tableView:heightForHeaderInSection:-Methode.

(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;
}  

Bearbeiten: So ändern Sie den Headertitel (kommentierte Frage):

  1. Fügen Sie der Kopfzelle ein Label hinzu
  2. setzen Sie das Tag des Labels auf eine bestimmte Nummer (z. B. 123).
  3. Rufen Sie das Label in Ihrer tableView:viewForHeaderInSection:-Methode auf, indem Sie Folgendes aufrufen:
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. Jetzt können Sie das Label verwenden, um einen neuen Titel festzulegen:
    [label setText:@"New Title"];
379
Tieme

In iOS 6.0 und höher hat sich die neue dequeueReusableHeaderFooterViewWithIdentifier-API geändert.

Ich habe einen guide (getestet auf iOS 9) geschrieben, der wie folgt zusammengefasst werden kann:

  1. Unterklasse UITableViewHeaderFooterView
  2. Erstellen Sie mit der Unterklassenansicht eine Spitze, und fügen Sie eine Containeransicht hinzu, die alle anderen Ansichten in der Kopf-/Fußzeile enthält
  3. Registrieren Sie die Nib in viewDidLoad
  4. Implementieren Sie viewForHeaderInSection und verwenden Sie dequeueReusableHeaderFooterViewWithIdentifier, um die Kopf-/Fußzeile zurückzuholen
52
samwize

Ich habe es in iOS7 mit einer Prototypzelle im Storyboard zum Laufen gebracht. Ich habe eine Schaltfläche in der Kopfzeilenansicht des benutzerdefinierten Abschnitts, die einen im Storyboard eingerichteten Abschnitt auslöst.

Beginnen Sie mit Tiemes Lösung

Wie pedro.m darauf hinweist, besteht das Problem darin, dass durch Tippen auf den Abschnittskopf die erste Zelle des Abschnitts ausgewählt wird. 

Wie Paul Von hervorhebt, wird dies dadurch behoben, dass die Inhaltsansicht der Zelle anstelle der gesamten Zelle zurückgegeben wird.

Wie Hons jedoch hervorhebt, führt ein langer Druck auf die Kopfzeile zu einem Absturz der App.

Die Lösung besteht darin, alle gestureRecognizers aus contentView zu entfernen.

-(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; }

Wenn Sie keine Gesten in den Kopfzeilenansichten Ihres Abschnitts verwenden, scheint dieser kleine Hack es zu schaffen.

22
damon

Wenn Sie Storyboards verwenden, können Sie in der Tabellenansicht eine Prototypzelle verwenden, um die Kopfansicht zu gestalten. Legen Sie eine eindeutige ID und viewForHeaderInSection fest, mit der Sie die Zelle mit dieser ID ausreißen und in eine UIView umwandeln können.

13
barksten

Wenn Sie eine schnelle Implementierung benötigen, befolgen Sie die Anweisungen in der akzeptierten Antwort und implementieren Sie in Ihrem UITableViewController die folgenden Methoden:

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
}
10
JZ.

Die Lösung, die ich mir vorgestellt habe, ist im Grunde dieselbe Lösung, die vor der Einführung von Storyboards verwendet wurde.

Erstellen Sie eine neue, leere Schnittstellenklassendatei. Ziehen Sie eine UIView auf die Leinwand, je nach Wunsch.

Laden Sie die Spitze manuell, und weisen Sie sie den entsprechenden Header-/Footer-Abschnitten in den Delegatenmethoden viewForHeaderInSection oder viewForFooterInSection zu.

Ich hatte die Hoffnung, dass Apple dieses Szenario mit Storyboards vereinfachte und nach einer besseren oder einfacheren Lösung suchte. Zum Beispiel können benutzerdefinierte Kopf- und Fußzeilen direkt hinzugefügt werden.

9
Seamus

Wenn Sie das contentView der Zelle zurückgeben, treten zwei Probleme auf:

  1. absturz im Zusammenhang mit Gesten
  2. sie verwenden contentView nicht erneut (bei jedem Aufruf von viewForHeaderInSection erstellen Sie eine neue Zelle).

Lösung:

Wrapper-Klasse für den Tabellenkopf\footer . Es handelt sich lediglich um einen Container, der von UITableViewHeaderFooterView geerbt wird und die Zelle enthält

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

Klasse in Ihrer UITableView registrieren (zum Beispiel in viewDidLoad)

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

In Ihrem 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;
}
5

Ich habe die folgenden Schritte ausgeführt, um die Kopf-/Fußzeilenansichten faul zu erstellen:

  • Fügen Sie dem Storyboard einen Freiformansicht-Controller für die Kopf-/Fußzeile des Abschnitts hinzu
  • Behandeln Sie alle Inhalte für den Header im View-Controller
  • Stellen Sie in der Tabellensichtsteuerung ein veränderbares Array von Ansichtscontrollern für die mit [NSNull null] aufgefüllten Abschnittskopf-/-fußzeilen bereit.
  • Wenn in viewForHeaderInSection/viewForFooterInSection der View Controller noch nicht vorhanden ist, erstellen Sie ihn mit Storyboards instantiateViewControllerWithIdentifier, speichern Sie ihn im Array und geben Sie die View Controller-Ansicht zurück
2
Vipera Berus

Ich war in einem Szenario in Schwierigkeiten, in dem Header nie wiederverwendet wurde, obwohl alle richtigen Schritte ausgeführt wurden.

Als Hinweis an alle, die die Situation erreichen wollen, leere Abschnitte (0 Zeilen) anzuzeigen, sei gewarnt, dass:

dequeueReusableHeaderFooterViewWithIdentifier verwendet den Header erst wieder, wenn Sie mindestens eine Zeile zurückgeben.

Ich hoffe es hilft

2

Um den Vorschlag von Damon weiter zu verfolgen, habe ich die Kopfzeile wie eine normale Zeile mit einem Offenlegungsindikator auswählbar gemacht.

Ich habe eine Button-Unterklasse von UIButton (Unterklassenname "ButtonWithArgument") zur Prototypzelle der Kopfzeile hinzugefügt und den Titeltext gelöscht (der fette "Title" -Text ist ein weiteres UILabel in der Prototypzelle).

Button In Interface Builder

setzen Sie dann den Button auf die gesamte Kopfansicht und fügen Sie mit Avario's Trick

- (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

ButtonWithArgument.m

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

Wie sieht es mit einer Lösung aus, bei der der Header auf einem View-Array basiert:

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

Weiter im Delegierten:

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

Verwenden Sie Tiemes Lösung als Basis, vergessen Sie jedoch den viewWithTag: und andere fischartige Ansätze. Versuchen Sie stattdessen, den Header neu zu laden (indem Sie diesen Abschnitt erneut laden).

Nachdem Sie Ihre benutzerdefinierte Zellenkopfansicht mit all den ausgefallenen AutoLayout-Elementen eingerichtet haben, entziehen Sie sie einfach und geben Sie die contentView nach der Einrichtung zurück.

-(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
Laszlo
  1. Fügen Sie eine Zelle in StoryBoard hinzu, und legen Sie reuseidentified fest. 

     sb

  2. Code 

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    und 

     link

  3. Benutzen:

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

Hier ist @Vitaliy Gozhenkos Antwort , in Swift.
Zusammenfassend erstellen Sie eine UITableViewHeaderFooterView, die eine UITableViewCell enthält. Diese UITableViewCell ist "entschlüsselbar" und Sie können sie in Ihrem Storyboard entwerfen.

  1. Erstellen Sie eine UITableViewHeaderFooterView-Klasse

    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. Stecken Sie Ihre Tableview mit dieser Klasse in Ihre viewDidLoad-Funktion:

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. Wenn Sie nach einem Abschnittsheader fragen, entfernen Sie eine CustomHeaderFooterView und fügen eine Zelle ein

    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;
    }
    
0
CedricSoubrie

Ähnlich wie bei laszlo answer, aber Sie können dieselbe Prototypzelle sowohl für die Tabellenzellen als auch für die Abschnittsüberschriftszelle wiederverwenden. Fügen Sie die ersten beiden Funktionen Ihrer UIViewController-Unterklasse hinzu

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
Richard Legault