Rozwiń / zwiń sekcję w UITableView w iOS

Odpowiedzi:

109

Musisz stworzyć własny, niestandardowy wiersz nagłówka i umieścić go jako pierwszy wiersz każdej sekcji. Podklasy UITableViewlub nagłówki, które już istnieją, będą uciążliwe. Biorąc pod uwagę sposób, w jaki teraz działają, nie jestem pewien, czy można łatwo uzyskać z nich działania. Możesz ustawić komórkę tak, aby WYGLĄDAŁA jak nagłówek, i ustawić tableView:didSelectRowAtIndexPathręczne rozwijanie lub zwijanie sekcji, w której się znajduje.

Przechowałbym tablicę wartości logicznych odpowiadających „wydatkom” każdej sekcji. Wtedy można mieć tableView:didSelectRowAtIndexPathna każdym z niestandardowych wierszy nagłówka przełącznik tej wartości, a następnie załaduj tę sekcję konkretnego.

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

Następnie ustaw tak, numberOfRowsInSectionaby sprawdzić mybooleanswartość i zwrócić 1, jeśli sekcja nie jest rozwinięta, lub 1+ liczbę elementów w sekcji, jeśli jest rozwinięta.

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

Będziesz także musiał zaktualizować, cellForRowAtIndexPathaby zwrócić niestandardową komórkę nagłówka dla pierwszego wiersza w dowolnej sekcji.

mjdth
źródło
2
jeśli używałeś aplikacji Beejive, wiesz, że ich zwijany nagłówek sekcji faktycznie „unosi się” na górze tabeli, nawet jeśli przewinąłeś część jej sekcji, tak jak zwykłe nagłówki sekcji Apple. nie jest to możliwe, jeśli po prostu dodasz komórkę na początku sekcji
user102008
Ładne, eleganckie rozwiązanie! user102008 ma punkt w pływającym nagłówku, ale w scenariuszu, w którym chcesz przewijać „sekcje”, jest to świetne podejście.
Nick Cipollina
@mjdth plz daj mi dowolny przykładowy kod bcz Potrzebuję konkretnej komórki ukryj / odkryj ... z góry dziękuję
Bajaj
11
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionjest lepszym sposobem na zapewnienie własnego „własnego nagłówka”, ponieważ właśnie do tego został zaprojektowany.
William Denniss
początkowo działało to dla mnie, gdy miałem tylko jedną sekcję, ale jak tylko mam więcej, pojawia się błąd „Nieprawidłowa aktualizacja, nieprawidłowa liczba wierszy”. Wiem, że to rozwiązanie jest starsze, ale czy będzie działać tylko dla jednej sekcji? jeśli mamy więcej niż jedną sekcję, czy będziemy musieli dodać kod, który faktycznie dodaje / usuwa wiersze?
skinsfan00atg
103

Przykładowy kod do animowania akcji rozwijania / zwijania za pomocą nagłówka sekcji widoku tabeli jest udostępniany przez firmę Apple tutaj: Animacje i gesty widoku tabeli

Kluczem do tego podejścia jest zaimplementowanie - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectioni zwrócenie niestandardowego UIView, który zawiera przycisk (zwykle tego samego rozmiaru co sam widok nagłówka). Podklasując UIView i używając tego dla widoku nagłówka (tak jak w tym przykładzie), można łatwo przechowywać dodatkowe dane, takie jak numer sekcji.

samwize
źródło
nie pamiętam, ale dlaczego przykładowy kod nie działa w wersjach wcześniejszych niż iOS 4?
samwize
1
Nie wiem. jest na nim po prostu
napis
1
Bieżący zaktualizowany kod pod linkiem zawiera błędy i można go łatwo rozbić
Ankit Srivastava
1
Tak jak wspomniany wcześniej Ankit Srivastava, łatwo jest złamać ten przykład kodu: po prostu skopiuj i wklej wszystkie słowniki pozycji na PlaysAndQuotations.plist (przetestowałem to z 30 wpisami w słowniku głównym) - Teraz uruchom aplikację i otwórz pierwszą grę - potem przewijasz w dół, aż zobaczysz strzałkę skierowaną w dół (myślę, że to pochodzi dequeueReusableHeaderFooterViewWithIdentifier) - kliknij tę strzałkę i przewiń z powrotem do pierwszej gry i spróbuj ją zamknąć -> NSInternalInconsistencyException (iOS 8.4 / iPhone 5s)
Raimund Wege
22

