UITableView установлен на статические ячейки. Можно ли программно скрыть некоторые ячейки?


132

UITableView установить в статические ячейки.

Можно ли программно скрыть некоторые ячейки?

Ответы:


48

Вы ищете это решение:

StaticDataTableViewController 2.0

https://github.com/xelvenone/StaticDataTableViewController

который может отображать / скрывать / перезагружать любые статические ячейки с анимацией или без нее!

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

Примечание всегда использовать только (reloadDataAnimated: YES / NO) (не вызывайте [self.tableView reloadData] напрямую)

Это не использует хакерское решение с установкой высоты 0 и позволяет вам анимировать изменения и скрывать целые разделы


6
Одна проблема с этим решением заключается в том, что оно оставляет зазоры там, где были скрытые ячейки.
Джек

как ты имеешь ввиду пробелы? можешь лучше описать ситуацию, может прямо на гитхабе? вы запускали образец?
Питер Лапису

1
Я имею в виду, что если у вас есть, например, три раздела, и вы скрываете ячейки из средней части, остается место, где был этот раздел. Я не пробовал пример проекта, но я попробовал код. Я попробую образец.
Джек,

1
проверьте образец, там было более крупное обновление, возможно, у вас был какой-то старый код ... работает идеально для меня
Питер Лапису,

Я попробовал образец, и, похоже, он там работает, единственное, что я делаю иначе, - это использую, IBOutletCollectionно я не вижу, как это повлияет на разницу. Я только вчера загрузил код, поэтому не думаю, что это старая версия.
Джек,

164

Чтобы скрыть статические ячейки в UITable:

  1. Добавьте этот метод :

В вашем классе делегата контроллера UITableView:

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

Swift:

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

Этот метод будет вызываться для каждой ячейки в UITable. После вызова ячейки, которую вы хотите скрыть, мы устанавливаем ее высоту равной 0. Мы идентифицируем целевую ячейку, создав для нее выход:

  1. В дизайнере создайте выход для ячеек, которые вы хотите скрыть. Выход для одной такой ячейки выше называется «cellYouWantToHide».
  2. Отметьте «Подпредставления клипов» в IB для ячеек, которые вы хотите скрыть. Для скрытых ячеек необходимо, чтобы ClipToBounds = YES. В противном случае текст будет накапливаться в UITableView.

10
Хорошее решение. Чтобы избежать этой ClipToBoundsпроблемы, вы также можете сделать ячейку скрытой. Мне это показалось бы чище. ;-)
MonsieurDart

13
+1 для ClipToBounds = ДА. Решения Numeorus в других потоках упустили это.
Джефф

2
Отличная идея. Это самое простое решение, которое я нашел. Другие решения содержали код в двух или более местах. Кстати, в IB, ClipToBounds указан как «Подпредставления клипа».
VaporwareWolf

1
Небольшое исправление, используйте self .cellYouWantToHide вместо этого .cellYouWantToHide.
Золтан Винклер

2
без создания объекта ячейки, мы также можем использовать "index.row", чтобы скрыть "uitableviewcell"
g212gs

38

Лучший способ, как описано в следующем блоге http://ali-reynolds.com/2013/06/29/hide-cells-in-static-table-view/

Создайте представление статической таблицы как обычно в конструкторе интерфейса - со всеми потенциально скрытыми ячейками. Но есть одна вещь, которую вы должны сделать для каждой потенциальной ячейки, которую вы хотите скрыть - проверьте свойство «Обрезать подпредставления» ячейки, иначе содержимое ячейки не исчезнет, ​​когда вы попытаетесь скрыть ее (уменьшив ее высоту - подробнее позже).

ТАК - у вас есть переключатель в ячейке, и предполагается, что переключатель скрывает и показывает некоторые статические ячейки. Подключите его к IBAction и там сделайте это:

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

Это дает вам хорошую анимацию для появления и исчезновения клеток. Теперь реализуйте следующий метод делегата табличного представления:

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

И это все. Нажмите переключатель, и ячейка скроется и снова появится с красивой плавной анимацией.


Обновление: вам также необходимо установить для метки ячейки значение Скрыть / показать вместе с ячейкой Скрыть / показать, иначе метки будут беспорядочно.
Мохамед Салех

1
Вы должны отметить, что если какой-либо UIView в содержимом вашей статической ячейки имеет какие-либо ограничения в отношении cell.content, вы можете получить ошибку времени выполнения, если эти ограничения станут недействительными для вашей новой высоты ячейки.
Педро Борхес

1
Лучший ответ. Спасибо.
Эрик Чен

