Erweitern / Reduzieren Sie den Abschnitt in UITableView unter iOS


Antworten:


109

Sie müssen Ihre eigene benutzerdefinierte Kopfzeile erstellen und diese als erste Zeile jedes Abschnitts einfügen. Das Unterklassen der UITableViewoder der Header, die bereits vorhanden sind, wird schmerzhaft sein. Aufgrund der Art und Weise, wie sie jetzt funktionieren, bin ich mir nicht sicher, ob Sie leicht Aktionen aus ihnen herausholen können. Sie könnten eine Zelle zu sehen wie ein Header, und Setup die Einrichtung tableView:didSelectRowAtIndexPathden Abschnitt manuell erweitern oder reduzieren es in ist.

Ich würde ein Array von Booleschen Werten speichern, die dem "verbrauchten" Wert jedes Ihrer Abschnitte entsprechen. Dann können Sie den tableView:didSelectRowAtIndexPathWert in jeder Ihrer benutzerdefinierten Kopfzeilen umschalten und dann diesen bestimmten Abschnitt neu laden.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == 0) {
        ///it's the first row of any section so it would be your custom section header

        ///put in your code to toggle your boolean value here
        mybooleans[indexPath.section] = !mybooleans[indexPath.section];

        ///reload this section
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
    }
}

Setzen Sie dann numberOfRowsInSectionden überprüfen mybooleansWert und 1 zurück , wenn der Abschnitt nicht erweitert wird, oder 1+ die Anzahl der Elemente in dem Abschnitt , wenn es erweitert wird.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    if (mybooleans[section]) {
        ///we want the number of people plus the header cell
        return [self numberOfPeopleInGroup:section] + 1;
    } else {
        ///we just want the header cell
        return 1;
    }
}

Außerdem müssen Sie aktualisieren cellForRowAtIndexPath, um eine benutzerdefinierte Kopfzelle für die erste Zeile in einem beliebigen Abschnitt zurückzugeben.


2
Wenn Sie die Beejive-App verwendet haben, wissen Sie, dass der zusammenklappbare Abschnittskopf tatsächlich am oberen Rand der Tabelle "schwebt", selbst wenn Sie durch einen Teil des Abschnitts gescrollt haben, genau wie bei den normalen Apple-Abschnittsüberschriften. Das ist nicht möglich, wenn Sie einfach eine Zelle am Anfang des Abschnitts
hinzufügen

Schöne elegante Lösung! user102008 hat einen Punkt im schwebenden Header, aber in dem Szenario, in dem die "Abschnitte" tatsächlich gescrollt werden sollen, ist dies ein großartiger Ansatz.
Nick Cipollina

@mjdth plz gib mir einen Beispielcode bcz ich brauche eine bestimmte Zelle ein- / ausblenden .. danke im Voraus
Bajaj

11
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionist der bessere Weg, um Ihren "eigenen benutzerdefinierten Header" bereitzustellen, da genau das dafür vorgesehen ist.
William Denniss

Dies funktionierte anfangs für mich, als ich nur einen Abschnitt hatte, aber sobald ich mehr hatte, erhalte ich den Fehler "ungültiges Update ungültige Anzahl von Zeilen". Ich weiß, dass diese Lösung älter ist, aber funktioniert dies nur für einen Abschnitt? Wenn wir mehr als einen Abschnitt haben, müssen wir den Code hinzufügen, der die Zeilen tatsächlich hinzufügt / löscht?
Skinsfan00atg

103

Einige Beispielcodes zum Animieren einer Erweiterungs- / Reduzierungsaktion mithilfe einer Abschnittsüberschrift für die Tabellenansicht werden von Apple hier bereitgestellt: Animationen und Gesten für die Tabellenansicht

Der Schlüssel zu diesem Ansatz besteht darin, - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectioneine benutzerdefinierte UIView zu implementieren und zurückzugeben, die eine Schaltfläche enthält (normalerweise dieselbe Größe wie die Header-Ansicht selbst). Indem Sie UIView in Unterklassen unterteilen und diese für die Header-Ansicht verwenden (wie in diesem Beispiel), können Sie problemlos zusätzliche Daten wie die Abschnittsnummer speichern.



Ich erinnere mich nicht, aber warum funktioniert der Beispielcode unter iOS 4 nicht?
Samwize

1
ich weiß es nicht. es heißt nur "iOS 4.0.2 oder höher"
user102008

1
Der aktuell aktualisierte Code unter dem Link enthält Fehler und kann leicht zum Absturz gebracht werden
Ankit Srivastava

1
Wie Ankit Srivastava bereits erwähnt hat, ist es einfach, dieses Codebeispiel zu brechen: Kopieren Sie einfach alle Elementwörterbücher und fügen Sie sie in die PlaysAndQuotations.plist ein (ich habe dies mit 30 Einträgen im Stammwörterbuch getestet). - Starten Sie nun die App und öffnen Sie das erste Spiel. Danach scrollen Sie nach unten, bis Sie einen Pfeil sehen, der nach unten zeigt (ich glaube, das kommt von dequeueReusableHeaderFooterViewWithIdentifier) - klicken Sie auf diesen Pfeil und scrollen Sie zurück zum ersten Spiel und versuchen Sie es zu schließen -> NSInternalInconsistencyException (iOS 8.4 / iPhone 5s)
Raimund Wege

22

Ich habe eine schöne Lösung erhalten, die von Apples Table View-Animationen und -Gesten inspiriert ist . Ich habe unnötige Teile aus Apples Beispiel gelöscht und in schnell übersetzt.

Ich weiß, dass die Antwort ziemlich lang ist, aber der gesamte Code ist notwendig. Glücklicherweise können Sie den größten Teil des Codes einfach kopieren und einfügen und müssen nur ein wenig an Schritt 1 und 3 ändern

1. erstellen SectionHeaderView.swiftundSectionHeaderView.xib

import UIKit

protocol SectionHeaderViewDelegate {
    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionOpened: Int)
    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionClosed: Int)
}

class SectionHeaderView: UITableViewHeaderFooterView {

