UITableViewCell развернуть по клику - PullRequest
52 голосов
/ 08 января 2011

Допустим, у нас есть пользовательский UITableViewCell

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

Разработчик, пожалуйста, помогите мне .. как я могу решить эту задачу

Ответы [ 10 ]

94 голосов
/ 04 сентября 2013

Я не собираюсь здесь ничего говорить, чтобы противоречить принятому ответу, считая его совершенно правильным.Однако я собираюсь более подробно рассказать о том, как этого добиться.Если вы не хотите читать все это и больше интересуетесь воспроизведением исходного кода в рабочем проекте, я загрузил пример проекта в GitHub .

БазовыйИдея состоит в том, чтобы иметь внутри метода -tableView: heightForRowAtIndexPath: условие, определяющее, следует ли расширять текущую ячейку.Это будет вызвано вызовом начала / конца обновлений таблицы из -tableView: didSelectRowAtIndexPath:. В этом примере я покажу, как создать табличное представление, позволяющее расширять одну ячейку за раз.

Первое, что вам нужно сделать, это объявить ссылку на объект NSIndexPath .Вы можете делать это как хотите, но я рекомендую использовать объявление свойства, например:

@property (strong, nonatomic) NSIndexPath *expandedIndexPath;

ПРИМЕЧАНИЕ: Вам не нужно создавать этот путь индекса внутри viewDidLoad или любого другогоаналогичный метод.Тот факт, что индекс изначально равен нулю, будет означать только то, что в таблице изначально не будет расширенной строки.Если вы предпочитаете, чтобы таблица начиналась с расширенной строки по вашему выбору, вы можете добавить что-то вроде этого в метод viewDidLoad:

NSInteger row = 1;
NSInteger section = 2;
self.expandedIndexPath = [NSIndexPath indexPathForRow:row inSection:section];

Следующий шаг - перейти к вашему UITableViewDelegate. метод -tableView: didSelectRowAtIndexPath: для добавления логики для изменения расширенного индекса ячейки на основе выбора пользователя.Идея здесь состоит в том, чтобы проверить путь к индексу, который был только что выбран, по пути к индексу, сохраненному в переменной expandedIndexPath.Если они совпадают, то мы знаем, что пользователь пытается отменить выбор расширенной ячейки, и в этом случае мы устанавливаем переменную равной nil.В противном случае мы устанавливаем переменную expandedIndexPath для индекса, который был только что выбран.Все это делается между вызовами beginUpdates / endUpdates, чтобы позволить табличному представлению автоматически обрабатывать анимацию перехода.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView beginUpdates]; // tell the table you're about to start making changes

    // If the index path of the currently expanded cell is the same as the index that
    // has just been tapped set the expanded index to nil so that there aren't any
    // expanded cells, otherwise, set the expanded index to the index that has just
    // been selected.
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
        self.expandedIndexPath = nil;
    } else {
        self.expandedIndexPath = indexPath;
    }

    [tableView endUpdates]; // tell the table you're done making your changes
}

Затем последним шагом является другой UITableViewDelegate метод -tableView: heightForRowAtIndexPath:.Этот метод будет вызван после того, как вы запустили beginUpdates один раз для каждого пути индекса, который, по мнению таблицы, нуждается в обновлении.Здесь вы можете сравнить expandedIndexPath с индексным путем, который в настоящее время переоценивается.

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

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Compares the index path for the current cell to the index path stored in the expanded
    // index path variable. If the two match, return a height of 100 points, otherwise return
    // a height of 44 points.
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
        return 100.0; // Expanded height
    }
    return 44.0; // Normal height
}
67 голосов
/ 06 сентября 2011

Реализуйте heightForRowAtIndexPath, чтобы вычислить правильную высоту. Затем в коде для вашей кнопки заставьте таблицу переоценить высоту каждой ячейки с помощью beginUpdates plus endUpdates:

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

Изменения высоты ячеек таблицы будут автоматически рассчитываться с помощью heightForRowAtIndexPath, и изменения также будут анимированными.

Фактически, вместо кнопки в вашей ячейке, которая делает это, вы можете даже просто сделать выбор этой ячейки в didSelectRowAtIndexPath.

9 голосов
/ 26 ноября 2013

Я создал библиотеку с открытым исходным кодом для этого. Вы просто реализуете свертывание и расширяете делегаты в своем коде и voilà ! Вы также можете выполнять любые рисунки и анимации. проверить это .

enter image description here

8 голосов
/ 19 сентября 2014

Вместо использования [tableView beginUpdates] и [tableView endUpdates] я использую метод [tableView reloadRowsAtIndexPath:... withRowAnimation:...] внутри метода didSelectRowAtIndexPath.

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

8 голосов
/ 31 августа 2011

Я сделал повторно используемый компонент, который будет делать именно то, о чем вы говорите. Его довольно просто использовать, и есть демонстрационный проект.

GCRetractableSectionController на GitHub.

3 голосов
/ 29 ноября 2017

Это ответ Мика, но для Swift 4. (IndexPath заменяет NSIndexPath, который поставляется с пустым IndexPath, так как nil приведет к аварийному завершению Swift. Также вы можете сравнить два экземпляра IndexPath, используя ==)

Объявитьсвойство extendedIndexPath.

var expandedIndexPath = IndexPath()

Необязательная часть viewDidLoad.

expandedIndexPath = IndexPath(row: 1, section: 2)

Затем часть didSelectRow.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.beginUpdates()

    if indexPath == expandedIndexPath {
        expandedIndexPath = IndexPath()
    } else {
        expandedIndexPath = indexPath
    }

    tableView.endUpdates()
}