1
Я знал, что этот трюк работает с динамическим контентом. Также неплохо работает со статикой. Так просто.
Ants

2
Это действительно работает, но чтобы скрыть упущенный момент: если строка, которую нужно скрыть / показать, содержит средство выбора даты, выполняется только [self.tableView beginUpdates]; [self.tableView endUpdates]; все равно оставит глюк, скрывая его. Чтобы устранить сбой, вы должны вызвать [self.tableView reloadRowsAtIndexPaths: @ [indexPathOfTargetRow] withRowAnimation: UITableViewRowAnimationNone]; Также следует заметить, что здесь как-то "отключена" строчная анимация.
Крис

34

Мое решение идет в том же направлении, что и Гарет, хотя кое-что я делаю иначе.

Поехали:

1. Скройте ячейки

Нет возможности напрямую скрыть ячейки. UITableViewController- это источник данных, который предоставляет статические ячейки, и в настоящее время нет способа сказать ему «не предоставлять ячейку x». Таким образом, мы должны предоставить собственный источник данных, который делегирует функции UITableViewControllerобъекту для получения статических ячеек.

Проще всего создать подкласс UITableViewControllerи переопределить все методы, которые должны вести себя по-другому при скрытии ячеек .

В простейшем случае (таблица с одним разделом, все ячейки имеют одинаковую высоту) это будет выглядеть так:

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

Если в вашей таблице несколько разделов или ячейки имеют разную высоту, вам необходимо переопределить другие методы. Здесь применяется тот же принцип: вам нужно сместить indexPath, section и row перед делегированием super.

Также имейте в виду, что параметр indexPath для таких методов, как didSelectRowAtIndexPath: будет отличаться для одной и той же ячейки в зависимости от состояния (т. Количества скрытых ячеек). Поэтому, вероятно, будет хорошей идеей всегда смещать любой параметр indexPath и работать с этими значениями.

2. Анимируйте изменение

Как уже сказал Гарет, вы получаете серьезные сбои, если вы анимируете изменения, используя reloadSections:withRowAnimation:метод.

Я обнаружил, что если вы позвоните reloadData:сразу после этого, анимация значительно улучшится (остались только незначительные глюки). После анимации таблица отображается правильно.

Итак, что я делаю, это:

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

2
Благослови тебя, милый милый человек.
guptron

вы нашли решение для незначительных сбоев анимации? для меня разделительные линии между ячейками не будут правильно анимироваться, и id скорее не будет использовать анимацию, а не использовать gliched. отличное решение!
Максимилиан Кёрнер,

Моя проблема в том, что со статическими ячейками numberOfRowsInSection:вызывается только при первой загрузке таблицы. Когда я вызываю [self.tableView reloadData] - меня numberOfRowsInSection:больше не вызывают. Только cellForRowAtIndexPath:называется. Чего мне не хватает?
Роман

13
  1. В дизайнере создайте выход для ячеек, которые вы хотите скрыть. Например, вы хотите скрыть cellOne, поэтому в viewDidLoad () сделайте это

cellOneOutlet.hidden = true

теперь переопределите метод ниже, проверьте, какой статус ячейки скрыт, и верните высоту 0 для этих ячеек. Это один из многих способов скрыть любую ячейку в статическом tableView в swift.

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

}

Лучшее решение на данный момент!
Дердида

добавить tableView.reloadData () после внесения изменений, и это идеально. Спасло меня много усилий, чтобы изменить решение! спасибо
Shayan C

1
Swift 4 не позволяет мне использовать let tableViewCell = super.tableView(tableView,cellForRowAtIndexPath: indexPath). Я предполагаю, что он заменен на let tableViewCell = tableView.cellForRow(at: indexPath as IndexPath).
Clifton Labrum

Поскольку я использую статические ячейки в a UITableViewController, у меня нет UITableViewметодов делегатов. heightForRowНужны ли мне и другие методы, чтобы вызвать его?
Clifton Labrum

@CliftonLabrum нет, вы можете переопределить только этот метод.
Александр Ершов

11

Я придумал альтернативу, которая фактически скрывает разделы и не удаляет их. Я попробовал подход @ henning77, но продолжал сталкиваться с проблемами, когда менял количество разделов статического UITableView. Этот метод мне очень понравился, но я в первую очередь пытаюсь скрыть разделы, а не строки. Я удаляю некоторые строки на лету, но это намного сложнее, поэтому я попытался сгруппировать вещи в секции, которые мне нужно показать или скрыть. Вот пример того, как я скрываю разделы:

Сначала я объявляю свойство NSMutableArray

