UITableView auf statische Zellen gesetzt. Ist es möglich, einige der Zellen programmgesteuert auszublenden?

132

UITableView auf statische Zellen setzen.

Ist es möglich, einige der Zellen programmgesteuert auszublenden?

Shmidt
quelle

Antworten:

48

Sie suchen diese Lösung:

StaticDataTableViewController 2.0

https://github.com/xelvenone/StaticDataTableViewController

die statische Zellen mit oder ohne Animation ein- / ausblenden / neu laden können!

[self cell:self.outletToMyStaticCell1 setHidden:hide]; 
[self cell:self.outletToMyStaticCell2 setHidden:hide]; 
[self reloadDataAnimated:YES];

Hinweis: Verwenden Sie immer nur (reloadDataAnimated: YES / NO) (rufen Sie [self.tableView reloadData] nicht direkt auf.)

Dies verwendet nicht die Hacky-Lösung mit der Einstellung Höhe auf 0 und ermöglicht es Ihnen, die Änderung zu animieren und ganze Abschnitte auszublenden

Peter Lapisu
quelle
6
Ein Problem bei dieser Lösung ist, dass Lücken dort bleiben, wo sich die versteckten Zellen befanden.
Jack
Wie meinst du Lücken? Kannst du die Situation besser beschreiben, vielleicht direkt auf Github? Haben Sie die Probe ausgeführt?
Peter Lapisu
1
Ich meine, wenn Sie zum Beispiel drei Abschnitte haben und die Zellen vor dem mittleren Abschnitt verstecken, bleibt der Raum, in dem dieser Abschnitt war, erhalten. Ich habe das Beispielprojekt nicht ausprobiert, aber ich habe den Code ausprobiert. Ich werde die Probe ausprobieren.
Jack
1
Schauen Sie sich das Beispiel an, es gab ein größeres Update, vielleicht hatten Sie einen alten Code ... funktioniert perfekt für mich
Peter Lapisu
Ich habe das Beispiel ausprobiert und es scheint dort zu funktionieren. Das einzige, was ich anders mache, ist die Verwendung von a, IBOutletCollectionaber ich sehe nicht, wie das einen Unterschied machen würde. Ich habe den Code erst gestern heruntergeladen, daher glaube ich nicht, dass es sich um eine alte Version handelt.
Jack
164

So verbergen Sie statische Zellen in UITable:

  1. Fügen Sie diese Methode hinzu :

In Ihrer UITableView-Controller-Delegatenklasse:

Ziel c:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell* cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if(cell == self.cellYouWantToHide)
        return 0; //set the hidden cell's height to 0

    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

Schnell:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    var cell = super.tableView(tableView, cellForRowAtIndexPath: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}

Diese Methode wird für jede Zelle in der UITable aufgerufen. Sobald es für die Zelle aufgerufen wird, die Sie ausblenden möchten, setzen wir ihre Höhe auf 0. Wir identifizieren die Zielzelle, indem wir einen Ausgang dafür erstellen:

  1. Erstellen Sie im Designer eine Steckdose für die Zelle (n), die Sie ausblenden möchten. Der Ausgang für eine solche Zelle wird oben als "cellYouWantToHide" bezeichnet.
  2. Aktivieren Sie im IB die Option "Clip-Unteransichten" für die Zellen, die Sie ausblenden möchten. Die Zellen, die Sie ausblenden, müssen ClipToBounds = YES haben. Andernfalls wird der Text in der UITableView angehäuft.
Genauso wie
quelle
10
Schöne Lösung. Um das ClipToBoundsProblem zu vermeiden , können Sie die Zelle auch auf "Ausgeblendet" setzen. Das scheint mir sauberer zu sein. ;-)
MonsieurDart
13
+1 für ClipToBounds = YES. Numeorus-Lösungen in anderen Threads haben das übersehen.
Jeff
2
Großartige Idee. Dies ist die einfachste Lösung, die ich gefunden habe. Andere Lösungen hatten Code an zwei oder mehr Stellen. Übrigens wird ClipToBounds in IB als "Clip-Unteransichten" aufgeführt.
VaporwareWolf
1
Nur eine kleine Korrektur, die Verwendung selbst .cellYouWantToHide statt dieser .cellYouWantToHide.
Zoltan Vinkler
2
Ohne ein Zellenobjekt zu erstellen, können wir auch "index.row" verwenden, um die "uitableviewcell"
g212gs
38

Der beste Weg ist wie im folgenden Blog beschrieben: http://ali-reynolds.com/2013/06/29/hide-cells-in-static-table-view/

Entwerfen Sie Ihre statische Tabellenansicht wie gewohnt im Interface Builder - einschließlich aller möglicherweise ausgeblendeten Zellen. Für jede potenzielle Zelle, die Sie ausblenden möchten, müssen Sie jedoch eines tun: Überprüfen Sie die Eigenschaft „Clip-Unteransichten“ der Zelle. Andernfalls verschwindet der Inhalt der Zelle nicht, wenn Sie versuchen, sie auszublenden (indem Sie ihre Höhe verringern) - später mehr).

SO - Sie haben einen Schalter in einer Zelle und der Schalter soll einige statische Zellen ausblenden und anzeigen. Schließen Sie es an eine IBAction an und machen Sie dort Folgendes:

[self.tableView beginUpdates];
[self.tableView endUpdates];

Das gibt Ihnen schöne Animationen für die Zellen, die erscheinen und verschwinden. Implementieren Sie nun die folgende Delegatenmethode für die Tabellenansicht:

- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 1 && indexPath.row == 1) { // This is the cell to hide - change as you need
    // Show or hide cell
        if (self.mySwitch.on) {
            return 44; // Show the cell - adjust the height as you need
        } else {
            return 0; // Hide the cell
        }
   }
   return 44;
}

Und das ist es. Wenn Sie den Schalter umlegen, wird die Zelle ausgeblendet und mit einer schönen, flüssigen Animation wieder angezeigt.

Mohamed Saleh
quelle
Update: Sie müssen auch die Zellbezeichnung so einstellen, dass sie mit Ihrer Zelle ausgeblendet / angezeigt wird, da sonst die Beschriftung ein Chaos verursacht.
Mohamed Saleh
1
Sie sollten beachten, dass Sie möglicherweise einen Laufzeitfehler erhalten, wenn eine UIView innerhalb des Inhalts Ihrer statischen Zelle Einschränkungen für die Zelle enthält. Inhalt, wenn diese Einschränkungen für Ihre neue Zellenhöhe ungültig werden.
Pedro Borges
1
Beste Antwort. Vielen Dank.
Eric Chen
1
Ich wusste, dass dieser Trick mit dynamischen Inhalten funktioniert. Funktioniert auch sehr gut mit statischer Aufladung. So einfach auch.
Ameisen
2
Dieser funktioniert wirklich, aber um einen fehlenden Punkt abzudecken: Wenn die auszublendende / angezeigte Zeile eine Datumsauswahl enthält, führen Sie nur [self.tableView beginUpdates] aus. [self.tableView endUpdates]; wird immer noch eine Panne hinterlassen, während Sie es verstecken. Um den Fehler zu beseitigen, sollten Sie [self.tableView reloadRowsAtIndexPaths: @ [indexPathOfTargetRow] withRowAnimation: UITableViewRowAnimationNone] aufrufen. Auch zu bemerken, dass die Zeilenanimation hier irgendwie "deaktiviert" ist.
Chris
34

Meine Lösung geht in eine ähnliche Richtung wie Gareth, obwohl ich einige Dinge anders mache.

Hier geht:

1. Verstecken Sie die Zellen

Es gibt keine Möglichkeit, die Zellen direkt auszublenden. UITableViewControllerist die Datenquelle, die die statischen Zellen bereitstellt, und derzeit gibt es keine Möglichkeit, "keine Zelle x bereitzustellen" anzugeben. Wir müssen also unsere eigene Datenquelle bereitstellen, die an die delegiert UITableViewController, um die statischen Zellen zu erhalten.

Am einfachsten ist es UITableViewController, alle Methoden zu unterordnen und zu überschreiben , die sich beim Ausblenden von Zellen anders verhalten müssen .

Im einfachsten Fall (Einzelabschnitts-Tabelle, alle Zellen haben die gleiche Höhe) würde dies folgendermaßen aussehen:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section    
{
    return [super tableView:tableView numberOfRowsInSection:section] - numberOfCellsHidden;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Recalculate indexPath based on hidden cells
    indexPath = [self offsetIndexPath:indexPath];

    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (NSIndexPath*)offsetIndexPath:(NSIndexPath*)indexPath
{
    int offsetSection = indexPath.section; // Also offset section if you intend to hide whole sections
    int numberOfCellsHiddenAbove = ... // Calculate how many cells are hidden above the given indexPath.row
    int offsetRow = indexPath.row + numberOfCellsHiddenAbove;

    return [NSIndexPath indexPathForRow:offsetRow inSection:offsetSection];
}

Wenn Ihre Tabelle mehrere Abschnitte enthält oder die Zellen unterschiedliche Höhen haben, müssen Sie weitere Methoden überschreiben. Hier gilt das gleiche Prinzip: Sie müssen indexPath, section und row versetzen, bevor Sie an super delegieren.

Beachten Sie auch, dass der indexPath-Parameter für Methoden wie didSelectRowAtIndexPath:für dieselbe Zelle je nach Status unterschiedlich ist (dh die Anzahl der ausgeblendeten Zellen). Daher ist es wahrscheinlich eine gute Idee, jeden indexPath-Parameter immer zu versetzen und mit diesen Werten zu arbeiten.

2. Animieren Sie die Änderung

Wie Gareth bereits sagte, treten große Störungen auf, wenn Sie Änderungen mithilfe der reloadSections:withRowAnimation:Methode animieren .

Ich habe herausgefunden, dass reloadData:die Animation erheblich verbessert wird, wenn Sie unmittelbar danach anrufen (nur noch kleine Störungen). Die Tabelle wird nach der Animation korrekt angezeigt.

Was ich also mache ist:

- (void)changeState
{
     // Change state so cells are hidden/unhidden
     ...

    // Reload all sections
    NSIndexSet* reloadSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfSectionsInTableView:tableView])];

    [tableView reloadSections:reloadSet withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView reloadData];
}
henning77
quelle
2
Segne dich, du süße süße Person.
Guptron
Haben Sie eine Lösung für die kleinen Animationsprobleme gefunden? Für mich werden die Trennlinien zwischen den Zellen nicht richtig animiert und ich verwende lieber keine Animation als eine klischeehafte. Tolle Lösung!
Maximilian Körner
Mein Problem ist, dass mit statischen Zellen numberOfRowsInSection:nur aufgerufen wird, wenn die Tabelle zum ersten Mal geladen wird. Wenn ich [self.tableView reloadData] aufrufe - numberOfRowsInSection:wird nie wieder aufgerufen. Nur cellForRowAtIndexPath:heißt. Was vermisse ich?
Roman
13
  1. Erstellen Sie im Designer eine Steckdose für die Zelle (n), die Sie ausblenden möchten. Zum Beispiel möchten Sie 'cellOne' ausblenden, also tun Sie dies in viewDidLoad ()