    var section: Int?
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var disclosureButton: UIButton!
    @IBAction func toggleOpen() {
        self.toggleOpenWithUserAction(true)
    }
    var delegate: SectionHeaderViewDelegate?

    func toggleOpenWithUserAction(userAction: Bool) {
        self.disclosureButton.selected = !self.disclosureButton.selected

        if userAction {
            if self.disclosureButton.selected {
                self.delegate?.sectionHeaderView(self, sectionClosed: self.section!)
            } else {
                self.delegate?.sectionHeaderView(self, sectionOpened: self.section!)
            }
        }
    }

    override func awakeFromNib() {
        var tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "toggleOpen")
        self.addGestureRecognizer(tapGesture)
        // change the button image here, you can also set image via IB.
        self.disclosureButton.setImage(UIImage(named: "arrow_up"), forState: UIControlState.Selected)
        self.disclosureButton.setImage(UIImage(named: "arrow_down"), forState: UIControlState.Normal)
    }

}

Die SectionHeaderView.xib(die Ansicht mit grauem Hintergrund) sollte in einer Tabellenansicht ungefähr so ​​aussehen (Sie können sie natürlich an Ihre Bedürfnisse anpassen): Geben Sie hier die Bildbeschreibung ein

Hinweis:

a) Die toggleOpenAktion sollte mit verknüpft seindisclosureButton

b) die disclosureButtonund toggleOpenAktion sind nicht notwendig. Sie können diese beiden Dinge löschen, wenn Sie die Schaltfläche nicht benötigen.

2. erstellen SectionInfo.swift

import UIKit

class SectionInfo: NSObject {
    var open: Bool = true
    var itemsInSection: NSMutableArray = []
    var sectionTitle: String?

    init(itemsInSection: NSMutableArray, sectionTitle: String) {
        self.itemsInSection = itemsInSection
        self.sectionTitle = sectionTitle
    }
}

3.in Ihrer Tabellenansicht

import UIKit

class TableViewController: UITableViewController, SectionHeaderViewDelegate  {

    let SectionHeaderViewIdentifier = "SectionHeaderViewIdentifier"

    var sectionInfoArray: NSMutableArray = []

    override func viewDidLoad() {
        super.viewDidLoad()

        let sectionHeaderNib: UINib = UINib(nibName: "SectionHeaderView", bundle: nil)
        self.tableView.registerNib(sectionHeaderNib, forHeaderFooterViewReuseIdentifier: SectionHeaderViewIdentifier)

        // you can change section height based on your needs
        self.tableView.sectionHeaderHeight = 30

        // You should set up your SectionInfo here
        var firstSection: SectionInfo = SectionInfo(itemsInSection: ["1"], sectionTitle: "firstSection")
        var secondSection: SectionInfo = SectionInfo(itemsInSection: ["2"], sectionTitle: "secondSection"))
        sectionInfoArray.addObjectsFromArray([firstSection, secondSection])
    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return sectionInfoArray.count
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if self.sectionInfoArray.count > 0 {
            var sectionInfo: SectionInfo = sectionInfoArray[section] as! SectionInfo
            if sectionInfo.open {
                return sectionInfo.open ? sectionInfo.itemsInSection.count : 0
            }
        }
        return 0
    }

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let sectionHeaderView: SectionHeaderView! = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier(SectionHeaderViewIdentifier) as! SectionHeaderView
        var sectionInfo: SectionInfo = sectionInfoArray[section] as! SectionInfo

        sectionHeaderView.titleLabel.text = sectionInfo.sectionTitle
        sectionHeaderView.section = section
        sectionHeaderView.delegate = self
        let backGroundView = UIView()
        // you can customize the background color of the header here
        backGroundView.backgroundColor = UIColor(red:0.89, green:0.89, blue:0.89, alpha:1)
        sectionHeaderView.backgroundView = backGroundView
        return sectionHeaderView
    }

    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionOpened: Int) {
        var sectionInfo: SectionInfo = sectionInfoArray[sectionOpened] as! SectionInfo
        var countOfRowsToInsert = sectionInfo.itemsInSection.count
        sectionInfo.open = true

        var indexPathToInsert: NSMutableArray = NSMutableArray()
        for i in 0..<countOfRowsToInsert {
            indexPathToInsert.addObject(NSIndexPath(forRow: i, inSection: sectionOpened))
        }
        self.tableView.insertRowsAtIndexPaths(indexPathToInsert as [AnyObject], withRowAnimation: .Top)
    }

    func sectionHeaderView(sectionHeaderView: SectionHeaderView, sectionClosed: Int) {
        var sectionInfo: SectionInfo = sectionInfoArray[sectionClosed] as! SectionInfo
        var countOfRowsToDelete = sectionInfo.itemsInSection.count
        sectionInfo.open = false
        if countOfRowsToDelete > 0 {
            var indexPathToDelete: NSMutableArray = NSMutableArray()
            for i in 0..<countOfRowsToDelete {
                indexPathToDelete.addObject(NSIndexPath(forRow: i, inSection: sectionClosed))
            }
            self.tableView.deleteRowsAtIndexPaths(indexPathToDelete as [AnyObject], withRowAnimation: .Top)
        }
    }
}

1
Vielen Dank für die Mühe! Mit einem kleinen Beispielprojekt auf Github wäre es eine noch bessere Antwort
Max MacLeod

Vielen Dank für die detaillierte Antwort. Ein Beispielprojekt wäre besser.
Thiha Aung

20

Um den zusammenklappbaren Tabellenabschnitt in iOS zu implementieren, besteht die Magie darin, die Anzahl der Zeilen für jeden Abschnitt zu steuern oder die Zeilenhöhe für jeden Abschnitt zu verwalten.

Außerdem müssen wir den Abschnittskopf anpassen, damit wir das Tap-Ereignis aus dem Kopfbereich abhören können (unabhängig davon, ob es sich um eine Schaltfläche oder den gesamten Kopf handelt).

Wie gehe ich mit dem Header um? Es ist sehr einfach, wir erweitern die UITableViewCell-Klasse und erstellen eine benutzerdefinierte Header-Zelle wie folgt:

import UIKit

class CollapsibleTableViewHeader: UITableViewCell {

    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var toggleButton: UIButton!

}

Verwenden Sie dann viewForHeaderInSection, um die Header-Zelle anzuschließen:

override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  let header = tableView.dequeueReusableCellWithIdentifier("header") as! CollapsibleTableViewHeader

  header.titleLabel.text = sections[section].name
  header.toggleButton.tag = section
  header.toggleButton.addTarget(self, action: #selector(CollapsibleTableViewController.toggleCollapse), forControlEvents: .TouchUpInside)

  header.toggleButton.rotate(sections[section].collapsed! ? 0.0 : CGFloat(M_PI_2))

  return header.contentView
}

Denken Sie daran, dass wir die contentView zurückgeben müssen, da diese Funktion die Rückgabe einer UIView erwartet.

Lassen Sie uns nun den zusammenklappbaren Teil behandeln. Hier ist die Umschaltfunktion, mit der die zusammenklappbare Stütze jedes Abschnitts umgeschaltet wird:

func toggleCollapse(sender: UIButton) {
  let section = sender.tag
  let collapsed = sections[section].collapsed

  // Toggle collapse
  sections[section].collapsed = !collapsed

  // Reload section
  tableView.reloadSections(NSIndexSet(index: section), withRowAnimation: .Automatic)
}

hängt davon ab, wie Sie die Abschnittsdaten verwalten. In diesem Fall habe ich die Abschnittsdaten ungefähr so:

struct Section {
  var name: String!
  var items: [String]!
  var collapsed: Bool!

  init(name: String, items: [String]) {
    self.name = name
    self.items = items
    self.collapsed = false
  }
}

var sections = [Section]()

sections = [
  Section(name: "Mac", items: ["MacBook", "MacBook Air", "MacBook Pro", "iMac", "Mac Pro", "Mac mini", "Accessories", "OS X El Capitan"]),
  Section(name: "iPad", items: ["iPad Pro", "iPad Air 2", "iPad mini 4", "Accessories"]),
  Section(name: "iPhone", items: ["iPhone 6s", "iPhone 6", "iPhone SE", "Accessories"])
]

Schließlich müssen wir basierend auf der zusammenklappbaren Stütze jedes Abschnitts die Anzahl der Zeilen dieses Abschnitts steuern:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return (sections[section].collapsed!) ? 0 : sections[section].items.count
}

Ich habe eine voll funktionsfähige Demo auf meinem Github: https://github.com/jeantimex/ios-swift-collapsible-table-section

Demo

Wenn Sie die reduzierbaren Abschnitte in einer gruppierten Tabelle implementieren möchten, habe ich hier eine weitere Demo mit Quellcode: https://github.com/jeantimex/ios-swift-collapsible-table-section-in-grouped-section

Hoffentlich hilft das.


Hallo, ich habe meinen benutzerdefinierten Header-Abschnitt für eine XIB-Datei erstellt und die Schreibfeder bei meinem Table View Controller registriert. Wenn ich einen Abschnitt lösche und erneut versuche, ihn zu erweitern / zu reduzieren, wird ein schwerwiegender Fehler angezeigt, der besagt, dass der Index außerhalb des Bereichs liegt. Gibt es eine Möglichkeit, dies zu beheben? Vielen Dank!
iamhx

sehr schöne und saubere Lösung!
Joel

10

Ich habe eine bessere Lösung, dass Sie einen UIButton in die Abschnittsüberschrift einfügen und die Größe dieser Schaltfläche gleich der Abschnittsgröße festlegen, diese jedoch durch eine klare Hintergrundfarbe ausblenden. Danach können Sie leicht überprüfen, auf welchen Abschnitt zum Erweitern oder Reduzieren geklickt wird


3
Meiner Meinung nach ist diese Lösung besser als die akzeptierte Antwort, da Sie semantisch Ihren Header als Header behalten und keine gefälschte Zeile verwenden, um einen Header zu simulieren. Die Methode tableView:numberOfRowsInSection:bleibt unberührt und Sie können sie weiterhin für das verwenden, was sie wirklich bedeutet. Gleiches gilt für tableView:cellForRowAtIndexPath:.
Cœur

Sie tippen also auf die Schaltfläche in der Abschnittsüberschrift, aber wie bestimmen Sie, welcher Abschnitt neu geladen werden soll?
Memmons

@Answerbot Hallo, es ist extrem einfach, das Tag für die Schaltfläche mit demselben Wert wie der Abschnittsindex festzulegen.
Sohn Nguyen

Ich hatte Angst, dass du das sagen würdest. Der Missbrauch der Tag-Eigenschaft für Dinge wie tableView-Indizes ist eine schlechte Wahl für das Design.
Memmons

Ich habe noch nie "großartige" Lösungen für das Problem gesehen, weshalb ich gehofft habe, dass Sie einen anderen Ansatz haben. Die beste Antwort, die ich gesehen habe, ist das Apple-Referenzprojekt. Apple unterteilt a UITableViewHeaderFooterViewund fügt eine sectionEigenschaft hinzu und definiert a, SectionHeaderViewDelegatedie den Rückruf zum Öffnen / Schließen des Abschnitts bereitstellt. ( developer.apple.com/library/ios/samplecode/TableViewUpdates/… )
memmons

7

Am Ende habe ich nur eine Header-Ansicht erstellt, die eine Schaltfläche enthielt (ich habe Son Nguyens Lösung oben nachträglich gesehen , aber hier ist mein Code. Es sieht nach viel aus, ist aber ziemlich einfach):

Deklarieren Sie ein paar Bools für Ihre Abschnitte

bool customerIsCollapsed = NO;
bool siteIsCollapsed = NO;

...Code

