UITableView ustawione na komórki statyczne. Czy można programowo ukryć niektóre komórki?

132

UITableView ustawione na komórki statyczne.

Czy można programowo ukryć niektóre komórki?

Shmidt
źródło

Odpowiedzi:

48

Szukasz tego rozwiązania:

StaticDataTableViewController 2.0

https://github.com/xelvenone/StaticDataTableViewController

który może pokazać / ukryć / przeładować dowolne statyczne komórki z animacją lub bez!

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

Uwaga, aby zawsze używać tylko (reloadDataAnimated: YES / NO) (nie wywołuj [self.tableView reloadData] bezpośrednio)

Nie wykorzystuje to hackerskiego rozwiązania z ustawieniem wysokości na 0 i pozwala animować zmianę i ukrywać całe sekcje

Peter Lapisu
źródło
6
Jednym z problemów z tym rozwiązaniem jest to, że pozostawia luki w miejscach, w których były ukryte komórki.
Jack
co masz na myśli? czy możesz lepiej opisać sytuację, może bezpośrednio na githubie? sprawdziłeś próbkę?
Peter Lapisu
1
Chodzi mi o to, że jeśli masz na przykład trzy sekcje i ukryjesz komórki ze środkowej sekcji, przestrzeń, w której ta sekcja była, pozostaje. Nie próbowałem przykładowego projektu, ale wypróbowałem kod. Spróbuję tej próbki.
Jack
1
sprawdź próbkę, była większa aktualizacja, może miałeś jakiś stary kod ... działa idealnie dla mnie
Peter Lapisu
Wypróbowałem próbkę i wydaje się, że tam działa, jedyną rzeczą, którą robię inaczej, jest użycie a, IBOutletCollectionale nie widzę, jak to by miało znaczenie. Ściągnąłem kod dopiero wczoraj, więc nie sądzę, że jest to stara wersja.
Jack
164

Aby ukryć komórki statyczne w UITable:

  1. Dodaj tę metodę :

W klasie delegata kontrolera UITableView:

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

Szybki:

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

Ta metoda zostanie wywołana dla każdej komórki w UITable. Po wywołaniu komórki, którą chcesz ukryć, ustawiamy jej wysokość na 0. Identyfikujemy komórkę docelową, tworząc dla niej gniazdo:

  1. W projektancie utwórz ujście dla komórek, które chcesz ukryć. Wyjście dla jednej takiej komórki jest nazwane powyżej „komórka, którą chcesz ukryć”.
  2. Zaznacz „Podglądy wycinków” w IB dla komórek, które chcesz ukryć. Ukrywane komórki muszą mieć ClipToBounds = YES. W przeciwnym razie tekst będzie gromadził się w UITableView.
Tak jak
źródło
10
Niezłe rozwiązanie. Aby uniknąć tego ClipToBoundsproblemu, możesz również ustawić komórkę jako ukrytą. Wydaje mi się to czystsze. ;-)
MonsieurDart
13
+1 dla ClipToBounds = TAK. Rozwiązania Numeorus w innych wątkach to przegapiły.
Jeff,
2
Świetny pomysł. To najłatwiejsze rozwiązanie, jakie znalazłem. Inne rozwiązania miały kod w dwóch lub więcej lokalizacjach. BTW, w IB, ClipToBounds jest wymienione jako „Podglądy klipów”.
VaporwareWolf
1
Tylko mała korekta, użyj self .cellYouWantToHide zamiast tego .cellYouWantToHide.
Zoltan Vinkler
2
bez tworzenia obiektu komórki możemy również użyć „index.row”, aby ukryć „uitableviewcell”
g212gs
38

Najlepszy sposób jest opisany na następującym blogu http://ali-reynolds.com/2013/06/29/hide-cells-in-static-table-view/