cellOneOutlet.hidden = true

Überschreiben Sie nun die folgende Methode, überprüfen Sie, welcher Zellenstatus ausgeblendet ist, und geben Sie die Höhe 0 für diese Zellen zurück. Dies ist eine von vielen Möglichkeiten, wie Sie jede Zelle in statischer tableView schnell ausblenden können.

override func tableView(tableView: UITableView, heightForRowAtIndexPathindexPath: NSIndexPath) -> CGFloat 
{

let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath)

        if tableViewCell.hidden == true
        {
            return 0
        }
        else{
             return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
        }

}
Saleh Masum
quelle
Beste Lösung bisher!
derdida
Fügen Sie tableView.reloadData () hinzu, nachdem Sie Änderungen vorgenommen haben, und es ist perfekt. Ersparte mir viel Mühe, die Lösung zu ändern! danke
Shayan C
1
Swift 4 lässt mich nicht benutzen let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath). Ich gehe davon aus, dass es durch ersetzt wird let tableViewCell = tableView.cellForRow(at: indexPath as IndexPath).
Clifton Labrum
Da ich statische Zellen auf a verwende UITableViewController, habe ich keine UITableViewDelegatmethoden. heightForRowBenötige ich auch andere Methoden , um es zum Aufrufen zu bringen ?
Clifton Labrum
@CliftonLabrum nein, Sie können nur diese Methode überschreiben.
Alexander Ershov
11

Ich habe mir eine Alternative ausgedacht, bei der Abschnitte tatsächlich ausgeblendet und nicht gelöscht werden. Ich habe den Ansatz von @ henning77 ausprobiert, bin aber immer wieder auf Probleme gestoßen, als ich die Anzahl der Abschnitte der statischen UITableView geändert habe. Diese Methode hat bei mir sehr gut funktioniert, aber ich versuche hauptsächlich, Abschnitte anstelle von Zeilen auszublenden. Ich entferne einige Zeilen im laufenden Betrieb erfolgreich, aber es ist viel chaotischer, deshalb habe ich versucht, Dinge in Abschnitte zu gruppieren, die ich ein- oder ausblenden muss. Hier ist ein Beispiel, wie ich Abschnitte verstecke:

Zuerst deklariere ich eine NSMutableArray-Eigenschaft

@property (nonatomic, strong) NSMutableArray *hiddenSections;

In viewDidLoad (oder nachdem Sie Ihre Daten abgefragt haben) können Sie dem Array Abschnitte hinzufügen, die Sie ausblenden möchten.

- (void)viewDidLoad
{
    hiddenSections = [NSMutableArray new];

    if(some piece of data is empty){
        // Add index of section that should be hidden
        [self.hiddenSections addObject:[NSNumber numberWithInt:1]];
    }

    ... add as many sections to the array as needed

    [self.tableView reloadData];
}

Implementieren Sie anschließend die folgenden TableView-Delegatmethoden

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return nil;
    }

    return [super tableView:tableView titleForHeaderInSection:section];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        return 0;
    }

    return [super tableView:tableView heightForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:indexPath.section]]){
        [cell setHidden:YES];
    }
}

Setzen Sie dann die Höhe der Kopf- und Fußzeile für ausgeblendete Abschnitte auf 1, da Sie die Höhe nicht auf 0 setzen können. Dies führt zu einem zusätzlichen Abstand von 2 Pixel. Wir können dies jedoch ausgleichen, indem wir die Höhe der nächsten sichtbaren Kopfzeile anpassen.

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 
{
    CGFloat height = [super tableView:tableView heightForHeaderInSection:section];

    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        height = 1; // Can't be zero
    }
    else if([self tableView:tableView titleForHeaderInSection:section] == nil){ // Only adjust if title is nil
        // Adjust height for previous hidden sections
        CGFloat adjust = 0;

        for(int i = (section - 1); i >= 0; i--){
            if([self.hiddenSections containsObject:[NSNumber numberWithInt:i]]){
                adjust = adjust + 2;
            }
            else {
                break;
            }
        }

        if(adjust > 0)
        {                
            if(height == -1){
                height = self.tableView.sectionHeaderHeight;
            }

            height = height - adjust;

            if(height < 1){
                height = 1;
            }
        }
    }

    return height;
}

-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 
{   
    if([self.hiddenSections containsObject:[NSNumber numberWithInt:section]]){
        return 1;
    }
    return [super tableView:tableView heightForFooterInSection:section];
}