Jetzt in Ihrer Tabellenansicht Methoden delegieren ...

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _tblSearchResults.frame.size.width, 35)];

    UILabel *lblSection = [UILabel new];
    [lblSection setFrame:CGRectMake(0, 0, 300, 30)];
    [lblSection setFont:[UIFont fontWithName:@"Helvetica-Bold" size:17]];
    [lblSection setBackgroundColor:[UIColor clearColor]];
    lblSection.alpha = 0.5;
    if(section == 0)
    {
        if(!customerIsCollapsed)
            [lblSection setText:@"Customers    --touch to show--"];
        else
            [lblSection setText:@"Customers    --touch to hide--"];
    }
    else
    {
        if(!siteIsCollapsed)
            [lblSection setText:@"Sites    --touch to show--"];
        else
            [lblSection setText:@"Sites    --touch to hide--"];    }

    UIButton *btnCollapse = [UIButton buttonWithType:UIButtonTypeCustom];
    [btnCollapse setFrame:CGRectMake(0, 0, _tblSearchResults.frame.size.width, 35)];
    [btnCollapse setBackgroundColor:[UIColor clearColor]];
    [btnCollapse addTarget:self action:@selector(touchedSection:) forControlEvents:UIControlEventTouchUpInside];
    btnCollapse.tag = section;


    [headerView addSubview:lblSection];
    [headerView addSubview:btnCollapse];

    return headerView;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    if(section == 0)
    {
        if(customerIsCollapsed)
            return 0;
        else
            return _customerArray.count;
    }
    else if (section == 1)
    {
        if(siteIsCollapsed)
            return 0;
        else
        return _siteArray.count;

    }
    return 0;
}

und schließlich die Funktion, die aufgerufen wird, wenn Sie eine der Abschnittsüberschriften-Schaltflächen berühren:

- (IBAction)touchedSection:(id)sender
{
    UIButton *btnSection = (UIButton *)sender;

    if(btnSection.tag == 0)
    {
        NSLog(@"Touched Customers header");
        if(!customerIsCollapsed)
            customerIsCollapsed = YES;
        else
            customerIsCollapsed = NO;

    }
    else if(btnSection.tag == 1)
    {
        NSLog(@"Touched Site header");
        if(!siteIsCollapsed)
            siteIsCollapsed = YES;
        else
            siteIsCollapsed = NO;

    }
    [_tblSearchResults reloadData];
}

Ich habe mich nur gefragt, ob der Abschnitt kollabiert und animiert oder ohne Animation erweitert wird. Ohne Animation wird es sehr schlecht aussehen. Wie können wir Animationen hinzufügen?
Sam

@ Sam Wenn Sie etwas wie [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];in der Collapse / Uncollapse-Methode verwenden, sollte es gut animiert werden.
William Denniss

5

Dies ist der beste Weg, um erweiterbare Tabellenansichtszellen zu erstellen

.h Datei

  NSMutableIndexSet *expandedSections;

.m Datei

if (!expandedSections)
    {
        expandedSections = [[NSMutableIndexSet alloc] init];
    }
   UITableView *masterTable = [[UITableView alloc] initWithFrame:CGRectMake(0,100,1024,648) style:UITableViewStyleGrouped];
    masterTable.delegate = self;
    masterTable.dataSource = self;
    [self.view addSubview:masterTable];

Delegierungsmethoden für Tabellenansichten

- (BOOL)tableView:(UITableView *)tableView canCollapseSection:(NSInteger)section
{
    // if (section>0) return YES;

    return YES;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return 4;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if ([self tableView:tableView canCollapseSection:section])
    {
        if ([expandedSections containsIndex:section])
        {
            return 5; // return rows when expanded
        }

        return 1; // only top row showing
    }

    // Return the number of rows in the section.
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
    }

    // Configure the cell...

    if ([self tableView:tableView canCollapseSection:indexPath.section])
    {
        if (!indexPath.row)
        {
            // first row
            cell.textLabel.text = @"Expandable"; // only top row showing

            if ([expandedSections containsIndex:indexPath.section])
            {

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableContract"]];
                cell.accessoryView = imView;
            }
            else
            {

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableExpand"]];
                cell.accessoryView = imView;
            }
        }
        else
        {
            // all other rows
            if (indexPath.section == 0) {
                cell.textLabel.text = @"section one";
            }else if (indexPath.section == 1) {
                cell.textLabel.text = @"section 2";
            }else if (indexPath.section == 2) {
                cell.textLabel.text = @"3";
            }else {
                cell.textLabel.text = @"some other sections";
            }

            cell.accessoryView = nil;
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        }
    }
    else
    {
        cell.accessoryView = nil;
        cell.textLabel.text = @"Normal Cell";

    }

    return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([self tableView:tableView canCollapseSection:indexPath.section])
    {
        if (!indexPath.row)
        {
            // only first row toggles exapand/collapse
            [tableView deselectRowAtIndexPath:indexPath animated:YES];

            NSInteger section = indexPath.section;
            BOOL currentlyExpanded = [expandedSections containsIndex:section];
            NSInteger rows;


            NSMutableArray *tmpArray = [NSMutableArray array];

            if (currentlyExpanded)
            {
                rows = [self tableView:tableView numberOfRowsInSection:section];
                [expandedSections removeIndex:section];

            }
            else
            {
                [expandedSections addIndex:section];
                rows = [self tableView:tableView numberOfRowsInSection:section];
            }


            for (int i=1; i<rows; i++)
            {
                NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i 
                                                               inSection:section];
                [tmpArray addObject:tmpIndexPath];
            }

            UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

            if (currentlyExpanded)
            {
                [tableView deleteRowsAtIndexPaths:tmpArray 
                                 withRowAnimation:UITableViewRowAnimationTop];

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableExpand"]];
                cell.accessoryView = imView;
            }
            else
            {
                [tableView insertRowsAtIndexPaths:tmpArray 
                                 withRowAnimation:UITableViewRowAnimationTop];

                UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"UITableContract"]];
                cell.accessoryView = imView;
            }
        }
    }

    NSLog(@"section :%d,row:%d",indexPath.section,indexPath.row);

}

8
Sie sollten Fragen wahrscheinlich als exakte Duplikate kennzeichnen, anstatt nur die gleiche Antwort auf alle zu spammen.
CasperOne

Wenn ein Abschnitt bereits erweitert ist und ein anderer Abschnitt angeklickt wird, gibt es einen Fehler
Shivam