Zaprojektuj swój statyczny widok tabeli w normalny sposób w konstruktorze interfejsu - wraz ze wszystkimi potencjalnie ukrytymi komórkami. Ale jest jedna rzecz, którą musisz zrobić dla każdej potencjalnej komórki, którą chcesz ukryć - zaznacz właściwość „Clip subviews” komórki, w przeciwnym razie zawartość komórki nie zniknie, gdy spróbujesz ją ukryć (zmniejszając jej wysokość - więcej później).

A więc - masz przełącznik w komórce, a przełącznik ma ukrywać i pokazywać statyczne komórki. Podłącz go do IBAction i tam zrób to:

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

Daje to przyjemne animacje dla pojawiających się i znikających komórek. Teraz zaimplementuj następującą metodę delegata widoku tabeli:

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

I to wszystko. Przesuń przełącznik, a komórka ukryje się i pojawi się ponownie z ładną, płynną animacją.

Mohamed Saleh
źródło
Aktualizacja: musisz również ustawić etykietę komórki na Ukryj / pokaż razem z tobą Ukryj / pokaż komórkę, w przeciwnym razie etykiety będą powodować bałagan.
Mohamed Saleh
1
Należy zauważyć, że jeśli jakikolwiek UIView w zawartości komórki statycznej ma jakiekolwiek ograniczenia względem komórki cell.content, może wystąpić błąd w czasie wykonywania, jeśli te ograniczenia staną się nieprawidłowe dla nowej wysokości komórki.
Pedro Borges
1
Najlepsza odpowiedź. Dzięki.
Eric Chen
1
Wiedziałem, że ta sztuczka działa z dynamiczną zawartością. Działa całkiem nieźle ze statycznymi. To też takie proste.
Ants
2
Ten naprawdę działa, ale aby wypełnić brakujący punkt: jeśli wiersz do ukrycia / wyświetlenia zawiera selektor dat, robi tylko [self.tableView beginUpdates]; [self.tableView endUpdates]; nadal pozostawi usterkę podczas jej ukrywania. Aby wyeliminować usterkę, należy wywołać [self.tableView reloadRowsAtIndexPaths: @ [indexPathOfTargetRow] withRowAnimation: UITableViewRowAnimationNone]; Należy również zauważyć, że animacja wierszy jest tutaj w jakiś sposób „wyłączona”.
Chris
34

Moje rozwiązanie idzie w podobnym kierunku, co Gareth, chociaż niektóre rzeczy robię inaczej.

Tutaj idzie:

1. Ukryj komórki

Nie ma możliwości bezpośredniego ukrycia komórek. UITableViewControllerto źródło danych, które udostępnia komórki statyczne i obecnie nie ma sposobu, aby powiedzieć „nie dostarczaj komórki x”. Musimy więc zapewnić własne źródło danych, które jest delegowane do UITableViewControllerw celu uzyskania statycznych komórek.

Najłatwiej jest tworzyć podklasy UITableViewControlleri zastępować wszystkie metody, które muszą zachowywać się inaczej podczas ukrywania komórek .

W najprostszym przypadku (tabela z pojedynczą sekcją, wszystkie komórki mają tę samą wysokość) wyglądałoby to tak:

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

Jeśli tabela ma wiele sekcji lub komórki mają różne wysokości, musisz zastąpić więcej metod. Ta sama zasada obowiązuje tutaj: musisz przesunąć indexPath, sekcję i wiersz przed delegowaniem do super.

Należy również pamiętać, że parametr indexPath dla metod takich jak didSelectRowAtIndexPath:będzie różny dla tej samej komórki, w zależności od stanu (tj. Liczby komórek ukrytych). Dlatego prawdopodobnie dobrym pomysłem jest zawsze kompensowanie dowolnego parametru indexPath i praca z tymi wartościami.

2. Ożyw zmianę

Jak już powiedział Gareth, możesz mieć poważne usterki, jeśli animujesz zmiany za pomocą reloadSections:withRowAnimation:metody.

Dowiedziałem się, że jeśli zadzwonisz reloadData:zaraz potem, animacja znacznie się poprawi (pozostały tylko drobne usterki). Tabela jest wyświetlana poprawnie po animacji.