Wenn Sie dann bestimmte Zeilen ausblenden müssen, können Sie die Anzahl der ZeilenInSection anpassen und festlegen, welche Zeilen in cellForRowAtIndexPath zurückgegeben werden. In diesem Beispiel habe ich hier einen Abschnitt mit drei Zeilen, in denen drei leer sein könnten und entfernt werden müssen.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSInteger rows = [super tableView:tableView numberOfRowsInSection:section];

    if(self.organization != nil){
        if(section == 5){ // Contact
            if([self.organization objectForKey:@"Phone"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"Email"] == [NSNull null]){     
                rows--;
            }

            if([self.organization objectForKey:@"City"] == [NSNull null]){     
                rows--;
            }
        }
    }

    return rows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{    
    return [super tableView:tableView cellForRowAtIndexPath:[self offsetIndexPath:indexPath]];
}

Verwenden Sie diesen offsetIndexPath, um den indexPath für Zeilen zu berechnen, in denen Sie Zeilen bedingt entfernen. Wird nicht benötigt, wenn Sie nur Abschnitte ausblenden

- (NSIndexPath *)offsetIndexPath:(NSIndexPath*)indexPath
{
    int row = indexPath.row;

    if(self.organization != nil){
        if(indexPath.section == 5){
            // Adjust row to return based on which rows before are hidden
            if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 0 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Address"] != [NSNull null]){     
                row = row + 2;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] != [NSNull null] && [self.organization objectForKey:@"Email"] == [NSNull null]){     
                row++;
            }
            else if(indexPath.row == 1 && [self.organization objectForKey:@"Phone"] == [NSNull null] && [self.organization objectForKey:@"Email"] != [NSNull null]){     
                row++;
            }
        }
    }

    NSIndexPath *offsetPath = [NSIndexPath indexPathForRow:row inSection:indexPath.section];

    return offsetPath;
}

Es gibt viele Methoden zum Überschreiben, aber was mir an diesem Ansatz gefällt, ist, dass er wiederverwendbar ist. Richten Sie das Array hiddenSections ein, fügen Sie es hinzu, und die richtigen Abschnitte werden ausgeblendet. Das Ausblenden der Reihen ist etwas kniffliger, aber möglich. Wir können nicht einfach die Höhe der Zeilen, die wir ausblenden möchten, auf 0 setzen, wenn wir eine gruppierte UITableView verwenden, da die Rahmen nicht korrekt gezeichnet werden.

Austin
quelle
1
Ich würde wahrscheinlich stattdessen NSMutableSetfür hiddenSectionsverwenden. Es ist viel schneller, da Sie meistens auf Mitgliedschaft testen.
Pixelfreak
Gute Antwort, Austin. Ich habe über diese Antwort abgestimmt und werde sie in Zukunft als Referenz für meine Projekte verwenden. pixelfreak macht auch einen guten Punkt bei der Verwendung NSMutableSetfür hiddenSections, obwohl ich verstehe, dass der Punkt Ihrer Antwort eher konzeptionell als wählerisch war, welche Art von Datenstruktur Sie verwenden sollten.
BigSauce
10

Es stellt sich heraus, dass Sie Zellen in einer statischen UITableView ein- und ausblenden können - und zwar mit Animation. Und es ist nicht so schwer zu erreichen.

Demo-Projekt

Demo-Projektvideo

Das Wesentliche:

  1. Use tableView:heightForRowAtIndexPath: um Zellenhöhen basierend auf einem bestimmten Status dynamisch anzugeben.
  2. Wenn sich der Status ändert, animieren Sie Zellen, die durch Aufrufen angezeigt / ausgeblendet werden tableView.beginUpdates();tableView.endUpdates()
  3. Rufen Sie nicht tableView.cellForRowAtIndexPath:innerhalbtableView:heightForRowAtIndexPath: . Verwenden Sie zwischengespeicherte indexPaths, um die Zellen zu unterscheiden.
  4. Zellen nicht verstecken. Legen Sie stattdessen die Eigenschaft "Clip Subviews" in Xcode fest.
  5. Verwenden Sie benutzerdefinierte Zellen (nicht einfach usw.), um eine schöne versteckte Animation zu erhalten. Behandeln Sie das automatische Layout auch korrekt für den Fall, dass die Zellenhöhe == 0 ist.

Mehr Infos in meinem Blog (russische Sprache)

Sergey Skoblikov
quelle
1
Dieser Punkt war für mich wichtig: "Zellen nicht ausblenden. Stattdessen die Eigenschaft" Clip-Unteransichten "in Xcode festlegen."
Balkoth
8

Ja, das ist definitiv möglich, obwohl ich im Moment mit dem gleichen Problem zu kämpfen habe. Ich habe es geschafft, die Zellen zum Verstecken zu bringen und alles funktioniert in Ordnung, aber ich kann das Ding derzeit nicht ordentlich animieren. Folgendes habe ich gefunden:

Ich verstecke Zeilen basierend auf dem Status eines EIN / AUS-Schalters in der ersten Zeile des ersten Abschnitts. Wenn der Schalter auf ON steht, befindet sich im selben Abschnitt 1 Zeile darunter, andernfalls befinden sich 2 verschiedene Zeilen.