Mam fajne rozwiązanie inspirowane animacjami i gestami widoku tabeli Apple . Usunąłem niepotrzebne części z próbki Apple i przetłumaczyłem to na szybkie.

Wiem, że odpowiedź jest dość długa, ale cały kod jest potrzebny. Na szczęście możesz po prostu skopiować i pominąć większość kodu i po prostu dokonać niewielkiej modyfikacji w kroku 1 i 3

1. utwórz SectionHeaderView.swiftiSectionHeaderView.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)
    }

}

SectionHeaderView.xib(widok z szarym tle) powinien wyglądać tak w tableview (można ją dostosować w zależności od potrzeb, oczywiście): wprowadź opis obrazu tutaj

Uwaga:

a) toggleOpendziałanie powinno być powiązanedisclosureButton

b) działania disclosureButtoni toggleOpennie są konieczne. Możesz usunąć te dwie rzeczy, jeśli nie potrzebujesz przycisku.

2. tworzyć 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. w widoku tabeli

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)
        }
    }
}
Brian
źródło
1
dzięki za wysiłek! Przy małym przykładowym projekcie na githubie byłaby to jeszcze lepsza odpowiedź
Max MacLeod
Dziękujemy za udzielenie szczegółowej odpowiedzi. Przykładowy projekt byłby lepszy.
Thiha Aung
20

Aby zaimplementować zwijaną sekcję tabeli w iOS, magia polega na tym, jak kontrolować liczbę wierszy dla każdej sekcji lub możemy zarządzać wysokością wierszy dla każdej sekcji.

Musimy również dostosować nagłówek sekcji, abyśmy mogli słuchać zdarzenia tap z obszaru nagłówka (niezależnie od tego, czy jest to przycisk, czy cały nagłówek).

Jak sobie radzić z nagłówkiem? To bardzo proste, rozszerzamy klasę UITableViewCell i tworzymy niestandardową komórkę nagłówka w następujący sposób:

import UIKit

class CollapsibleTableViewHeader: UITableViewCell {

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

}

następnie użyj viewForHeaderInSection, aby podłączyć komórkę nagłówka:

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
}

pamiętaj, że musimy zwrócić contentView, ponieważ ta funkcja oczekuje zwrócenia UIView.

Teraz zajmijmy się składaną częścią, oto funkcja przełączania, która przełącza zwijaną rekwizyt każdej sekcji:

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

zależy od tego, jak zarządzasz danymi sekcji, w tym przypadku mam dane sekcji mniej więcej tak:

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

w końcu to, co musimy zrobić, opiera się na zwijanym rekwizycie każdej sekcji, kontroluje liczbę wierszy tej sekcji:

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

Mam w pełni działające demo na moim Githubie: https://github.com/jeantimex/ios-swift-collapsible-table-section

próbny

Jeśli chcesz zaimplementować zwijane sekcje w tabeli w stylu zgrupowanym, mam inne demo z kodem źródłowym tutaj: https://github.com/jeantimex/ios-swift-collapsible-table-section-in-grouped-section

Mam nadzieję, że to pomoże.

jeantimex
źródło
Cześć, wykonałem niestandardową sekcję nagłówka w pliku xib i zarejestrowałem końcówkę w moim kontrolerze widoku tabeli. Kiedy usuwam sekcję i ponownie próbuję rozwinąć / zwinąć, pojawia się błąd krytyczny z informacją, że indeks jest poza zakresem. Czy jest jakiś sposób, aby to naprawić? Dzięki!
iamhx
bardzo ładne i czyste rozwiązanie!
Joel
10

Mam lepsze rozwiązanie, że powinieneś dodać przycisk UIButton do nagłówka sekcji i ustawić rozmiar tego przycisku na równy rozmiarowi sekcji, ale ukryj go za pomocą przezroczystego koloru tła, po czym możesz łatwo sprawdzić, która sekcja jest kliknięta, aby rozwinąć lub zwinąć