Więc to co robię to:

- (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
źródło
2
Błogosławię cię, słodka, słodka osoba.
guptron,
czy znalazłeś rozwiązanie dla drobnych usterek animacji? dla mnie linie separatora między komórkami nie będą animowane poprawnie i raczej nie używają animacji niż gliched. świetne rozwiązanie!
Maximilian Körner
Mój problem polega na tym, że ze statycznymi komórkami numberOfRowsInSection:wywoływana jest tylko przy pierwszym ładowaniu tabeli. Kiedy wywołuję [self.tableView reloadData] - numberOfRowsInSection:nigdy nie jestem ponownie wywoływany. Tylko cellForRowAtIndexPath:jest wezwany. czego mi brakuje?
Roman
13
  1. W projektancie utwórz ujście dla komórek, które chcesz ukryć. Na przykład chcesz ukryć „cellOne”, więc w viewDidLoad () zrób to

cellOneOutlet.hidden = true

teraz zastąp poniższą metodę, sprawdź, który stan komórki jest ukryty i zwróć wysokość 0 dla tych komórek. Jest to jeden z wielu sposobów na ukrycie dowolnej komórki w statycznym widoku tableView.

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
źródło
Jak dotąd najlepsze rozwiązanie!
derdida,
dodaj tableView.reloadData () po wprowadzeniu zmian i jest idealnie. Zaoszczędziło mi wiele wysiłku, aby zmienić rozwiązanie! dzięki
Shayan C
1
Swift 4 nie pozwala mi używać let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath). Zakładam, że jest zastąpiony przez let tableViewCell = tableView.cellForRow(at: indexPath as IndexPath).
Clifton Labrum,
Ponieważ używam komórek statycznych na a UITableViewController, nie mam żadnych UITableViewmetod delegata. Aby to zadzwoniło heightForRow, czy muszę mieć również inne metody?
Clifton Labrum,
@CliftonLabrum nie, możesz nadpisać tylko tę metodę.
Alexander Ershov
11

Wymyśliłem alternatywę, która faktycznie ukrywa sekcje i ich nie usuwa. Wypróbowałem podejście @ henning77, ale napotykałem problemy, gdy zmieniałem liczbę sekcji statycznego widoku UITableView. Ta metoda zadziałała dla mnie naprawdę dobrze, ale przede wszystkim próbuję ukryć sekcje zamiast wierszy. Z powodzeniem usuwam niektóre wiersze w locie, ale jest dużo bardziej bałaganiarski, więc próbowałem pogrupować elementy w sekcje, które muszę pokazać lub ukryć. Oto przykład tego, jak ukrywam sekcje:

Najpierw deklaruję właściwość NSMutableArray

@property (nonatomic, strong) NSMutableArray *hiddenSections;

W viewDidLoad (lub po przeszukaniu danych) możesz dodać sekcje, które chcesz ukryć do tablicy.

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

Następnie zaimplementuj następujące metody delegata TableView

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

Następnie ustaw wysokość nagłówka i stopki na 1 dla ukrytych sekcji, ponieważ nie możesz ustawić wysokości na 0. Powoduje to dodatkowe 2 piksele, ale możemy to nadrobić, dostosowując wysokość następnego widocznego nagłówka.

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

Następnie, jeśli masz określone wiersze do ukrycia, możesz dostosować numberOfRowsInSection i które wiersze są zwracane w cellForRowAtIndexPath. W tym przykładzie mam sekcję, która ma trzy wiersze, gdzie dowolne trzy mogą być puste i należy je usunąć.

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

Użyj tego offsetIndexPath, aby obliczyć indexPath dla wierszy, w których warunkowo usuwasz wiersze. Nie jest potrzebne, jeśli ukrywasz tylko sekcje

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