Ich habe einen Selektor aufgerufen, wenn der Schalter umgeschaltet ist, und ich setze eine Variable, um anzuzeigen, in welchem ​​Zustand ich mich befinde. Dann rufe ich auf:

[[self tableView] reloadData];

Ich überschreibe die Funktion tableView: willDisplayCell: forRowAtIndexPath: und wenn die Zelle ausgeblendet werden soll, mache ich Folgendes :

[cell setHidden:YES];

Das verbirgt die Zelle und ihren Inhalt, entfernt aber nicht den Platz, den sie einnimmt.

Überschreiben Sie zum Entfernen des Leerzeichens die tableView: heightForRowAtIndexPath: und geben Sie 0 für Zeilen zurück, die ausgeblendet werden sollen.

Sie müssen auch tableView: numberOfRowsInSection: überschreiben und die Anzahl der Zeilen in diesem Abschnitt zurückgeben. Sie müssen hier etwas Seltsames tun, damit bei einer gruppierten Tabelle die abgerundeten Ecken in den richtigen Zellen auftreten. In meiner statischen Tabelle befindet sich der vollständige Satz von Zellen für den Abschnitt, sodass die erste Zelle die Option enthält, dann 1 Zelle für die EIN-Statusoptionen und 2 weitere Zellen für die AUS-Statusoptionen, insgesamt 4 Zellen. Wenn die Option aktiviert ist, muss ich 4 zurückgeben. Dies schließt die versteckte Option ein, damit die zuletzt angezeigte Option ein abgerundetes Feld enthält. Wenn die Option deaktiviert ist, werden die letzten beiden Optionen nicht angezeigt, sodass ich 2 zurückgebe. Dies alles fühlt sich klobig an. Entschuldigung, wenn dies nicht sehr klar ist, ist es schwierig zu beschreiben. Um den Aufbau zu veranschaulichen, ist dies der Aufbau des Tabellenabschnitts in IB:

  • Zeile 0: Option mit EIN / AUS-Schalter
  • Zeile 1: Wird angezeigt, wenn die Option aktiviert ist
  • Zeile 2: Wird angezeigt, wenn die Option AUS ist
  • Zeile 3: Wird angezeigt, wenn die Option deaktiviert ist

Wenn die Option aktiviert ist, werden in der Tabelle zwei Zeilen angezeigt:

  • Zeile 0: Option mit EIN / AUS-Schalter
  • Zeile 1: Wird angezeigt, wenn die Option aktiviert ist

Wenn die Option deaktiviert ist, werden in der Tabelle vier Zeilen angezeigt:

  • Zeile 0: Option mit EIN / AUS-Schalter
  • Zeile 1: Wird angezeigt, wenn die Option aktiviert ist
  • Zeile 2: Wird angezeigt, wenn die Option AUS ist
  • Zeile 3: Wird angezeigt, wenn die Option deaktiviert ist

Dieser Ansatz fühlt sich aus mehreren Gründen nicht richtig an. Er ist genau so weit wie ich bisher mit meinen Experimenten gekommen bin. Bitte lassen Sie mich wissen, wenn Sie einen besseren Weg finden. Die Probleme, die ich bisher beobachtet habe, sind:

  • Es fühlt sich falsch an, der Tabelle mitzuteilen, dass sich die Anzahl der Zeilen von der vermutlich in den zugrunde liegenden Daten enthaltenen unterscheidet.

  • Ich kann die Veränderung nicht animieren. Ich habe versucht, tableView: reloadSections: withRowAnimation: anstelle von reloadData zu verwenden, und die Ergebnisse scheinen keinen Sinn zu ergeben. Ich versuche immer noch, dies zum Laufen zu bringen. Derzeit scheint die tableView nicht die richtigen Zeilen zu aktualisieren, sodass eine verborgen bleibt, die angezeigt werden soll, und eine Leere unter der ersten Zeile verbleibt. Ich denke, dies könnte mit dem ersten Punkt über die zugrunde liegenden Daten zusammenhängen.

Hoffentlich kann jemand alternative Methoden vorschlagen oder vielleicht die Animation erweitern, aber vielleicht können Sie damit anfangen. Ich entschuldige mich für das Fehlen von Hyperlinks zu Funktionen. Ich habe sie eingefügt, aber sie wurden vom Spamfilter abgelehnt, weil ich ein ziemlich neuer Benutzer bin.

Gareth Clarke
quelle
Ich bin mir ziemlich sicher, dass Sie noch einmal anrufen sollten, [[self tableView] reloadData];nachdem Sie die Zellen versteckt haben
Shmidt
Es tut uns leid, dies wiederzubeleben, aber wie konnten Sie UITableViewController in Unterklassen unterteilen und mit statischen Zellen verwenden? Xcode lässt mich nicht.
Dany Joumaa
Nessup, Sie müssen UITableView nicht unterordnen. Wählen Sie im Storyboard Ihren TableViewController aus und klicken Sie dann auf TableView. Jetzt können Sie zwischen dynamischen / statischen Zellen wählen.
Shmidt
1
Ich habe die Lösung gefunden, wie man statische Zellen zum Ein- und Ausblenden animiert. Rufen Sie einfach return [super tableView:tableView cellForRowAtIndexPath:indexPath];in UITableViewControllerUnterklasse statische Zellen zu schaffen.
Indexpfade
8