Hallo Herr, Ausgewählter Index Höhe Wie ändere ich? heightForRowAtIndexPath Wie arbeite ich mit Ihrem Code?
Gami Nilesh

Hallo Sir, wie navigiere ich zu einem anderen Ansichts-Controller, wenn eine erweiterte Zeile ausgewählt wurde?
Arbaz Shaikh

1

Basierend auf der "Button in Header" -Lösung ist hier eine saubere und minimalistische Implementierung:

  • Sie verfolgen reduzierte (oder erweiterte) Abschnitte in einer Eigenschaft
  • Sie kennzeichnen die Schaltfläche mit dem Abschnittsindex
  • Sie legen einen ausgewählten Status auf dieser Schaltfläche fest, um die Pfeilrichtung zu ändern (wie △ und ▽).

Hier ist der Code:

@interface MyTableViewController ()
@property (nonatomic, strong) NSMutableIndexSet *collapsedSections;
@end

...

@implementation MyTableViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (!self)
        return;
    self.collapsedSections = [NSMutableIndexSet indexSet];
    return self;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // if section is collapsed
    if ([self.collapsedSections containsIndex:section])
        return 0;

    // if section is expanded
#warning incomplete implementation
    return [super tableView:tableView numberOfRowsInSection:section];
}

- (IBAction)toggleSectionHeader:(UIView *)sender
{
    UITableView *tableView = self.tableView;
    NSInteger section = sender.tag;

    MyTableViewHeaderFooterView *headerView = (MyTableViewHeaderFooterView *)[self tableView:tableView viewForHeaderInSection:section];

    if ([self.collapsedSections containsIndex:section])
    {
        // section is collapsed
        headerView.button.selected = YES;
        [self.collapsedSections removeIndex:section];
    }
    else
    {
        // section is expanded
        headerView.button.selected = NO;
        [self.collapsedSections addIndex:section];
    }

    [tableView beginUpdates];
    [tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

@end

1

Ich habe einen anderen relativ einfachen Weg gefunden, um dieses Problem zu lösen. Mit dieser Methode müssen wir unsere Zelle nicht ändern, was fast immer mit dem Datenarray-Index zusammenhängt, was möglicherweise zu Problemen in unserem Ansichts-Controller führen kann.

Zunächst fügen wir unserer Controller-Klasse die folgenden Eigenschaften hinzu:

@property (strong, nonatomic) NSMutableArray* collapsedSections;
@property (strong, nonatomic) NSMutableArray* sectionViews;

collapsedSectionsspeichert reduzierte Abschnittsnummern. sectionViewsspeichert unsere benutzerdefinierte Schnittansicht.

Synthetisiere es:

@synthesize collapsedSections;
@synthesize sectionViews;

Initialisieren Sie es:

- (void) viewDidLoad
{
    [super viewDidLoad];

    self.collapsedSections = [NSMutableArray array];
    self.sectionViews      = [NSMutableArray array];
}

Danach müssen wir unsere UITableView verbinden, damit innerhalb unserer View Controller-Klasse darauf zugegriffen werden kann:

@property (strong, nonatomic) IBOutlet UITableView *tblMain;

Verbinden Sie es von XIB, um den Controller ctrl + dragwie gewohnt anzuzeigen .

Anschließend erstellen wir eine Ansicht als benutzerdefinierten Abschnittskopf für unsere Tabellenansicht, indem wir diesen UITableView-Delegaten implementieren:

- (UIView*) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    // Create View
    CGRect frame = CGRectZero;

    frame.origin = CGPointZero;

    frame.size.height = 30.f;
    frame.size.width  = tableView.bounds.size.width;

    UIView* view = [[UIView alloc] initWithFrame:frame];

    [view setBackgroundColor:[UIColor blueColor]];

    // Add label for title
    NSArray* titles = @[@"Title 1", @"Title 2", @"Title 3"];

    NSString* selectedTitle = [titles objectAtIndex:section];

    CGRect labelFrame = frame;

    labelFrame.size.height = 30.f;
    labelFrame.size.width -= 20.f;
    labelFrame.origin.x += 10.f;

    UILabel* titleLabel = [[UILabel alloc] initWithFrame:labelFrame];

    [titleLabel setText:selectedTitle];
    [titleLabel setTextColor:[UIColor whiteColor]];

    [view addSubview:titleLabel];

    // Add touch gesture
    [self attachTapGestureToView:view];

    // Save created view to our class property array
    [self saveSectionView:view inSection:section];

    return view;
}

Als Nächstes implementieren wir eine Methode zum Speichern unseres zuvor erstellten benutzerdefinierten Abschnittskopfs in der Klasseneigenschaft:

- (void) saveSectionView:(UIView*) view inSection:(NSInteger) section
{
    NSInteger sectionCount = [self numberOfSectionsInTableView:[self tblMain]];

    if(section < sectionCount)
    {
        if([[self sectionViews] indexOfObject:view] == NSNotFound)
        {
            [[self sectionViews] addObject:view];
        }
    }
}

Fügen Sie UIGestureRecognizerDelegateunserer View Controller .h-Datei hinzu:

@interface MyViewController : UIViewController<UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate>

Dann erstellen wir eine Methode attachTapGestureToView:

- (void) attachTapGestureToView:(UIView*) view
{
    UITapGestureRecognizer* tapAction = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)];

    [tapAction setDelegate:self];

    [view addGestureRecognizer:tapAction];
}

Die obige Methode fügt der zuvor erstellten Schnittansicht eine Tippgestenerkennung hinzu. Als nächstes sollten wir den onTap:Selektor implementieren

- (void) onTap:(UITapGestureRecognizer*) gestureRecognizer
{
    // Take view who attach current recognizer
    UIView* sectionView = [gestureRecognizer view]; 

    // [self sectionViews] is Array containing our custom section views
    NSInteger section = [self sectionNumberOfView:sectionView];

    // [self tblMain] is our connected IBOutlet table view
    NSInteger sectionCount = [self numberOfSectionsInTableView:[self tblMain]];

    // If section more than section count minus one set at last
    section = section > (sectionCount - 1) ? 2 : section;

    [self toggleCollapseSection:section];
}