Son Nguyen
źródło
3
Moim zdaniem to rozwiązanie jest lepsze niż zaakceptowana odpowiedź, ponieważ semantycznie zachowujesz nagłówek jako nagłówek i nie używasz fałszywego wiersza do symulacji nagłówka. Metoda tableView:numberOfRowsInSection:pozostanie nietknięta i nadal będziesz mógł jej używać do tego, co naprawdę oznacza. To samo dotyczy tableView:cellForRowAtIndexPath:.
Coœur,
Tak więc dotykasz przycisku w nagłówku sekcji, ale jak zamierzasz określić, która sekcja powinna zostać ponownie załadowana?
memmons
@Answerbot Cześć, Jest to niezwykle łatwe dzięki ustawieniu tagu dla przycisku przy użyciu tej samej wartości z indeksem sekcji.
Son Nguyen
Bałam się, że to powiesz. Nadużywanie właściwości tagu w przypadku takich rzeczy, jak indeksy tableView jest złym wyborem projektowym.
memmons
Nigdy nie widziałem „świetnych” rozwiązań tego problemu, dlatego miałem nadzieję, że masz inne podejście. Najlepsza odpowiedź, jaką widziałem, to projekt referencyjny Apple. Apple tworzy podklasy a UITableViewHeaderFooterViewi dodaje sectionwłaściwość oraz definiuje a, SectionHeaderViewDelegatektóry zapewnia wywołanie zwrotne w celu otwarcia / zamknięcia sekcji. ( developer.apple.com/library/ios/samplecode/TableViewUpdates/ ... )
memmons
7

Skończyło się na utworzeniu widoku headerView, który zawierał przycisk ( po fakcie widziałem powyżej rozwiązanie Son Nguyen , ale oto mój kod ... wygląda na dużo, ale jest całkiem prosty):

zadeklaruj kilka wartości dla swoich sekcji

bool customerIsCollapsed = NO;
bool siteIsCollapsed = NO;

...kod

teraz w metodach delegata tableview ...

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

i wreszcie funkcja, która jest wywoływana po dotknięciu jednego z przycisków nagłówka sekcji:

- (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];
}
RyanG
źródło
Właśnie się zastanawiałem, czy sekcja zwinie się i rozwinie animowana czy bez animacji. Bez animacji będzie wyglądać bardzo źle. jak możemy dodać do tego animację?
Sam,
@Sam, jeśli używasz czegoś takiego jak [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];w metodzie collapse / uncollapse, powinno to ładnie się animować.
William Denniss
5

To najlepszy sposób, jaki znalazłem na tworzenie komórek z rozszerzalnym widokiem tabeli

plik .h

  NSMutableIndexSet *expandedSections;

plik .m

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];

Metody delegatów widoku tabeli

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

}
vamsi575kg
źródło
8
Prawdopodobnie powinieneś oznaczać pytania jako dokładne duplikaty, zamiast po prostu spamować tę samą odpowiedź na wszystkich.
casperOne
jeśli sekcja jest już rozwinięta i kliknięto inną sekcję, pojawia się błąd
shivam,
cześć panie, wysokość wybranego indeksu, jak zmienić? heightForRowAtIndexPath jak pracować z kodem?
Gami Nilesh
Cześć panie, jak przejść do innego kontrolera widoku w wybranym rozwiniętym wierszu?
Arbaz Shaikh
1

Tak więc, w oparciu o rozwiązanie `` przycisk w nagłówku '', oto czysta i minimalistyczna implementacja:

  • śledzisz zwinięte (lub rozwinięte) sekcje w usłudze
  • oznaczysz przycisk indeksem sekcji
  • ustawiłeś wybrany stan na tym przycisku, aby zmienić kierunek strzałki (jak △ i ▽)

Oto kod:

@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
Cœur
źródło
1

Znalazłem inny stosunkowo prosty sposób rozwiązania tego problemu. Korzystając z tej metody, nie będziemy musieli zmieniać naszej komórki, która prawie zawsze jest związana z indeksem tablicy danych, potencjalnie powodując bałagan w naszym kontrolerze widoku.

Najpierw dodajemy następujące właściwości do naszej klasy kontrolera:

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

collapsedSectionszapisze zwinięte numery sekcji. sectionViewszapisze nasz niestandardowy widok przekroju.

Zsyntetyzuj to:

@synthesize collapsedSections;
@synthesize sectionViews;

Zainicjuj to:

- (void) viewDidLoad
{
    [super viewDidLoad];

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

Następnie musimy połączyć nasz UITableView, aby można było uzyskać do niego dostęp z poziomu naszej klasy kontrolera widoku:

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

Podłącz go z XIB, aby wyświetlić kontroler używając ctrl + dragjak zwykle.

Następnie tworzymy widok jako niestandardowy nagłówek sekcji dla naszego widoku tabeli, implementując tego delegata UITableView:

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

Następnie implementujemy metodę zapisywania utworzonego wcześniej niestandardowego nagłówka sekcji we właściwości klasy:

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

Dodaj UIGestureRecognizerDelegatedo naszego pliku .h kontrolera widoku:

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

Następnie tworzymy metodę attachTapGestureToView:

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

    [tapAction setDelegate:self];

    [view addGestureRecognizer:tapAction];
}