Nach Justas 'Antwort, aber für Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let cell = super.tableView(tableView, cellForRowAt: indexPath)

    if cell == self.cellYouWantToHide {
        return 0
    }

    return super.tableView(tableView, heightForRowAt: indexPath)
}
Chris Herbst
quelle
1
Möglicherweise benötigen Sie eine tableView.reloadRows(at:, with:), um die Zellen zu aktualisieren, wenn Sie die Höhe ändern, während die Zeile bereits sichtbar ist.
Frost-Lee
5

Okay, nach einigem Versuch habe ich eine nicht häufige Antwort. Ich verwende die Variable "isHidden" oder "hidden", um zu überprüfen, ob diese Zelle ausgeblendet werden soll.

  1. Erstellen Sie ein IBOutlet für Ihren View Controller. @IBOutlet weak var myCell: UITableViewCell!

  2. Aktualisieren Sie die myCellin Ihrer benutzerdefinierten Funktion. Beispiel: Sie können sie in viewDidLoad hinzufügen:

override func viewDidLoad() { super.viewDidLoad() self.myCell.isHidden = true }

  1. in Ihrer Delegatenmethode:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cell = super.tableView(tableView, cellForRowAt: indexPath) guard !cell.isHidden else { return 0 } return super.tableView(tableView, heightForRowAt: indexPath) }

Dies reduziert Ihre Logik bei der Delegate-Methode und Sie müssen sich nur auf Ihre Geschäftsanforderungen konzentrieren.

BananZ
quelle
4

Die obigen Antworten, die Zellen ausblenden / anzeigen, rowHeight ändern oder mit Einschränkungen für das automatische Layout herumspielen, haben bei mir aufgrund von Problemen mit dem automatischen Layout nicht funktioniert. Der Code wurde unerträglich.

Für eine einfache statische Tabelle funktionierte das Beste für mich:

  1. Erstellen Sie einen Ausgang für jede Zelle in der statischen Tabelle
  2. Erstellen Sie ein Array nur mit den Ausgängen der anzuzeigenden Zellen
  3. Überschreiben Sie cellForRowAtIndexPath, um die Zelle aus dem Array zurückzugeben
  4. Überschreiben Sie numberOfRowsInSection, um die Anzahl der Arrays zurückzugeben
  5. Implementieren Sie eine Methode, um zu bestimmen, welche Zellen sich in diesem Array befinden müssen, rufen Sie diese Methode bei Bedarf auf und laden Sie dann die Daten neu.

Hier ist ein Beispiel von meinem Table View Controller:

@IBOutlet weak var titleCell: UITableViewCell!
@IBOutlet weak var nagCell: UITableViewCell!
@IBOutlet weak var categoryCell: UITableViewCell!

var cellsToShow: [UITableViewCell] = []

override func viewDidLoad() {
    super.viewDidLoad()
    determinCellsToShow()
}

func determinCellsToShow() {
    if detail!.duration.type != nil {
        cellsToShow = [titleCell, nagCell, categoryCell]
    }
    else {
        cellsToShow = [titleCell,  categoryCell]
    }
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    return cellsToShow[indexPath.row]
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return cellsToShow.count
}
David Barta
quelle
Das hat auch 5 Jahre später super geklappt! Ich danke dir sehr.
Schrockwell
2020 und dies ist die beste Lösung dafür. Kein Hack nötig.
Mdonati
3

Einfache kompatible Methode für iOS 11 und IB / Storyboard

Für iOS 11 stellte ich fest, dass eine modifizierte Version von Mohamed Salehs Antwort am besten funktioniert, wobei einige Verbesserungen auf der Dokumentation von Apple basieren. Es animiert gut, vermeidet hässliche Hacks oder fest codierte Werte und verwendet Zeilenhöhen, die bereits im Interface Builder festgelegt wurden .

Das Grundkonzept besteht darin, die Zeilenhöhe für alle ausgeblendeten Zeilen auf 0 zu setzen. Verwenden Sie dann tableView.performBatchUpdates, um eine Animation auszulösen, die konsistent funktioniert.

Stellen Sie die Zellenhöhen ein

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath == indexPathOfHiddenCell {
        if cellIsHidden {
            return 0
        }
    }
    // Calling super will use the height set in your storyboard, avoiding hardcoded values
    return super.tableView(tableView, heightForRowAt: indexPath)
}

Sie wollen sicherstellen cellIsHiddenund indexPathOfHiddenCellwerden in geeigneter Weise auf Ihren Anwendungsfall eingestellt. Für meinen Code sind sie Eigenschaften auf meinem Table View Controller.

Die Zelle umschalten

Um die Sichtbarkeit zu steuern (wahrscheinlich eine Schaltflächenaktion oder didSelectRow), schalten Sie den cellIsHidden-Status innerhalb eines performBatchUpdatesBlocks um:

tableView.performBatchUpdates({
                // Use self to capture for block
                self.cellIsHidden = !self.cellIsHidden 
            }, completion: nil)

Apple empfiehlt performBatchUpdatesüber beginUpdates/endUpdates wann immer möglich.

Robmathers
quelle
1

Ich habe eine Lösung gefunden, um versteckte Zellen in einer statischen Tabelle zu animieren.

