Чи можете ви анімувати зміну висоти на UITableViewCell, коли вибрано?


393

Я використовую UITableViewв додатку iPhone, і у мене є список людей, які належать до групи. Мені б хотілося, щоб коли користувач натискав на конкретну особу (вибираючи таким чином комірку), ця клітина зростає у висоту, щоб відобразити кілька елементів управління інтерфейсом для редагування властивостей цієї людини.

Чи можливо це?

Відповіді:


879

Я знайшов дійсно просте рішення цього як побічний ефект для роботи над якою UITableViewя працював .....

Зберігайте висоту комірки у змінній, яка зазвичай повідомляє початкову висоту за допомогою tableView: heightForRowAtIndexPath:, тоді, коли ви хочете анімувати зміну висоти, просто змініть значення змінної та зателефонуйте цьому ...

[tableView beginUpdates];
[tableView endUpdates];

Ви побачите, що це не робить повне перезавантаження, але достатньо для того, UITableViewщоб знати, що він повинен перемальовувати клітинки, захоплюючи нове значення висоти для комірки .... і здогадайтесь, що? АНІМАЛЮЄ зміни для вас. Солодке.

У мене в блозі є більш детальне пояснення та повні зразки коду ... Анімаційна зміна висоти комірки UITableView


6
Це геніально. Але чи можна контролювати швидкість анімації таблиці?
Джош Кахане

7
Це працює, але якщо я зробимо клітинку більшою, то скажімо від 44 до 74, а потім знову зменшую до 44, роздільна лінія діє абсолютно дивно. Чи може хтось це підтвердити?
plaetzchen

46
Це химерне рішення, але це те, що Apple рекомендує також у сесії WWDC 2010 "Оглядова таблиця". Я збираюся подати звіт про помилку щодо додавання цього до документації, оскільки я лише витратив на дослідження 2 години.
bpapa

5
Я спробував це рішення, і воно працює лише іноді, коли ви натискаєте комірку, є 50% ймовірності працювати. У когось однакова помилка? Це тому, що iOS7?
Джоан Кардона

7
Тепер це також написано в офіційній документації: You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell. developer.apple.com/library/ios/documentation/UIKit/Reference/…
Ярослав

63

Мені подобається відповідь Саймона Лі. Я насправді не пробував цей метод, але, схоже, він змінив би розмір усіх комірок у списку. Я сподівався на зміну просто осередку, який прослуховується. Я якось це робив, як Саймон, але з невеликою різницею. Це змінить зовнішній вигляд комірки при її виборі. І це оживляє. Просто ще один спосіб зробити це.

Створіть int для утримання значення для поточного вибраного індексу комірок:

int currentSelection;

Тоді:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    int row = [indexPath row];
    selectedNumber = row;
    [tableView beginUpdates];
    [tableView endUpdates];
}

Тоді:

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

    if ([indexPath row] == currentSelection) {
        return  80;
    }
    else return 40;


}

Я впевнений, що ви можете внести подібні зміни в tableView: cellForRowAtIndexPath: змінити тип комірки або навіть завантажити файл xib для комірки.

Ось так, поточний Вибір розпочнеться з 0. Вам потрібно буде внести корективи, якщо ви не хочете, щоб перша комірка списку (в індексі 0) виглядала вибраною за замовчуванням.


2
Перевірте код, доданий до моєї публікації, я зробив саме це, подвоївши висоту для комірки, коли вибрано. :)
Саймон Лі

8
"Я насправді не пробував цей метод, але схоже, що він змінив би розмір усіх комірок у списку" - тоді ви не виглядали дуже важко.
jamie

13
Поточний вибір вже зберігається у tableView.indexPathForSelectedRow.
Нік

22

Додайте властивість для відстеження вибраної комірки

@property (nonatomic) int currentSelection;

Встановіть його на значення дозорної (наприклад) viewDidLoad, щоб переконатися, що UITableViewстарти починаються у нормальному положенні

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //sentinel
    self.currentSelection = -1;
}

У heightForRowAtIndexPathви можете встановити висоту ви хочете для вибраної комірки

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    int rowHeight;
    if ([indexPath row] == self.currentSelection) {
        rowHeight = self.newCellHeight;
    } else rowHeight = 57.0f;
    return rowHeight;
}

У didSelectRowAtIndexPathвас зберегти поточний вибір і зберегти висоту динамічного, якщо потрібно

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        // do things with your cell here

        // set selection
        self.currentSelection = indexPath.row;
        // save height for full text label
        self.newCellHeight = cell.titleLbl.frame.size.height + cell.descriptionLbl.frame.size.height + 10;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