@property (nonatomic, strong) NSMutableArray *hiddenSections;

В viewDidLoad (или после того, как вы запросили свои данные) вы можете добавить разделы, которые вы хотите скрыть в массив.

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

Затем реализуйте следующие методы делегата 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];
    }
}

Затем установите высоту верхнего и нижнего колонтитула на 1 для скрытых разделов, потому что вы не можете установить высоту на 0. Это приводит к дополнительному 2 пиксельному пространству, но мы можем компенсировать это, отрегулировав высоту следующего видимого заголовка.

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

Затем, если у вас есть определенные строки, которые нужно скрыть, вы можете настроить numberOfRowsInSection и то, какие строки возвращаются в cellForRowAtIndexPath. В этом примере у меня есть раздел с тремя строками, любые три из которых могут быть пустыми и должны быть удалены.

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

Используйте этот offsetIndexPath для вычисления indexPath для строк, из которых вы условно удаляете строки. Не требуется, если вы только скрываете разделы

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

Есть много методов для переопределения, но что мне нравится в этом подходе, так это то, что его можно использовать повторно. Настройте массив hiddenSections, добавьте к нему, и он скроет правильные разделы. Скрыть ряды немного сложнее, но возможно. Мы не можем просто установить высоту строк, которые мы хотим скрыть, равной 0, если мы используем сгруппированный UITableView, потому что границы не будут отображаться правильно.


1
Я бы , вероятно , использовать NSMutableSetдля hiddenSectionsвместо. Это намного быстрее, поскольку вы в основном тестируете членство.
pixelfreak

Хороший ответ, Остин. Я проголосовал за этот ответ и буду использовать его как ссылку для своих проектов в будущем. pixelfreak также хорошо подходит NSMutableSetдля использования for hiddenSections, хотя я понимаю, что суть вашего ответа была более концептуальной, чем придирчивой, относительно того, какой тип структуры данных вы должны использовать.
BigSauce

10

Оказывается, вы можете скрывать и отображать ячейки в статическом UITableView - и с анимацией. И это не так уж и сложно.

Демо-проект

Видео демонстрационного проекта

Суть:

  1. Use tableView:heightForRowAtIndexPath: для динамического определения высоты ячеек на основе некоторого состояния.
  2. При изменении состояния анимированные ячейки отображаются / скрываются путем вызова tableView.beginUpdates();tableView.endUpdates()
  3. Не tableView.cellForRowAtIndexPath:заходите внутрь tableView:heightForRowAtIndexPath:. Используйте кэшированные indexPaths, чтобы различать ячейки.
  4. Не прячьте клетки. Вместо этого установите свойство «Clip Subviews» в Xcode.
  5. Используйте пользовательские ячейки (не простые и т. Д.), Чтобы получить красивую анимацию скрытия. Также правильно обрабатывайте Auto Layout для случая, когда высота ячейки == 0.

Больше информации в моем блоге (русский язык)


1
Этот момент был для меня важен: «Не скрывайте ячейки. Вместо этого установите свойство« Clip Subviews »в Xcode».
Balkoth

8

Да, это определенно возможно, хотя сейчас я борюсь с той же проблемой. Мне удалось заставить клетки спрятаться, и все работает нормально, но в настоящее время я не могу аккуратно оживить их. Вот что я нашел:

Я скрываю строки в зависимости от состояния переключателя ВКЛ / ВЫКЛ в первой строке первого раздела. Если переключатель находится в положении ON, под ним в том же разделе находится 1 строка, в противном случае - 2 разные строки.

У меня есть селектор, вызываемый при переключении переключателя, и я устанавливаю переменную, чтобы указать, в каком состоянии я нахожусь. Затем я вызываю:

[[self tableView] reloadData];

Я переопределяю функцию tableView: willDisplayCell: forRowAtIndexPath: и, если ячейка должна быть скрыта, я делаю следующее:

[cell setHidden:YES];

Это скрывает ячейку и ее содержимое, но не удаляет занимаемое ею пространство.

Чтобы удалить пробел, переопределите tableView: heightForRowAtIndexPath: и верните 0 для строк, которые должны быть скрыты.