Die obige Methode wird aufgerufen, wenn der Benutzer auf einen unserer Abschnitte in der Tabellenansicht tippt. Diese Methode sucht die korrekte Abschnittsnummer basierend auf unserem zuvor erstellten sectionViewsArray.

Außerdem implementieren wir eine Methode, um herauszufinden, zu welchem ​​Abschnitt der Header-Ansicht gehört.

- (NSInteger) sectionNumberOfView:(UIView*) view
{
    UILabel* label = [[view subviews] objectAtIndex:0];

    NSInteger sectionNum = 0;

    for(UIView* sectionView in [self sectionViews])
    {
        UILabel* sectionLabel = [[sectionView subviews] objectAtIndex:0];

        //NSLog(@"Section: %d -> %@ vs %@", sectionNum, [label text], [sectionLabel text]);

        if([[label text] isEqualToString:[sectionLabel text]])
        {
            return sectionNum;
        }

        sectionNum++;
    }

    return NSNotFound;
}

Als nächstes müssen wir die Methode implementieren toggleCollapseSection:

- (void) toggleCollapseSection:(NSInteger) section
{
    if([self isCollapsedSection:section])
    {
        [self removeCollapsedSection:section];
    }
    else
    {
        [self addCollapsedSection:section];
    }

    [[self tblMain] reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade];
}

Diese Methode fügt die Abschnittsnummer in unser zuvor erstelltes collapsedSectionsArray ein / entfernt sie . Wenn eine Abschnittsnummer in dieses Array eingefügt wird, bedeutet dies, dass der Abschnitt reduziert und erweitert werden sollte, wenn dies nicht der Fall ist.

Als nächstes werden wir umsetzen removeCollapsedSection:, addCollapsedSection:sectionundisCollapsedSection:section

- (BOOL)isCollapsedSection:(NSInteger) section
{
    for(NSNumber* existing in [self collapsedSections])
    {
        NSInteger current = [existing integerValue];

        if(current == section)
        {
            return YES;
        }
    }

    return NO;
}

- (void)removeCollapsedSection:(NSInteger) section
{
    [[self collapsedSections] removeObjectIdenticalTo:[NSNumber numberWithInteger:section]];
}

- (void)addCollapsedSection:(NSInteger) section
{
    [[self collapsedSections] addObject:[NSNumber numberWithInteger:section]];
}

Diese drei Methoden sind nur hilfreich, um uns den Zugriff auf das collapsedSectionsArray zu erleichtern .

Implementieren Sie abschließend diesen Delegaten für Tabellenansichten, damit unsere benutzerdefinierten Abschnittsansichten gut aussehen.

- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 30.f; // Same as each custom section view height
}

Ich hoffe es hilft.


1

Ich habe ein NSDictionary als Datenquelle verwendet, das sieht nach viel Code aus, ist aber sehr einfach und funktioniert sehr gut! wie sieht das hier aus

Ich habe eine Aufzählung für die Abschnitte erstellt

typedef NS_ENUM(NSUInteger, TableViewSection) {

    TableViewSection0 = 0,
    TableViewSection1,
    TableViewSection2,
    TableViewSectionCount
};

Abschnitte Eigenschaft:

@property (nonatomic, strong) NSMutableDictionary * sectionsDisctionary;

Eine Methode, die meine Abschnitte zurückgibt:

-(NSArray <NSNumber *> * )sections{

    return @[@(TableViewSection0), @(TableViewSection1), @(TableViewSection2)];
}

Und dann richte meine Daten ein:

-(void)loadAndSetupData{

    self.sectionsDisctionary = [NSMutableDictionary dictionary];

    NSArray * sections = [self sections];

    for (NSNumber * section in sections) {

    NSArray * sectionObjects = [self objectsForSection:section.integerValue];

    [self.sectionsDisctionary setObject:[NSMutableDictionary dictionaryWithDictionary:@{@"visible" : @YES, @"objects" : sectionObjects}] forKey:section];
    }
}

-(NSArray *)objectsForSection:(NSInteger)section{

    NSArray * objects;

    switch (section) {

        case TableViewSection0:

            objects = @[] // objects for section 0;
            break;

        case TableViewSection1:

            objects = @[] // objects for section 1;
            break;

        case TableViewSection2:

            objects = @[] // objects for section 2;
            break;

        default:
            break;
    }

    return objects;
}

Die nächsten Methoden helfen Ihnen zu wissen, wann ein Abschnitt geöffnet wird und wie Sie auf die Datenquelle der Tabellenansicht reagieren:

Antworten Sie auf den Abschnitt zur Datenquelle:

/**
 *  Asks the delegate for a view object to display in the header of the specified section of the table view.
 *
 *  @param tableView The table-view object asking for the view object.
 *  @param section   An index number identifying a section of tableView .
 *
 *  @return A view object to be displayed in the header of section .
 */
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

    NSString * headerName = [self titleForSection:section];

    YourCustomSectionHeaderClass * header = (YourCustomSectionHeaderClass *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:YourCustomSectionHeaderClassIdentifier];

    [header setTag:section];
    [header addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]];
    header.title = headerName;
    header.collapsed = [self sectionIsOpened:section];


    return header;
}

/**
 * Asks the data source to return the number of sections in the table view
 *
 * @param An object representing the table view requesting this information.
 * @return The number of sections in tableView.
 */
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    // Return the number of sections.

    return self.sectionsDisctionary.count;
}

/**
 * Tells the data source to return the number of rows in a given section of a table view
 *
 * @param tableView: The table-view object requesting this information.
 * @param section: An index number identifying a section in tableView.
 * @return The number of rows in section.
 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    BOOL sectionOpened = [self sectionIsOpened:section];
    return sectionOpened ? [[self objectsForSection:section] count] : 0;
}

Werkzeuge:

/**
 Return the section at the given index

 @param index the index

 @return The section in the given index
 */
-(NSMutableDictionary *)sectionAtIndex:(NSInteger)index{

    NSString * asectionKey = [self.sectionsDisctionary.allKeys objectAtIndex:index];

    return [self.sectionsDisctionary objectForKey:asectionKey];
}