У didDeselectRowAtIndexPathвстановити індекс вибору назад до значення дозорного і анімувати спину клітин до нормальної форми

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {       
        // do things with your cell here

        // sentinel
        self.currentSelection = -1;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

Дякую, дякую, дякую! Я додав трохи коду, щоб зробити клітку перемикаючою. Я додав код нижче.
вересня 1515

14

reloadData не корисний, тому що немає анімації ...

Це те, що я зараз намагаюся:

NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

Це майже працює правильно. Майже. Я збільшую висоту комірки, і іноді в поданні таблиці є невелика «гикавка», коли комірка замінюється, ніби зберігається якесь положення прокрутки в поданні таблиці, нова клітинка (яка є першою коміркою у таблиці) закінчується його зміщення занадто високо, і прокрутка переглядає, щоб переставити його.


Особисто я виявив, що використання цього методу, але за допомогою UITableViewRowAnimationNone забезпечує більш плавний, але все ще не ідеальний результат.
Рон Сребро

11

Я не знаю, що це все про виклик послідовних викликів startUpdates / endUpdates, ви можете просто використовувати -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Ось приклад проекту .


Відмінно, це не розтягує мої текстові подання в комірці автоматичного макета. Але це мусить мати мерехтіння до анімації, тому що параметр «Анімація None» виглядає блискучим при оновленні розміру комірки.
h3dkandi

10

Я вирішив с reloadRowsAtIndexPaths.

Я зберігаю в didSelectRowAtIndexPathindexPath вибраної комірки і дзвоню reloadRowsAtIndexPathsв кінці (ви можете надіслати NSMutableArray для списку елементів, який ви хочете перезавантажити).

У heightForRowAtIndexPathви можете перевірити , якщо indexPath в списку або НЕ expandIndexPath осередку й висоти відправки.

Ви можете перевірити цей основний приклад: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam Це просте рішення.

я додаю свого роду код, якщо допоможу

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath
{
    if ([indexPath isEqual:_expandIndexPath])
        return 80;

    return 40;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Celda";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [cell.textLabel setText:@"wopwop"];

    return cell;
}

#pragma mark -
#pragma mark Tableview Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *modifiedRows = [NSMutableArray array];
    // Deselect cell
    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];
    _expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];

    // This will animate updating the row sizes
    [tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}

8

Замість beginUpdates()/ endUpdates()рекомендований дзвінок зараз:

tableView.performBatchUpdates(nil, completion: nil)

Що стосується beginUpdates / endUpdates від Apple, говорить: "Використовуйте метод performBatchUpdates (_: завершення :) замість цього, коли це можливо."

Дивіться: https://developer.apple.com/documentation/uikit/uitableview/1614908-beginupdates


3

Спробуйте це для розширення рядка по індексу:

@property (nonatomic) NSIndexPath *expandIndexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([indexPath isEqual:self.expandedIndexPath])
    return 100;

return 44;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandIndexPath]) {
    [modifiedRows addObject:self.expandIndexPath];
    self.expandIndexPath = nil;
} else {
    if (self.expandedIndexPath)
        [modifiedRows addObject:self.expandIndexPath];

    self.expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];
}

// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];

// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ViewControllerCellReuseIdentifier];
    cell.textLabel.text = [NSString stringWithFormat:@"I'm cell %ld:%ld", (long)indexPath.section, (long)indexPath.row];

return cell;
}

3
BOOL flag;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    flag = !flag;
    [tableView beginUpdates];
    [tableView reloadRowsAtIndexPaths:@[indexPath] 
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES == flag ? 20 : 40;
}

2

лише примітка для когось, як я, що шукає, додайте "Докладніші відомості" у користувацьку клітинку.

[tableView beginUpdates];
[tableView endUpdates];

Чудово справились, але не забудьте "обрізати" вигляд клітини. У програмі Interface Builder виберіть свою клітинку -> Перегляд вмісту -> у "Інспектор властивостей" виберіть " Підпогляд кліпу "


2

Ось коротка версія Сімонса відповідає на Swift 3. Також дозволяє змінювати вибір клітини

var cellIsSelected: IndexPath?


  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    cellIsSelected = cellIsSelected == indexPath ? nil : indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
  }


  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if cellIsSelected == indexPath {
      return 250
    }
    return 65
  }

2

Swift 4 і вище

додайте нижче код у свій метод делегування рядків didselect рядка tableview

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()

1

Швидка версія відповіді Саймона Лі.

// MARK: - Variables 
  var isCcBccSelected = false // To toggle Bcc.



    // MARK: UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    // Hide the Bcc Text Field , until CC gets focused in didSelectRowAtIndexPath()
    if self.cellTypes[indexPath.row] == CellType.Bcc {
        if (isCcBccSelected) {
            return 44
        } else {
            return 0
        }
    }

    return 44.0
}

Потім у didSelectRowAtIndexPath ()

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.tableView.deselectRowAtIndexPath(indexPath, animated: true)

    // To Get the Focus of CC, so that we can expand Bcc
    if self.cellTypes[indexPath.row] == CellType.Cc {

        if let cell = tableView.cellForRowAtIndexPath(indexPath) as? RecipientTableViewCell {

            if cell.tag == 1 {
                cell.recipientTypeLabel.text = "Cc:"
                cell.recipientTextField.userInteractionEnabled = true
                cell.recipientTextField.becomeFirstResponder()

                isCcBccSelected = true

                tableView.beginUpdates()
                tableView.endUpdates()
            }
        }
    }
}