Вам также необходимо переопределить tableView: numberOfRowsInSection: и вернуть количество строк в этом разделе. Здесь нужно сделать что-то странное, чтобы, если ваш стол был сгруппированным, закругленные углы появлялись в правильных ячейках. В моей статической таблице есть полный набор ячеек для раздела, поэтому есть первая ячейка, содержащая параметр, затем 1 ячейка для параметров состояния ВКЛ и еще 2 ячейки для параметров состояния ВЫКЛ, всего 4 ячейки. Когда опция включена, я должен вернуть 4, это включает скрытую опцию, так что последняя отображаемая опция имеет закругленную рамку. Когда опция отключена, последние две опции не отображаются, поэтому я возвращаю 2. Это все кажется неуклюжим. Извините, если это не очень понятно, сложно описать. Просто, чтобы проиллюстрировать настройку, это конструкция раздела таблицы в IB:

  • Строка 0: Вариант с переключателем ВКЛ / ВЫКЛ.
  • Строка 1: отображается, когда опция включена
  • Строка 2: отображается, когда опция выключена.
  • Строка 3: отображается, когда опция выключена.

Таким образом, когда опция включена, в таблице отображаются две строки:

  • Строка 0: Вариант с переключателем ВКЛ / ВЫКЛ.
  • Строка 1: отображается, когда опция включена

Когда опция ВЫКЛЮЧЕНА, таблица содержит четыре строки:

  • Строка 0: Вариант с переключателем ВКЛ / ВЫКЛ.
  • Строка 1: отображается, когда опция включена
  • Строка 2: отображается, когда опция выключена.
  • Строка 3: отображается, когда опция выключена.

Этот подход не кажется правильным по нескольким причинам, так же, как я дошел до моих экспериментов, поэтому, пожалуйста, дайте мне знать, если вы найдете лучший способ. До сих пор я наблюдал следующие проблемы:

  • Было бы неправильно сообщать таблице, что количество строк отличается от того, что предположительно содержится в базовых данных.

  • Кажется, я не могу оживить это изменение. Я пробовал использовать tableView: reloadSections: withRowAnimation: вместо reloadData, и результаты, похоже, не имеют смысла, я все еще пытаюсь заставить это работать. В настоящее время, похоже, происходит то, что tableView не обновляет правильные строки, поэтому одна остается скрытой, которая должна отображаться, а под первой строкой остается пустота. Я думаю, это может быть связано с первым пунктом о базовых данных.

Надеюсь, кто-нибудь сможет предложить альтернативные методы или, возможно, расширить с помощью анимации, но, возможно, это поможет вам начать. Приношу свои извинения за отсутствие гиперссылок на функции, я вставил их, но они были отклонены спам-фильтром, потому что я довольно новый пользователь.


Я почти уверен, что тебе стоит позвонить [[self tableView] reloadData];еще раз после того, как я спрятала камеры
Шмидт

Извините, что возродил это, но как вы смогли создать подкласс UITableViewController и использовать его со статическими ячейками? XCode не позволит мне.
Dany Joumaa

Nessup, вам не нужно создавать подклассы UITableView. В раскадровке выберите свой TableViewController, затем нажмите TableView. Теперь вы можете выбирать между динамическими / статическими ячейками.
Шмидт

1
Я нашел решение, как анимировать скрытие / отображение статических ячеек. Просто позвоните return [super tableView:tableView cellForRowAtIndexPath:indexPath];в UITableViewControllerподклассе для создания статических ячеек. Пути индексов индексируются статически, а не динамически ...
k06a

8

Согласно ответу Джастаса, но для 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)
}

1
вам может понадобиться tableView.reloadRows(at:, with:)обновить ячейки, если вы измените высоту, когда строка уже видна.
Frost-Lee

5

Ладно, после некоторых попыток у меня есть необычный ответ. Я использую переменную isHidden или скрытый, чтобы проверить, должна ли эта ячейка быть скрытой.

  1. создайте IBOutlet для вашего контроллера представления. @IBOutlet weak var myCell: UITableViewCell!

  2. Обновите myCellв своей пользовательской функции, например, вы можете добавить ее в viewDidLoad:

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

  1. в вашем методе делегата:

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

Это уменьшит вашу логику в методе делегата, и вам нужно будет сосредоточиться только на своих бизнес-требованиях.


4

Приведенные выше ответы, которые скрывают / отображают ячейки, изменяют rowHeight или беспорядок с ограничениями автоматического макета, не работали для меня из-за проблем с автоматическим макетом. Код стал невыносимым.

Для простой статической таблицы мне лучше всего было:

  1. Создайте розетку для каждой ячейки статической таблицы
  2. Создайте массив только с выходами ячеек для отображения
  3. Переопределить cellForRowAtIndexPath, чтобы вернуть ячейку из массива
  4. Переопределите numberOfRowsInSection, чтобы вернуть счетчик массива
  5. Реализуйте метод, чтобы определить, какие ячейки должны быть в этом массиве, и при необходимости вызывайте этот метод, а затем перезагружайте данные.