Istnieje wiele metod do zastąpienia, ale w tym podejściu podoba mi się to, że można je ponownie wykorzystać. Skonfiguruj tablicę hiddenSections, dodaj do niej, a spowoduje to ukrycie właściwych sekcji. Ukrywanie rzędów jest trochę trudniejsze, ale możliwe. Nie możemy po prostu ustawić wysokości wierszy, które chcemy ukryć, na 0, jeśli używamy zgrupowanego UITableView, ponieważ obramowania nie zostaną poprawnie narysowane.

Austin
źródło
1
I prawdopodobnie używać NSMutableSetdla hiddenSectionszamiast. Jest to o wiele szybsze, ponieważ najczęściej testujesz członkostwo.
pixelfreak
Dobra odpowiedź, Austin. Głosowałem za tą odpowiedzią i wykorzystam ją jako punkt odniesienia dla moich projektów w przyszłości. pixelfreak również ma sens, jeśli chodzi o używanie NSMutableSetfor hiddenSections, chociaż rozumiem, że punkt twojej odpowiedzi był bardziej koncepcyjny niż drobiazgowy co do tego, jakiego typu struktury danych powinieneś użyć.
BigSauce
10

Okazuje się, że możesz ukrywać i wyświetlać komórki w statycznym widoku UITableView - i za pomocą animacji. I nie jest to takie trudne.

Projekt demonstracyjny

Film z projektu demonstracyjnego

Istota:

  1. Use tableView:heightForRowAtIndexPath: do dynamicznego określania wysokości komórek na podstawie pewnego stanu.
  2. Gdy stan się zmieni, animuj komórki pokazujące / ukrywane przez wywołanie tableView.beginUpdates();tableView.endUpdates()
  3. Nie dzwoń do tableView.cellForRowAtIndexPath:środka tableView:heightForRowAtIndexPath:. Użyj zapisanych w pamięci podręcznej indexPaths, aby rozróżnić komórki.
  4. Nie ukrywaj komórek. Zamiast tego ustaw właściwość „Clip Subviews” w Xcode.
  5. Użyj komórek niestandardowych (nie zwykłych itp.), Aby uzyskać ładną animację ukrywania. Również poprawnie obsłuż Auto Layout dla przypadku, gdy wysokość komórki == 0.

Więcej informacji na moim blogu (język rosyjski)

Sergey Skoblikov
źródło
1
Ten punkt był dla mnie ważny: "Nie ukrywaj komórek. Zamiast tego ustaw właściwość" Clip Subviews "w Xcode."
balkot
8

Tak, na pewno jest to możliwe, chociaż obecnie borykam się z tym samym problemem. Udało mi się zmusić komórki do ukrycia i wszystko działa dobrze, ale obecnie nie mogę sprawić, by obiekt był dobrze ożywiony. Oto co znalazłem:

Ukrywam wiersze na podstawie stanu przełącznika WŁ. / WYŁ. W pierwszym rzędzie pierwszej sekcji. Jeśli przełącznik jest włączony, pod nim znajduje się 1 rząd w tej samej sekcji, w przeciwnym razie są 2 różne rzędy.

Mam selektor wywoływany, gdy przełącznik jest przełączany, i ustawiam zmienną, aby wskazać, w jakim stanie jestem. Następnie wzywam:

[[self tableView] reloadData];

I przesłonić tableView: willDisplayCell: forRowAtIndexPath: funkcję i jeśli komórka ma być ukryty zrobić to:

[cell setHidden:YES];

To ukrywa komórkę i jej zawartość, ale nie usuwa zajmowanej przez nią przestrzeni.

Aby usunąć spację, nadpisz tableView: heightForRowAtIndexPath: function i zwróć 0 dla wierszy, które powinny być ukryte.