/**
 Check if a section is currently opened

 @param section the section to check

 @return YES if is opened
 */
-(BOOL)sectionIsOpened:(NSInteger)section{

    NSDictionary * asection = [self sectionAtIndex:section];
    BOOL sectionOpened = [[asection objectForKey:@"visible"] boolValue];

    return sectionOpened;
}


/**
 Handle the section tap

 @param tap the UITapGestureRecognizer
 */
- (void)handleTapGesture:(UITapGestureRecognizer*)tap{

    NSInteger index = tap.view.tag;

    [self toggleSection:index];
}

Schalten Sie die Sichtbarkeit des Abschnitts ein

/**
 Switch the state of the section at the given section number

 @param section the section number
 */
-(void)toggleSection:(NSInteger)section{

    if (index >= 0){

        NSMutableDictionary * asection = [self sectionAtIndex:section];

        [asection setObject:@(![self sectionIsOpened:section]) forKey:@"visible"];

        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationFade];
    }
}

0
// -------------------------------------------------------------------------------
//  tableView:viewForHeaderInSection:
// -------------------------------------------------------------------------------
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    UIView *mView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
    [mView setBackgroundColor:[UIColor greenColor]];

    UIImageView *logoView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 5, 20, 20)];
    [logoView setImage:[UIImage imageNamed:@"carat.png"]];
    [mView addSubview:logoView];

    UIButton *bt = [UIButton buttonWithType:UIButtonTypeCustom];
    [bt setFrame:CGRectMake(0, 0, 150, 30)];
    [bt setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [bt setTag:section];
    [bt.titleLabel setFont:[UIFont systemFontOfSize:20]];
    [bt.titleLabel setTextAlignment:NSTextAlignmentCenter];
    [bt.titleLabel setTextColor:[UIColor blackColor]];
    [bt setTitle: @"More Info" forState: UIControlStateNormal];
    [bt addTarget:self action:@selector(addCell:) forControlEvents:UIControlEventTouchUpInside];
    [mView addSubview:bt];
    return mView;

}

#pragma mark - Suppose you want to hide/show section 2... then
#pragma mark  add or remove the section on toggle the section header for more info

- (void)addCell:(UIButton *)bt{

    // If section of more information
    if(bt.tag == 2) {

        // Initially more info is close, if more info is open
        if(ifOpen) {
            DLog(@"close More info");

            // Set height of section
            heightOfSection = 0.0f;

            // Reset the parameter that more info is closed now
            ifOpen = NO;
        }else {
            // Set height of section
            heightOfSection = 45.0f;
            // Reset the parameter that more info is closed now
            DLog(@"open more info again");
            ifOpen = YES;
        }
        //[self.tableView reloadData];
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:2] withRowAnimation:UITableViewRowAnimationFade];
    }

}// end addCell
#pragma mark -
#pragma mark  What will be the height of the section, Make it dynamic

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

    if (indexPath.section == 2) {
        return heightOfSection;
    }else {
        return 45.0f;
    }

// vKj


0
This action will happen in your didSelectRowAtIndexPath, when you will try to hide or show number of cell in a  section

first of all declare a global variable numberOfSectionInMoreInfo in .h file and in your viewDidLoad set suppose to numberOfSectionInMoreInfo = 4.

Now use following logic: 


 // More info link
        if(row == 3) {

            /*Logic: We are trying to hide/show the number of row into more information section */

            NSString *log= [NSString stringWithFormat:@"Number of section in more %i",numberOfSectionInMoreInfo];

            [objSpineCustomProtocol showAlertMessage:log];

            // Check if the number of rows are open or close in view
            if(numberOfSectionInMoreInfo > 4) {

                // close the more info toggle
                numberOfSectionInMoreInfo = 4;

            }else {

                // Open more info toggle
                numberOfSectionInMoreInfo = 9;

            }

            //reload this section
            [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];

// vKj


Warum zwei Antworten? Es scheint nicht, dass Sie zwei verschiedene Lösungen für das Problem bereitgestellt haben.
Cristik

0

Erweitern Sie dies Antwort in Ziel C habe ich Folgendes für diejenigen geschrieben, die in Swift geschrieben haben

Die Idee ist, Abschnitte innerhalb der Tabelle zu verwenden und die Anzahl der Zeilen im Abschnitt auf 1 (reduziert) und 3 (erweitert) zu setzen, wenn auf die erste Zeile in diesem Abschnitt getippt wird

Die Tabelle entscheidet anhand eines Arrays von Booleschen Werten, wie viele Zeilen gezeichnet werden sollen

Sie müssen zwei Zeilen im Storyboard erstellen und ihnen die Wiederverwendungskennungen "CollapsingRow" und "GroupHeading" geben.

import UIKit

class CollapsingTVC:UITableViewController{

    var sectionVisibilityArray:[Bool]!// Array index corresponds to section in table

    override func viewDidLoad(){
        super.viewDidLoad()
        sectionVisibilityArray = [false,false,false]
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    override func numberOfSections(in tableView: UITableView) -> Int{
        return sectionVisibilityArray.count
    }
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat{
        return 0
    }

    // numberOfRowsInSection - Get count of entries
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        var rowsToShow:Int = 0
        if(sectionVisibilityArray[section]){
            rowsToShow = 3 // Or however many rows should be displayed in that section
        }else{
            rowsToShow = 1
        }
        return rowsToShow
    }// numberOfRowsInSection


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
        if(indexPath.row == 0){
            if(sectionVisibilityArray[indexPath.section]){
                sectionVisibilityArray[indexPath.section] = false
            }else{
                sectionVisibilityArray[indexPath.section] = true
            }
            self.tableView.reloadSections([indexPath.section], with: .automatic)
        }
    }

    // cellForRowAtIndexPath - Get table cell corresponding to this IndexPath
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell:UITableViewCell

        if(indexPath.row == 0){
             cell = tableView.dequeueReusableCell(withIdentifier: "GroupHeading", for: indexPath as IndexPath)
        }else{
            cell = tableView.dequeueReusableCell(withIdentifier: "CollapsingRow", for: indexPath as IndexPath)
        }

        return cell

    }// cellForRowAtIndexPath

}

