Подтвердить что ты не робот

UITableViewCell расширяется по клику

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Я не собираюсь ничего говорить, чтобы противоречить принятому ответу, учитывая, что это совершенно правильно. Однако я расскажу подробнее о том, как это сделать. Если вы не хотите читать все это и больше интересуетесь игрой с исходным кодом в рабочем проекте, я загрузил проект в 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
}

Ответ 3

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

enter image description here

Ответ 4

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

GCRetractableSectionController на GitHub.

Ответ 5

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

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

Ответ 6

Я использовал исходный код 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;   
}

Ответ 7

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;

}

Ответ 8

Чтобы добавить в ответ 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];

}

Ответ 9

Это ответ Mick, но для Swift 4. (IndexPath заменяет NSIndexPath, который поставляется с пустым IndexPath, поскольку nil приведет к сбою Swift. Кроме того, метод сравнения теперь отличается. Вы больше не можете использовать NSOrderedSame)

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

var expandedIndexPath = IndexPath()

Дополнительная часть viewDidLoad.

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

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

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

    if indexPath.compare(expandedIndexPath) == .orderedSame {
        expandedIndexPath = IndexPath()
    } else {
        expandedIndexPath = indexPath
    }

    tableView.endUpdates()
}

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

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

    return 44
}