Powyższa metoda doda rozpoznawanie gestów dotknięcia do całego widoku przekroju, który utworzyliśmy wcześniej. Następnie należy zaimplementować onTap:selector

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

Powyższa metoda zostanie wywołana, gdy użytkownik dotknie jednej z naszych sekcji widoku tabeli. Ta metoda wyszukuje poprawny numer sekcji na podstawie naszej sectionViewstablicy, którą utworzyliśmy wcześniej.

Zaimplementowaliśmy również metodę pobierania, do której należy sekcja widoku nagłówka.

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

Następnie musimy zaimplementować metodę toggleCollapseSection:

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

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

Ta metoda wstawi / usunie numer sekcji do naszej collapsedSectionstablicy, którą utworzyliśmy wcześniej. Gdy numer sekcji zostanie wstawiony do tej tablicy, oznacza to, że sekcja powinna zostać zwinięta i rozwinięta, jeśli jest inaczej.

Następnie wdrożyć removeCollapsedSection:, addCollapsedSection:sectionaisCollapsedSection: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]];
}

Te trzy metody są tylko pomocnikami, które ułatwiają nam dostęp do collapsedSectionstablicy.

Na koniec zaimplementuj tego delegata widoku tabeli, aby nasze niestandardowe widoki sekcji wyglądały ładnie.

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

Mam nadzieję, że to pomoże.

yunhasnawa
źródło
1

Użyłem NSDictionary jako źródła danych, wygląda to na dużo kodu, ale jest naprawdę prosty i działa bardzo dobrze! jak tu wygląda

Utworzyłem wyliczenie dla sekcji

typedef NS_ENUM(NSUInteger, TableViewSection) {

    TableViewSection0 = 0,
    TableViewSection1,
    TableViewSection2,
    TableViewSectionCount
};

właściwość sekcji:

@property (nonatomic, strong) NSMutableDictionary * sectionsDisctionary;

Metoda zwracająca moje sekcje:

-(NSArray <NSNumber *> * )sections{

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

A następnie skonfiguruj moje źródło danych:

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

Kolejne metody pomogą ci dowiedzieć się, kiedy sekcja jest otwarta i jak odpowiedzieć na źródło danych widoku tabeli:

Odpowiedz w sekcji na źródło danych:

/**
 *  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;
}

Przybory:

/**
 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];
}

Przełącz widoczność sekcji

/**
 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];
    }
}
Anioł
źródło
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

Vinod Joshi
źródło
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

Vinod Joshi
źródło
Dlaczego dwie odpowiedzi? Nie wygląda na to, że podałeś dwa różne rozwiązania problemu.
Cristik
0

Rozwijając odpowiedź napisaną w celu C, napisałem następujące informacje dla osób piszących w języku Swift

Chodzi o to, aby użyć sekcji w tabeli i ustawić liczbę wierszy w sekcji na 1 (zwinięty) i 3 (rozwinięty), gdy pierwszy wiersz w tej sekcji zostanie dotknięty

Tabela decyduje o liczbie wierszy do narysowania na podstawie tablicy wartości logicznych

Musisz utworzyć dwa wiersze w scenorysie i nadać im identyfikatory ponownego użycia „CollapsingRow” i „GroupHeading”

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

}
Derek
źródło
0

Pewien przykładowy kod do animowania akcji rozwijania / zwijania przy użyciu nagłówka sekcji widoku tabeli jest udostępniany przez firmę Apple w sekcji Animacje i gesty widoku tabeli .

Kluczem do tego podejścia jest wdrożenie

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

i zwróć niestandardowy widok UIView, który zawiera przycisk (zazwyczaj taki sam rozmiar jak sam widok nagłówka). Podklasując UIView i używając tego dla widoku nagłówka (tak jak w tym przykładzie), można łatwo przechowywać dodatkowe dane, takie jak numer sekcji.

saraman
źródło
0

Zrobiłem to samo, używając wielu sekcji.

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

}
Tapash Mollick
źródło
0

Dodam to rozwiązanie dla kompletności i pokazując, jak pracować z nagłówkami sekcji.

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 do sedna: https://gist.github.com/pawelkijowskizimperium/fe1e8511a7932a0d40486a2669316d2c

pconor
źródło
0

w celu wsparcia rozwiązania @ jean.timex, użyj poniższego kodu, jeśli chcesz otworzyć jedną sekcję w dowolnym momencie. utwórz zmienną taką jak: var extendedSection = -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;
}
Suresh Durishetti
źródło