0

Einige Beispielcodes zum Animieren einer Erweiterungs- / Reduzierungsaktion mithilfe einer Abschnittsüberschrift in der Tabellenansicht werden von Apple unter Animationen und Gesten in der Tabellenansicht bereitgestellt .

Der Schlüssel zu diesem Ansatz liegt in der Implementierung

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

und geben Sie eine benutzerdefinierte UIView zurück, die eine Schaltfläche enthält (normalerweise dieselbe Größe wie die Header-Ansicht selbst). Indem Sie UIView in Unterklassen unterteilen und diese für die Header-Ansicht verwenden (wie in diesem Beispiel), können Sie problemlos zusätzliche Daten wie die Abschnittsnummer speichern.


0

Ich habe dasselbe mit mehreren Abschnitten gemacht.

class SCTierBenefitsViewController: UIViewController {
    @IBOutlet private weak var tblTierBenefits: UITableView!
    private var selectedIndexPath: IndexPath?
    private var isSelected:Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()

        tblTierBenefits.register(UINib(nibName:"TierBenefitsTableViewCell", bundle: nil), forCellReuseIdentifier:"TierBenefitsTableViewCell")
        tblTierBenefits.register(UINib(nibName:"TierBenefitsDetailsCell", bundle: nil), forCellReuseIdentifier:"TierBenefitsDetailsCell")

        tblTierBenefits.rowHeight = UITableViewAutomaticDimension;
        tblTierBenefits.estimatedRowHeight = 44.0;
        tblTierBenefits.tableFooterView = UIView()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

extension SCTierBenefitsViewController : UITableViewDataSource{

    func numberOfSections(in tableView: UITableView) -> Int {
        return 7
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return (isSelected && section == selectedIndexPath?.section) ? 2 : 1 
    }

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

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return nil
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.row {
        case 0:
            let cell:TierBenefitsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "TierBenefitsTableViewCell")! as! TierBenefitsTableViewCell
            cell.selectionStyle = .none
            cell.contentView.setNeedsLayout()
            cell.contentView.layoutIfNeeded()
            return cell

        case 1:
            let cell:TierBenefitsDetailsCell = tableView.dequeueReusableCell(withIdentifier: "TierBenefitsDetailsCell")! as! TierBenefitsDetailsCell
            cell.selectionStyle = .none
            return cell

        default:
            break
        }

        return UITableViewCell()
    }
}

extension SCTierBenefitsViewController : UITableViewDelegate{

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row == 0 {

            if let _selectedIndexPath = selectedIndexPath ,selectedIndexPath?.section == indexPath.section {
                tblTierBenefits.beginUpdates()
                expandCollapse(indexPath: _selectedIndexPath, isExpand: false)
                selectedIndexPath = nil
            }
            else{
                tblTierBenefits.beginUpdates()
                if selectedIndexPath != nil {
                    tblTierBenefits.reloadSections([(selectedIndexPath?.section)!], with: .none)
                }
                expandCollapse(indexPath: indexPath, isExpand: true)
            }
        }
    }

    private func  expandCollapse(indexPath: IndexPath?,isExpand: Bool){
        isSelected = isExpand
        selectedIndexPath = indexPath
        tblTierBenefits.reloadSections([(indexPath?.section)!], with: .none)
        tblTierBenefits.endUpdates()
    }

}

0

Der Vollständigkeit halber füge ich diese Lösung hinzu und zeige, wie man mit Abschnittsüberschriften arbeitet.

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet var tableView: UITableView!
    var headerButtons: [UIButton]!
    var sections = [true, true, true]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self

        let section0Button = UIButton(type: .detailDisclosure)
        section0Button.setTitle("Section 0", for: .normal)
        section0Button.addTarget(self, action: #selector(section0Tapped), for: .touchUpInside)

        let section1Button = UIButton(type: .detailDisclosure)
        section1Button.setTitle("Section 1", for: .normal)
        section1Button.addTarget(self, action: #selector(section1Tapped), for: .touchUpInside)

        let section2Button = UIButton(type: .detailDisclosure)
        section2Button.setTitle("Section 2", for: .normal)
        section2Button.addTarget(self, action: #selector(section2Tapped), for: .touchUpInside)

        headerButtons = [UIButton]()
        headerButtons.append(section0Button)
        headerButtons.append(section1Button)
        headerButtons.append(section2Button)
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sections[section] ? 3 : 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellReuseId = "cellReuseId"
        let cell = UITableViewCell(style: .default, reuseIdentifier: cellReuseId)
        cell.textLabel?.text = "\(indexPath.section): \(indexPath.row)"
        return cell
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return headerButtons[section]
    }

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

    @objc func section0Tapped() {
        sections[0] = !sections[0]
        tableView.reloadSections([0], with: .fade)
    }

    @objc func section1Tapped() {
        sections[1] = !sections[1]
        tableView.reloadSections([1], with: .fade)
    }

    @objc func section2Tapped() {
        sections[2] = !sections[2]
        tableView.reloadSections([2], with: .fade)
    }

}

Link zum Kern: https://gist.github.com/pawelkijowskizimperium/fe1e8511a7932a0d40486a2669316d2c


0

Verwenden Sie zur Unterstützung der @ jean.timex-Lösung den folgenden Code, wenn Sie jederzeit einen Abschnitt öffnen möchten. Erstellen Sie eine Variable wie: var expandSection = -1;

func toggleSection(_ header: CollapsibleTableViewHeader, section: Int) {
    let collapsed = !sections[section].collapsed
    // Toggle collapse
    sections[section].collapsed = collapsed
    header.setCollapsed(collapsed)
    tableView.reloadSections(NSIndexSet(index: section) as IndexSet, with: .automatic)
    if (expandedSection >= 0 && expandedSection != section){
        sections[expandedSection].collapsed = true
        tableView.reloadSections(NSIndexSet(index: expandedSection) as IndexSet, with: .automatic)
    }
    expandedSection = section;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.