// Class for wrapping Objective-C block
typedef BOOL (^HidableCellVisibilityFunctor)();
@interface BlockExecutor : NSObject
@property (strong,nonatomic) HidableCellVisibilityFunctor block;
+ (BlockExecutor*)executorWithBlock:(HidableCellVisibilityFunctor)block;
@end
@implementation BlockExecutor
@synthesize block = _block;
+ (BlockExecutor*)executorWithBlock:(HidableCellVisibilityFunctor)block
{
    BlockExecutor * executor = [[BlockExecutor alloc] init];
    executor.block = block;
    return executor;
}
@end

Es wird nur ein zusätzliches Wörterbuch benötigt:

@interface MyTableViewController ()
@property (nonatomic) NSMutableDictionary * hidableCellsDict;
@property (weak, nonatomic) IBOutlet UISwitch * birthdaySwitch;
@end

Schauen Sie sich auch die Implementierung von MyTableViewController an. Wir benötigen zwei Methoden, um indexPath zwischen sichtbaren und unsichtbaren Indizes zu konvertieren ...

- (NSIndexPath*)recoverIndexPath:(NSIndexPath *)indexPath
{
    int rowDelta = 0;
    for (NSIndexPath * ip in [[self.hidableCellsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
    {
        BlockExecutor * executor = [self.hidableCellsDict objectForKey:ip];
        if (ip.section == indexPath.section
            && ip.row <= indexPath.row + rowDelta
            && !executor.block())
        {
            rowDelta++;
        }
    }
    return [NSIndexPath indexPathForRow:indexPath.row+rowDelta inSection:indexPath.section];
}

- (NSIndexPath*)mapToNewIndexPath:(NSIndexPath *)indexPath
{
    int rowDelta = 0;
    for (NSIndexPath * ip in [[self.hidableCellsDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
    {
        BlockExecutor * executor = [self.hidableCellsDict objectForKey:ip];
        if (ip.section == indexPath.section
            && ip.row < indexPath.row - rowDelta
            && !executor.block())
        {
            rowDelta++;
        }
    }
    return [NSIndexPath indexPathForRow:indexPath.row-rowDelta inSection:indexPath.section];
}

Eine IBAction zur Änderung des UISwitch-Werts:

- (IBAction)birthdaySwitchChanged:(id)sender
{
    NSIndexPath * indexPath = [self mapToNewIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]];
    if (self.birthdaySwitch.on)
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    else
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}

Einige UITableViewDataSource- und UITableViewDelegate-Methoden:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int numberOfRows = [super tableView:tableView numberOfRowsInSection:section];
    for (NSIndexPath * indexPath in [self.hidableCellsDict allKeys])
        if (indexPath.section == section)
        {
            BlockExecutor * executor = [self.hidableCellsDict objectForKey:indexPath];
            numberOfRows -= (executor.block()?0:1);
        }
    return numberOfRows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    indexPath = [self recoverIndexPath:indexPath];
    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    indexPath = [self recoverIndexPath:indexPath];
    return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // initializing dictionary
    self.hidableCellsDict = [NSMutableDictionary dictionary];
    [self.hidableCellsDict setObject:[BlockExecutor executorWithBlock:^(){return self.birthdaySwitch.on;}] forKey:[NSIndexPath indexPathForRow:1 inSection:1]];
}

- (void)viewDidUnload
{
    [self setBirthdaySwitch:nil];
    [super viewDidUnload];
}

@end
k06a
quelle
1

Antworte schnell :

Fügen Sie Ihrem TableViewController die folgende Methode hinzu:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return indexPathOfCellYouWantToHide == indexPath ? 0 : super.tableView(tableView, heightForRowAtIndexPath: indexPath)
}

Wenn die tableView versucht, die Zelle zu zeichnen, die Sie ausblenden möchten, wird sie nicht angezeigt, da ihre Höhe dank der obigen Methode auf 0pt gesetzt wird. Alles andere bleibt unverändert.

Bitte beachten Sie, dass indexPathOfCellYouWantToHidedies jederzeit geändert werden kann :)

Federico Zanetello
quelle
1

In> Swift 2.2 habe ich hier einige Antworten zusammengefasst.

Stellen Sie eine Verbindung zum Storyboard her, um eine Verknüpfung zu Ihrer staticCell herzustellen.

@IBOutlet weak var updateStaticCell: UITableViewCell!

override func viewDidLoad() {
    ...
    updateStaticCell.hidden = true
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if indexPath.row == 0 {
        return 0
    } else {
        return super.tableView(tableView, heightForRowAtIndexPath: indexPath)
    }
}

Ich möchte meine erste Zelle ausblenden, also setze ich die Höhe wie oben beschrieben auf 0.

CodeOverRide
quelle
1

Swift 4:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    var height = super.tableView(tableView, heightForRowAt: indexPath)
    if (indexPath.row == HIDDENROW) {
        height = 0.0
    }
    return height
}
Pizzamonster
quelle
0

Für das einfachste Szenario, wenn Sie Zellen ganz unten in der Tabellenansicht ausblenden, können Sie das contentInset von tableView anpassen, nachdem Sie die Zelle ausgeblendet haben:

- (void)adjustBottomInsetForHiddenSections:(NSInteger)numberOfHiddenSections
{
    CGFloat bottomInset = numberOfHiddenSections * 44.0; // or any other 'magic number
    self.tableView.contentInset = UIEdgeInsetsMake(self.tableView.contentInset.top, self.tableView.contentInset.left, -bottomInset, self.tableView.contentInset.right);
}
Vladimir Shutyuk
quelle
0

Dies ist eine neue Methode, um dies mit https://github.com/k06a/ABStaticTableViewController zu tun

NSIndexPath *ip = [NSIndexPath indexPathForRow:1 section:1];
[self deleteRowsAtIndexPaths:@[ip] withRowAnimation:UITableViewRowAnimationFade]
k06a
quelle
0

Die Lösung von k06a ( https://github.com/k06a/ABStaticTableViewController ) ist besser, da sie den gesamten Abschnitt einschließlich der Kopf- und Fußzeilen der Zellen verbirgt, wobei diese Lösung ( https://github.com/peterpaulis/StaticDataTableViewController ) alles außer der Fußzeile verbirgt.

BEARBEITEN

Ich habe gerade eine Lösung gefunden, wenn Sie die Fußzeile verstecken möchten StaticDataTableViewController. Folgendes müssen Sie in die Datei StaticTableViewController.m kopieren:

- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    } else {
        return [super tableView:tableView titleForFooterInSection:section];
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {

    CGFloat height = [super tableView:tableView heightForFooterInSection:section];

    if (self.originalTable == nil) {
        return height;
    }

    if (!self.hideSectionsWithHiddenRows) {
        return height;
    }

    OriginalSection * os = self.originalTable.sections[section];
    if ([os numberOfVissibleRows] == 0) {
       //return 0;
        return CGFLOAT_MIN;
    } else {
        return height;
    }

    //return 0;
    return CGFLOAT_MIN;
}
pawel221
quelle
0

Sicher kannst du. Kehren Sie zunächst zu Ihrer tableView-Anzahl der Zellen zurück, die Sie anzeigen möchten, und rufen Sie dann superauf, um eine bestimmte Zelle aus Ihrem Storyboard zu erhalten, und geben Sie sie für tableView zurück:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.mode.numberOfCells()
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = super.tableView(tableView, cellForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))

    return cell
}