Musisz także nadpisać tableView: numberOfRowsInSection: i zwrócić liczbę wierszy w tej sekcji. Musisz zrobić tutaj coś dziwnego, aby jeśli twoja tabela jest stylem zgrupowanym, zaokrąglone rogi wystąpiły we właściwych komórkach. W mojej statycznej tabeli znajduje się pełny zestaw komórek dla sekcji, więc jest pierwsza komórka zawierająca opcję, następnie 1 komórka dla opcji stanu WŁĄCZENIA i 2 kolejne komórki dla opcji stanu WYŁĄCZENIA, łącznie 4 komórki. Kiedy opcja jest WŁĄCZONA, muszę zwrócić 4, obejmuje to opcję ukrytą, tak aby ostatnia wyświetlona opcja miała zaokrąglone pole. Gdy opcja jest wyłączona, dwie ostatnie opcje nie są wyświetlane, więc zwracam 2. To wszystko wydaje się niezgrabne. Przepraszam, jeśli nie jest to zbyt jasne, trudno to opisać. Aby zilustrować konfigurację, oto konstrukcja sekcji tabeli w IB:

  • Rząd 0: Opcja z włącznikiem / wyłącznikiem
  • Wiersz 1: wyświetlany, gdy opcja jest włączona
  • Wiersz 2: wyświetlany, gdy opcja jest wyłączona
  • Wiersz 3: wyświetlany, gdy opcja jest wyłączona

Więc gdy opcja jest WŁĄCZONA, w tabeli pojawiają się dwa wiersze:

  • Rząd 0: Opcja z włącznikiem / wyłącznikiem
  • Wiersz 1: wyświetlany, gdy opcja jest włączona

Gdy opcja jest WYŁĄCZONA, tabela wyświetla cztery wiersze, które są:

  • Rząd 0: Opcja z włącznikiem / wyłącznikiem
  • Wiersz 1: wyświetlany, gdy opcja jest włączona
  • Wiersz 2: wyświetlany, gdy opcja jest wyłączona
  • Wiersz 3: wyświetlany, gdy opcja jest wyłączona

To podejście nie wydaje się poprawne z kilku powodów, tak daleko, jak osiągnąłem z moich dotychczasowych eksperymentów, więc daj mi znać, jeśli znajdziesz lepszy sposób. Problemy, które zaobserwowałem do tej pory to:

  • Nieprawidłowe jest informowanie tabeli, że liczba wierszy różni się od tego, co przypuszczalnie zawiera dane bazowe.

  • Nie mogę ożywić zmiany. Próbowałem użyć tableView: reloadSections: withRowAnimation: zamiast reloadData i wyniki wydają się nie mieć sensu, nadal próbuję to uruchomić. Obecnie wydaje się, że tableView nie aktualizuje poprawnych wierszy, więc jeden pozostaje ukryty, który powinien zostać wyświetlony, a pod pierwszym wierszem pozostaje puste miejsce. Myślę, że może to być związane z pierwszym punktem dotyczącym danych bazowych.

Miejmy nadzieję, że ktoś będzie w stanie zasugerować alternatywne metody lub może to, jak rozszerzyć animację, ale może to pomoże Ci zacząć. Przepraszam za brak hiperłączy do funkcji, wstawiłem je, ale zostały odrzucone przez filtr spamu, ponieważ jestem dość nowym użytkownikiem.

Gareth Clarke
źródło
Jestem prawie pewien, że powinieneś zadzwonić [[self tableView] reloadData];jeszcze raz po ukryciu
cel
Przepraszam, że ożywiam to, ale jak udało Ci się podklasę UITableViewController i używać go z komórkami statycznymi? Xcode mi nie pozwala.
Dany Joumaa
Nessup, nie musisz tworzyć podklasy UITableView. W scenorysie wybierz swój TableViewController, a następnie kliknij TableView. Teraz możesz wybierać między komórkami dynamicznymi / statycznymi.
Shmidt,
1
Znalazłem rozwiązanie, jak animować ukrywanie / wyświetlanie statycznych komórek. Wystarczy zadzwonić return [super tableView:tableView cellForRowAtIndexPath:indexPath];na UITableViewControllerpodklasy dla tworzenia komórek statycznych. Ścieżki indeksu są indeksowane statycznie - nie dynamicznie ...
k06a
8