1

Так, це можливо.

UITableView має метод делегата didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [UIView animateWithDuration:.6
                          delay:0
         usingSpringWithDamping:UIViewAnimationOptionBeginFromCurrentState
          initialSpringVelocity:0
                        options:UIViewAnimationOptionBeginFromCurrentState animations:^{

                            cellindex = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
                            NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
                            [violatedTableView beginUpdates];
                            [violatedTableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
                            [violatedTableView endUpdates];
                        }
                     completion:^(BOOL finished) {
    }];
}

Але у вашому випадку, якщо користувач прокручує та вибирає іншу клітинку, то вам потрібно мати останню обрану комірку, щоб зменшити та розширити вибрані в даний час reloadRowsAtIndexPaths:виклики стільників, heightForRowAtIndexPath:тому обробляйте відповідно.


0

Ось мій код спеціального UITableViewпідкласу, який розширюється UITextViewза коміркою таблиці, не перезавантажуючи (і втрачаючи фокус клавіатури):

- (void)textViewDidChange:(UITextView *)textView {
    CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
    // Check, if text height changed
    if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
        [self beginUpdates];

        // Calculate difference in height
        CGFloat difference = textHeight - self.previousTextHeight;

        // Update currently editing cell's height
        CGRect editingCellFrame = self.editingCell.frame;
        editingCellFrame.size.height += difference;
        self.editingCell.frame = editingCellFrame;

        // Update UITableView contentSize
        self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);

        // Scroll to bottom if cell is at the end of the table
        if (self.editingNoteInEndOfTable) {
            self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
        } else {
            // Update all next to editing cells
            NSInteger editingCellIndex = [self.visibleCells indexOfObject:self.editingCell];
            for (NSInteger i = editingCellIndex; i < self.visibleCells.count; i++) {
                UITableViewCell *cell = self.visibleCells[i];
                CGRect cellFrame = cell.frame;
                cellFrame.origin.y += difference;
                cell.frame = cellFrame;
            }
        }
        [self endUpdates];
    }
    self.previousTextHeight = textHeight;
}

0

Я використовував дивовижну відповідь @ Joy, і вона прекрасно працювала з ios 8.4 та XCode 7.1.1.

У випадку, якщо ви хочете зробити свою клітинку перемикаючою, я змінив -tableViewDidSelect на таке:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//This is the bit I changed, so that if tapped once on the cell, 
//cell is expanded. If tapped again on the same cell, 
//cell is collapsed. 
    if (self.currentSelection==indexPath.row) {
        self.currentSelection = -1;
    }else{
        self.currentSelection = indexPath.row;
    }
        // animate
        [tableView beginUpdates];
        [tableView endUpdates];

}

Я сподіваюся, що щось із цього вам допомогло.


0

Перевірте цей метод після iOS 7 та новіших версій.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

Це було вдосконалено в iOS 8. Ми можемо встановити його як властивість самого подання таблиці.



0

Вхідні дані -

tableView.beginUpdates () tableView.endUpdates () ці функції не будуть викликати

func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}

Але, якщо це зробити, tableView.reloadRows (за адресою: [selectedIndexPath! Як IndexPath], з: .none)

Він викличе функцію tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} ця функція.


-1

Я просто вирішив цю проблему невеликим злому:

static int s_CellHeight = 30;
static int s_CellHeightEditing = 60;

- (void)onTimer {
    cellHeight++;
    [tableView reloadData];
    if (cellHeight < s_CellHeightEditing)
        heightAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(onTimer) userInfo:nil repeats:NO] retain];
}

- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
        if (isInEdit) {
            return cellHeight;
        }
        cellHeight = s_CellHeight;
        return s_CellHeight;
}

Коли мені потрібно збільшити висоту комірки, я встановив isInEdit = YESі закликаю метод, [self onTimer]і він оживляє ріст клітини, поки не досягне значення s_CellHeightEditing :-)


У тренажері відмінно працює, але в iPhone апаратне забезпечення відстає. Із затримкою таймера 0,05 і збільшенням осередку висотою на 5 одиниць це набагато краще, але нічого подібного до CoreAnimation
Дзамір

1
Підтвердьте, перш ніж після наступного разу.
ajay_nasa

-2

Виберіть вибраний покажчик рядка. Перезавантажте таблицю. У методі heightForRowAtIndexPath UITableViewDelegate встановіть висоту вибраного рядка на іншу висоту, а для інших поверніть звичайну висоту рядка


2
-1, не працює. Виклик [table reloadData]результатів зміни висоти відбувається миттєво, а не анімація.
Марк Амері
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.