Wenn Ihre Zellen eine andere Höhe haben, geben Sie diese ebenfalls zurück:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return super.tableView(tableView, heightForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))
}
Petr Syrov
quelle
0

Zusätzlich zur @ Saleh Masum-Lösung:

Wenn Sie Fehler beim automatischen Layout erhalten , können Sie die Einschränkungen einfach aus dem entfernentableViewCell.contentView

Swift 3:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    let tableViewCell = super.tableView(tableView, cellForRowAt: indexPath)

    if tableViewCell.isHidden == true
    {
        tableViewCell.contentView.removeConstraints(tableViewCell.contentView.constraints)
        return 0
    }
    else{
        return super.tableView(tableView, heightForRowAt: indexPath)
    }

}

Diese Lösung hängt vom Ablauf Ihrer App ab . Wenn Sie die Zelle in derselben View Controller-Instanz ein- / ausblenden möchten, ist dies möglicherweise nicht die beste Wahl, da dadurch die Einschränkungen aufgehoben werden .

Luca
quelle
0

Ich habe eine bessere Möglichkeit, statische Zellen und sogar Abschnitte dynamisch ohne Hacks auszublenden.

Wenn Sie die Zeilenhöhe auf 0 setzen, kann eine Zeile ausgeblendet werden. Dies funktioniert jedoch nicht, wenn Sie einen gesamten Abschnitt ausblenden möchten, der einige Leerzeichen enthält, selbst wenn Sie alle Zeilen ausblenden.

Mein Ansatz ist es, ein Abschnittsarray statischer Zellen zu erstellen. Dann wird der Inhalt der Tabellenansicht vom Abschnittsarray gesteuert.

Hier ist ein Beispielcode:

var tableSections = [[UITableViewCell]]()

private func configTableSections() {
    // seciton A
    tableSections.append([self.cell1InSectionA, self.cell2InSectionA])

    // section B
    if shouldShowSectionB {
        tableSections.append([self.cell1InSectionB, self.cell2InSectionB])
    }

    // section C
    if shouldShowCell1InSectionC {
        tableSections.append([self.cell1InSectionC, self.cell2InSectionC, self.cell3InSectionC])
    } else {
        tableSections.append([self.cell2InSectionC, self.cell3InSectionC])
    }
}

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

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tableSections[section].count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    return tableSections[indexPath.section][indexPath.row]
}

Auf diese Weise können Sie Ihren gesamten Konfigurationscode zusammenstellen, ohne den bösen Code schreiben zu müssen, um die Anzahl der Zeilen und Abschnitte zu berechnen. Und natürlich nein0 Höhen mehr.

Dieser Code ist auch sehr einfach zu pflegen. Zum Beispiel, wenn Sie weitere Zellen oder Abschnitte hinzufügen / entfernen möchten.

Ebenso können Sie ein Titelarray für Abschnittsüberschriften und ein Titelarray für Abschnittsfußzeilen erstellen, um Ihre Abschnittsüberschriften dynamisch zu konfigurieren.

Simon Wang
quelle