Zgodnie z odpowiedzią Justasa, ale dla 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
źródło
1
może być konieczne tableView.reloadRows(at:, with:)zaktualizowanie komórek, jeśli zmienisz wysokość, gdy wiersz jest już widoczny.
Frost-Lee
5

OK, po kilku próbach mam rzadką odpowiedź. Używam zmiennej „isHidden” lub „hidden”, aby sprawdzić, czy ta komórka powinna być ukryta.

  1. utwórz IBOutlet do kontrolera widoku. @IBOutlet weak var myCell: UITableViewCell!

  2. Zaktualizuj myCellw swojej funkcji niestandardowej, na przykład możesz dodać ją w viewDidLoad:

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

  1. w metodzie delegata:

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

Zmniejszy to logikę w metodzie delegowania i wystarczy skupić się na wymaganiach biznesowych.

BananZ
źródło
4

Powyższe odpowiedzi, które ukrywają / pokazują komórki, zmieniają rowHeight lub bałagan z ograniczeniami automatycznego układu, nie działają dla mnie z powodu problemów z automatycznym układem. Kod stał się nie do zniesienia.

W przypadku prostego statycznego stołu najlepiej działało dla mnie:

  1. Utwórz wylot dla każdej komórki w tabeli statycznej
  2. Utwórz tablicę tylko z wyjściami komórek do pokazania
  3. Zastąp cellForRowAtIndexPath, aby zwrócić komórkę z tablicy
  4. Zastąp numberOfRowsInSection, aby zwrócić liczbę tablicy
  5. Zaimplementuj metodę, aby określić, które komórki muszą znajdować się w tej tablicy, i wywołaj tę metodę w razie potrzeby, a następnie przeładuj dane.

Oto przykład z mojego kontrolera widoku tabeli:

@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
źródło
To działało świetnie, nawet 5 lat później! Dziękuję bardzo.
Schrockwell
2020 i to jest najlepsze rozwiązanie tego problemu. Nie trzeba hakować.
mdonati
3

Prosta metoda kompatybilna z iOS 11 i IB / Storyboard

W przypadku iOS 11 stwierdziłem, że zmodyfikowana wersja odpowiedzi Mohameda Saleha działa najlepiej, z pewnymi ulepszeniami opartymi na dokumentacji Apple. Animuje się ładnie, unika wszelkich brzydkich hacków lub zakodowanych wartości i używa wysokości wierszy już ustawionych w Interface Builder .

Podstawową koncepcją jest ustawienie wysokości wiersza na 0 dla wszystkich ukrytych wierszy. Następnie użyj, tableView.performBatchUpdatesaby wywołać animację, która działa konsekwentnie.

Ustaw wysokości celi

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

Będziesz chciał się upewnić cellIsHiddeni indexPathOfHiddenCellsą ustawione odpowiednio do twojego przypadku użycia. Dla mojego kodu są to właściwości mojego kontrolera widoku tabeli.

Przełączanie komórki

Bez względu na metodę kontrolującą widoczność (prawdopodobnie działanie przycisku lub didSelectRow), przełącz stan cellIsHidden wewnątrz performBatchUpdatesbloku:

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

Apple zaleca performBatchUpdatesponad beginUpdates/endUpdates kiedy tylko jest to możliwe.

robmathers
źródło
1

Znalazłem rozwiązanie do animacji ukrywania komórek w statycznej tabeli.

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

Potrzebny jest tylko jeden dodatkowy słownik:

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

I spójrz na implementację MyTableViewController. Potrzebujemy dwóch metod, aby przekonwertować indexPath między widocznymi i niewidocznymi indeksami ...

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

Jedna IBAction w sprawie zmiany wartości UISwitch:

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

Niektóre metody UITableViewDataSource i UITableViewDelegate:

- (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
źródło
1

Odpowiedz szybko :

Dodaj następującą metodę w TableViewController:

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

jeśli tableView spróbuje narysować komórkę, którą chcesz ukryć, nie wyświetli jej, ponieważ jej wysokość zostanie ustawiona na 0pt dzięki powyższej metodzie, wszystko inne pozostaje niezmienione.

Należy pamiętać, że indexPathOfCellYouWantToHidemożna to zmienić w dowolnym momencie :)

Federico Zanetello
źródło
1

W> Swift 2.2 połączyłem tutaj kilka odpowiedzi.

Zrób gniazdko ze scenorysu, aby połączyć się z staticCell.

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

Chcę ukryć moją pierwszą komórkę, więc ustawiłem wysokość na 0, jak opisano powyżej.

CodeOverRide
źródło
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
źródło
0

Aby uzyskać najłatwiejszy scenariusz, gdy ukryjesz komórki na samym dole widoku tabeli, możesz dostosować contentInset tableView po ukryciu komórki:

- (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
źródło
0

To nowy sposób na zrobienie tego za pomocą https://github.com/k06a/ABStaticTableViewController

NSIndexPath *ip = [NSIndexPath indexPathForRow:1 section:1];
[self deleteRowsAtIndexPaths:@[ip] withRowAnimation:UITableViewRowAnimationFade]
k06a
źródło
0

Rozwiązanie z k06a ( https://github.com/k06a/ABStaticTableViewController ) jest lepsze, ponieważ ukrywa całą sekcję, w tym nagłówki i stopki komórek, gdzie to rozwiązanie ( https://github.com/peterpaulis/StaticDataTableViewController ) ukrywa wszystko oprócz stopki.

EDYTOWAĆ

Właśnie znalazłem rozwiązanie, jeśli chcesz ukryć stopkę StaticDataTableViewController. Oto, co musisz skopiować w pliku StaticTableViewController.m:

- (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
źródło
0

Na pewno możesz. Najpierw wróć do swojej tabeli, wyświetl liczbę komórek, które chcesz wyświetlić, a następnie wywołaj, superaby uzyskać określoną komórkę ze swojego storyboardu i zwróć ją dla tableView:

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
}

Jeśli twoje komórki mają inną wysokość, zwróć ją również:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return super.tableView(tableView, heightForRowAtIndexPath: self.mode.indexPathForIndexPath(indexPath))
}
Petr Syrov
źródło
0

Oprócz rozwiązania @Saleh Masum:

Jeśli wystąpią błędy automatycznego układu , możesz po prostu usunąć ograniczenia z plikutableViewCell.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)
    }

}

To rozwiązanie zależy od przepływu Twojej aplikacji . Jeśli chcesz pokazać / ukryć komórkę w tej samej instancji kontrolera widoku, może to nie być najlepszy wybór, ponieważ usuwa ograniczenia .

Luca
źródło
0

Mam lepszy sposób na dynamiczne ukrywanie statycznych komórek, a nawet sekcji, bez żadnych hacków.

Ustawienie wysokości wiersza na 0 może ukryć wiersz, ale to nie działa, jeśli chcesz ukryć całą sekcję, która będzie zawierać spacje, nawet jeśli ukryjesz wszystkie wiersze.

Moje podejście polega na zbudowaniu tablicy sekcji komórek statycznych. Następnie zawartość widoku tabeli będzie sterowana przez tablicę sekcji.

Oto przykładowy kod:

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

W ten sposób możesz złożyć cały kod konfiguracji bez konieczności pisania nieprzyjemnego kodu do obliczania liczby wierszy i sekcji. I oczywiście nie ma 0już wysokości.

Ten kod jest również bardzo łatwy w utrzymaniu. Na przykład, jeśli chcesz dodać / usunąć więcej komórek lub sekcji.

Podobnie, możesz utworzyć tablicę tytułów nagłówków sekcji i tablicę tytułów stopek sekcji, aby dynamicznie skonfigurować tytuły sekcji.

Simon Wang
źródło