Затем часть heightForRow.

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath == expandedIndexPath {
        return 100
    }

    return 44
}
2 голосов
/ 13 мая 2013

Я использовал исходный код Gcamp и сделал свою собственную версию.

1) В методе loadView инициализируйте изменяемый массив, в котором вы будете сохранять расширенные или нераскрытые состояния ваших разделов. Крайне важно сохранять расширенные состояния в отдельном массиве, который не уничтожается при прокрутке табличного представления (например, если вы сохраните его в headerView, он будет перерисован и забудет, был он расширен или нет). В моем случае это массив _sectionStatuses.

- (void)loadView
{
     // At the beginning all sections are expanded
    _sectionStates = [NSMutableArray arrayWithCapacity:self.tableView.numberOfSections];
    for (int i = 0; i < self.tableView.numberOfSections; i++) {
        _sectionStates[i] = [NSNumber numberWithBool:YES];
    }
}

2) Создайте пользовательский заголовок для раздела с кнопкой расширения. Делегируйте действие от кнопки в вашем headerView к вашему TableViewController, используя шаблон делегирования. Вы можете найти подходящие изображения в исходном коде Gcamp.

3) Создайте действие для удаления или добавления строк. Здесь _foldersArray - моя структура, которая содержит все данные. HeaderView моего раздела - MCExpandableAccountHeaderView знает свой собственный номер раздела - я передаю его туда, когда создаю представления заголовка для каждого раздела. Очень важно перенести его в этот метод, поскольку вы должны знать, какой раздел теперь растянут или растянут.

- (void)expandClicked:(MCAccountHeaderView *)sender
{
MCExpandableAccountHeaderView *expandableAccountHeaderView = (MCExpandableAccountHeaderView*)sender;

// Finding a section, where a button was tapped
NSInteger section = expandableAccountHeaderView.section;

// Number of rows, that must be in a section when it is expanded
NSUInteger contentCount = [_foldersArray[section - 1][@"folders"] count];

// Change a saved status of a section
BOOL expanded = [_sectionStates[section] boolValue];
expanded = ! expanded;
expandableAccountHeaderView.expanded = expanded;
_sectionStates[section] = [NSNumber numberWithBool:expanded];

// Animation in a table
[self.tableView beginUpdates];

NSMutableArray* modifiedIndexPaths = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < contentCount; i++) {
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section];
    [modifiedIndexPaths addObject:indexPath];
}

if (expandableAccountHeaderView.expanded) [self.tableView insertRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade];
else [self.tableView deleteRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade];

[self.tableView endUpdates];

// Scroll to the top of current expanded section
if (expandableAccountHeaderView.expanded) [self.tableView scrollToRowAtIndexPath:INDEX_PATH(0, section) atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

4) Также важно возвращать правильное число или строки в разделе в зависимости от того, расширена она или нет.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
     BOOL expanded = [_sectionStates[section] boolValue];

     return expanded ? [_foldersArray[section - 1][@"folders"] count] : 0;   
}
0 голосов
/ 26 декабря 2018

После этой средней статьи о том, как расширить ячейки на основе нажатия кнопки и установки numbersOfLine для определенной метки, я смог выполнить анимацию, используя

tableView.beginUpdates()
tableView.performBatchUpdates({
  cell.description.numberOfLines = !expanded ? 0 : 3
}, completion: nil)
tableView.endUpdates()

Уведомление performBatchUpdates доступно только в iOS 11⬆️

0 голосов
/ 23 августа 2015

Чтобы добавить к 0x7fffffff ответ, я обнаружил, что мне нужно дополнительное условие в операторе if внутри didSelectRowAtIndexPath - таким образом:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{

   [tableView beginUpdates];

   if (self.expandedIndexPath && [indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
       self.expandedIndexPath = nil;
   } else {
       self.expandedIndexPath = indexPath;
   }

   [tableView endUpdates];

}
0 голосов
/ 19 февраля 2014
initialize iSelectedIndex = -1; and declare
UITableView *urTableView;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

return 10;    //Section count

}

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

return 3; //row count

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if(cell == nil)
{
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

}

[cell.textLabel setText:[NSString stringWithFormat:@"sec:%d,row:%d",indexPath.section,indexPath.row]];

return cell;

}


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

// adding a label with the tap gesture to the header in each section

headerLabel = [[UILabel alloc]init]; 

headerLabel.tag = section;

headerLabel.userInteractionEnabled = YES;

headerLabel.backgroundColor = [UIColor greenColor];

headerLabel.text = [NSString stringWithFormat:@"Header No.%d",section];

headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height);

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(gestureTapped:)];

[headerLabel addGestureRecognizer:tapGesture];

return headerLabel;

}

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

return 50.0; //adjust the height as you need

}

- (void)gestureTapped:(UITapGestureRecognizer *)sender{

UIView *theSuperview = self.view; // whatever view contains 

CGPoint touchPointInSuperview = [sender locationInView:theSuperview];

UIView *touchedView = [theSuperview hitTest:touchPointInSuperview withEvent:nil];

if([touchedView isKindOfClass:[UILabel class]])
{

    if (iSelectedIndex != touchedView.tag) { //if new header is selected , need to expand

        iSelectedIndex = touchedView.tag;

    }else{   // if the header is already expanded , need to collapse

        iSelectedIndex = -1;

    }

    [urTableView beginUpdates];

    [urTableView endUpdates];

}

}

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

// Show or hide cell

float height = 0.0;

if (indexPath.section == iSelectedIndex) {

    height = 44.0; // Show the cell - adjust the height as you need

}

return height;

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...