Вот пример из моего контроллера табличного представления:

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

Это отлично сработало даже 5 лет спустя! Огромное спасибо.
Schrockwell

2020, и это лучшее решение этой проблемы. Никакого взлома не требуется.
mdonati

3

Простой метод, совместимый с iOS 11 и IB / раскадровкой

Для iOS 11 я обнаружил, что модифицированная версия лучше всего ответа Мохамеда Салеха с некоторыми улучшениями, основанными на документации Apple. Он прекрасно анимируется, избегает уродливых взломов или жестко запрограммированных значений и использует высоту строк, уже установленную в Interface Builder. .

Основная идея состоит в том, чтобы установить высоту строки равной 0 для любых скрытых строк. Тогда используйтеtableView.performBatchUpdates для запуска анимации, которая работает стабильно.

Установите высоту ячейки

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

Вы хотите убедиться, cellIsHidden и indexPathOfHiddenCellправильно настроены на ваш случай использования. Для моего кода они являются свойствами моего контроллера табличного представления.

Переключение ячейки

В любом методе управления видимостью (вероятно, с помощью кнопки или didSelectRow) переключите состояние cellIsHidden внутри performBatchUpdatesблока:

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

Apple рекомендует performBatchUpdatesбольше beginUpdatesиendUpdates всегда, когда это возможно.


1

Я нашел решение для анимации скрытых ячеек в статической таблице.

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

Требуется только один дополнительный словарь:

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

И посмотрите на реализацию MyTableViewController. Нам нужны два метода для преобразования indexPath между видимым и невидимым индексами ...

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

Одно действие IBAction при изменении значения 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];
}

Некоторые методы UITableViewDataSource и 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

1

Ответь в кратчайшие сроки :

Добавьте следующий метод в свой TableViewController:

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

если tableView пытается нарисовать ячейку, которую вы хотите скрыть, он не будет отображаться, потому что его высота будет установлена ​​на 0pt благодаря описанному выше методу, все остальное остается неизменным.

Обратите внимание, что indexPathOfCellYouWantToHideможно изменить в любой момент :)


1

В> Swift 2.2 я собрал здесь несколько ответов.

Сделайте выход из раскадровки для ссылки на свой 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)
    }
}

Я хочу скрыть свою первую ячейку, поэтому установил высоту 0, как описано выше.


1

Свифт 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
}

0

В самом простом сценарии, когда вы скрываете ячейки в самом низу представления таблицы, вы можете настроить tableView contentInset после того, как скроете ячейку:

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


0

Решение от k06a ( https://github.com/k06a/ABStaticTableViewController ) лучше, потому что оно скрывает весь раздел, включая верхние и нижние колонтитулы ячеек, где это решение ( https://github.com/peterpaulis/StaticDataTableViewController ) скрывает все, кроме нижнего колонтитула.

РЕДАКТИРОВАТЬ

Я только что нашел решение, если вы хотите скрыть нижний колонтитул StaticDataTableViewController. Вот что вам нужно скопировать в файл 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;
}

0

Конечно можно. Во-первых, вернитесь к вашему tableView количество ячеек, которое вы хотите показать, затем вызовите superдля достижения определенной ячейки из вашей раскадровки и верните ее для 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
}

Если у ваших ячеек другая высота, верните и ее:

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

0

В дополнение к решению @Saleh Masum:

Если вы получаете ошибки автоматического макета , вы можете просто удалить ограничения изtableViewCell.contentView

Свифт 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)
    }

}

Это решение зависит от потока вашего приложения . Если вы хотите показать / скрыть ячейку в том же экземпляре контроллера представления, это может быть не лучшим выбором, поскольку он снимает ограничения .


0

У меня появился лучший способ динамически скрывать статические ячейки и даже разделы без каких-либо взломов.

Установка высоты строки на 0 может скрыть строку, но это не сработает, если вы хотите скрыть весь раздел, который будет содержать некоторые пробелы, даже если вы скрываете все строки.

Мой подход заключается в создании массива секций статических ячеек. Тогда содержимое табличного представления будет управляться массивом разделов.

Вот пример кода:

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

Таким образом, вы можете собрать весь свой код конфигурации без необходимости написания неприятного кода для вычисления количества строк и разделов. И конечно нет0 высот.

Этот код также очень легко поддерживать. Например, если вы хотите добавить / удалить больше ячеек или разделов.

Аналогично, вы можете создать массив заголовков разделов и массив заголовков нижнего колонтитула раздела, чтобы динамически конфигурировать заголовки разделов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.