Имейте reloadData для анимации UITableView при изменении


186

У меня есть UITableView с двумя режимами. Когда мы переключаемся между режимами, у меня разное количество секций и ячеек на секцию. В идеале, он делал бы классную анимацию, когда таблица растет или сжимается.

Вот код, который я пробовал, но он ничего не делает:

CGContextRef context = UIGraphicsGetCurrentContext(); 
[UIView beginAnimations:nil context:context]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 
[UIView setAnimationDuration:0.5]; 

[self.tableView reloadData];
[UIView commitAnimations];

Есть мысли о том, как я мог это сделать?

Ответы:


402

На самом деле все очень просто:

[_tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

Из документации :

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


13
И просто лучший ответ на этой странице!
Matthias D

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

4
Возможно, вы получите исключение, если никакие данные не изменились. См. Мой ответ
Матей

8
@Nosrettap: если вы хотите иметь больше или все разделы перезагрузки tableView, все, что вам нужно сделать, это расширить свой NSIndexSet со всеми индексами разделов, которые также необходимо
обновить

Версия Swift: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)примечательно, что она обновляет только Раздел 0, который является первым разделом по умолчанию.
kbpontius 03

272

Вы можете использовать:

Цель-C

[UIView transitionWithView: self.tableView
                  duration: 0.35f
                   options: UIViewAnimationOptionTransitionCrossDissolve
                animations: ^(void)
 {
      [self.tableView reloadData];
 }
                completion: nil];

Swift

UIView.transitionWithView(tableView,
                          duration: 0.35,
                          options: .TransitionCrossDissolve,
                          animations:
{ () -> Void in
    self.tableView.reloadData()
},
                          completion: nil);

Swift 3, 4 и 5

UIView.transition(with: tableView,
                  duration: 0.35,
                  options: .transitionCrossDissolve,
                  animations: { self.tableView.reloadData() }) // left out the unnecessary syntax in the completion block and the optional completion parameter

Никаких хлопот. : D

Вы также можете использовать любой из них UIViewAnimationOptionTransitionsдля более крутых эффектов:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom

2
Это полезно, если начальное / конечное состояние вашего tableview будет сильно отличаться (и было бы сложно вычислить разделы и строки для добавления / удаления), но вы хотите что-то менее резкое, чем неанимированная перезагрузка.
Ben Packard

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

1
Ничего себе, после того, как я попробовал все остальное, это явно идеально для меня
Лукас Гуссен

1
@MarkBridges, ответ с более высоким рейтингом действительно работает с несколькими разделами :) -[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
lobianco

2
@Anthony Это не сработает, если в конечном состоянии больше / меньше секций, чем в начальном. Тогда вам придется вручную отслеживать, какие разделы были добавлены / удалены, что довольно сложно.
nikolovski 08

79

Получите больше свободы, используя CATransitionкласс.

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


Например:

(не забудьте импортировать QuartzCore)

CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.fillMode = kCAFillModeForwards;
transition.duration = 0.5;
transition.subtype = kCATransitionFromBottom;

[[self.tableView layer] addAnimation:transition forKey:@"UITableViewReloadDataAnimationKey"];

Измените в typeсоответствии с вашими потребностями и kCATransitionFadeт. Д.

Реализация в Swift:

let transition = CATransition()
transition.type = kCATransitionPush
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.fillMode = kCAFillModeForwards
transition.duration = 0.5
transition.subtype = kCATransitionFromTop
self.tableView.layer.addAnimation(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()

Ссылка на CATransition

Swift 5:

let transition = CATransition()
transition.type = CATransitionType.push
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.fillMode = CAMediaTimingFillMode.forwards
transition.duration = 0.5
transition.subtype = CATransitionSubtype.fromTop
self.tableView.layer.add(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()

это оживляет таблицу в целом, а не отдельные строки / разделы.
Agos

@Agos Он все еще отвечает на вопрос. На вопрос «Как перезагрузить только одну строку» есть совершенно другой ответ, например, применение анимации к layerсвойству UITableViewCell.
Matej

@matejkramny Я ожидал, что таблица будет анимировать только разные строки (как указано в вопросе), но этот метод подталкивает всю таблицу сразу. Может я что-то упускаю?
Agos

@Agos хм, нет, он должен это делать. Он не может обрабатывать только половину таблицы, поскольку QuartzCore напрямую изменяет представление. Вы можете попробовать получить ячейки из табличного представления, а затем применить эту анимацию к каждой (но не уверен, что это сработает)
Матей

3
работал как шарм с kCATransitionFade Спасибо! :)
quarezz

60

Я считаю, что вы можете просто обновить свою структуру данных, а затем:

[tableView beginUpdates];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView endUpdates];

Кроме того, withRowAnimation - это не совсем логическое значение, а стиль анимации:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle

1
Простые вещи, но так легко перейти прямо в StackOverflow и получить нужный фрагмент, чем трал документы. . Благодарность!
Jasper Blues

24

Все эти ответы предполагают, что вы используете UITableView только с 1 разделом.

Чтобы точно обрабатывать ситуации, когда у вас более одного раздела, используйте:

NSRange range = NSMakeRange(0, myTableView.numberOfSections);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
[myTableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];

(Примечание: убедитесь, что у вас больше 0 разделов!)

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

int sectionNumber = 0; //Note that your section may be different

int nextIndex = [currentItems count]; //starting index of newly added items

[myTableView beginUpdates];

for (NSObject *item in itemsToAdd) {
    //Add the item to the data source
    [currentItems addObject:item];

    //Add the item to the table view
    NSIndexPath *path = [NSIndexPath indexPathForRow:nextIndex++ inSection:sectionNumber];
    [myTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationAutomatic];
}

[myTableView endUpdates];

3
Хороший замечание по поводу расчетов раздела, но у меня был только один раздел, и в вашем коде была ошибка с точностью до одной. Мне пришлось изменить первую строку вашего кода на NSRange range = NSMakeRange (0, myTableView.numberOfSections);
Danyal Aytekin

18

Чтобы приблизиться к этому, нужно сказать tableView удалить и добавить строки и разделы с

insertRowsAtIndexPaths:withRowAnimation:,
deleteRowsAtIndexPaths:withRowAnimation:,
insertSections:withRowAnimation:И
deleteSections:withRowAnimation:

методы UITableView.

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

Итак, поток вашего приложения будет следующим:

[self setTableIsInSecondState:YES];

[myTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES]];

Пока методы dataSource вашей таблицы возвращают правильный новый набор разделов и строк, проверяя [self tableIsInSecondState](или что-то еще), это приведет к желаемому эффекту.


17

Я не могу прокомментировать главный ответ, но быстрая реализация была бы такой:

self.tableView.reloadSections([0], with: UITableViewRowAnimation.fade)

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

Другие анимации доступны в документации: https://developer.apple.com/reference/uikit/uitableviewrowanimation

fade Вставленная или удаленная строка или строки переходят в табличное представление или исчезают из него.

вправо Вставленная строка или строки вставляются справа; удаленная строка или строки сдвинутся вправо.

слева Вставленная строка или строки вставляются слева; удаленная строка или строки сдвинутся влево.

вверх Вставленная строка или строки вставляются сверху; удаленная строка или строки выдвигаются вверх.

снизу Вставленная строка или строки сдвигаются снизу; удаленная строка или строки выдвигаются вниз.

case none Для вставленных или удаленных строк используется анимация по умолчанию.

middle В табличном представлении стараются удерживать старые и новые ячейки по центру в пространстве, которое они занимали или будут занимать. Доступно в iPhone 3.2.

автоматически В виде таблицы выбирается подходящий стиль анимации. (Представлено в iOS 5.0.)


1
Для Swift 4.2: self.tableView.reloadSections ([0], with: UITableView.RowAnimation.fade)
Reefwing

15

Версия Swift 4 для ответа @dmarnel:

tableView.reloadSections(IndexSet(integer: 0), with: .automatic)

9

Быстрая реализация:

let range = NSMakeRange(0, self.tableView!.numberOfSections())
let indexSet = NSIndexSet(indexesInRange: range)
self.tableView!.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.Automatic)


1

В моем случае я хотел добавить еще 10 строк в табличное представление (для функциональности типа «показать больше результатов») и сделал следующее:

  NSInteger tempNumber = self.numberOfRows;
  self.numberOfRows += 10;
  NSMutableArray *arrayOfIndexPaths = [[NSMutableArray alloc] init];
  for (NSInteger i = tempNumber; i < self.numberOfRows; i++) {
    [arrayOfIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
  }
  [self.tableView beginUpdates];
  [self.tableView insertRowsAtIndexPaths:arrayOfIndexPaths withRowAnimation:UITableViewRowAnimationTop];
  [self.tableView endUpdates];

В большинстве случаев вместо self.numberOfRows вы обычно используете счетчик массива объектов для представления таблицы. Итак, чтобы убедиться, что это решение хорошо работает для вас, «arrayOfIndexPaths» должен быть точным массивом путей индекса вставляемых строк. Если строка существует для любого из этих путей индекса, код может дать сбой, поэтому вы должны использовать метод reloadRowsAtIndexPaths: withRowAnimation: для этих путей индекса, чтобы избежать сбоя


1

Если вы хотите добавить свои собственные анимации в ячейки UITableView, используйте

[theTableView reloadData];
[theTableView layoutSubviews];
NSArray* visibleViews = [theTableView visibleCells];

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

Ознакомьтесь с этой публикацией, чтобы узнать о плавной пользовательской анимации ячеек. https://gist.github.com/floprr/1b7a58e4a18449d962bd


1

Анимировать без reloadData () в Swift можно так ( начиная с версии 2.2):

tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
    // ...or here, if you need to add/remove the same amount of objects to/from somewhere
    indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
    numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()

если вам нужно вызвать reloadData () после завершения анимации, вы можете принять изменения в CATransaction следующим образом:

CATransaction.begin()
CATransaction.setCompletionBlock({() in self.tableview.reloadData() })
tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove.count ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
     // ...or here, if you need to add/remove the same amount of objects to/from somewhere
     indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
     numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()
CATransaction.commit()

Логика показана для случая, когда вы удаляете строки, но та же идея работает и для добавления строк. Вы также можете изменить анимацию на UITableViewRowAnimation.Left, чтобы сделать ее аккуратной, или выбрать из списка других доступных анимаций.


1
CATransition *animation = [CATransition animation];
animation.duration = .3;
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setDuration:.3];

[[_elementTableView layer] addAnimation:animation forKey:@"UITableViewReloadDataAnimationKey"];

[tableView reloadData];

2
Зачем устанавливать продолжительность .3дважды?
Pang

1

Чтобы перезагрузить все разделы , а не только один с настраиваемой продолжительностью .

Пользовательский durationпараметр UIView.animateдля установки произвольной продолжительности.

UIView.animate(withDuration: 0.4, animations: { [weak self] in
    guard let `self` = self else { return }
    let indexSet = IndexSet(integersIn: 0..<self.tableView.numberOfSections)
    self.tableView.reloadSections(indexSet, with: UITableView.RowAnimation.fade)
})

0

Родная UITableViewанимация в Swift

Вставляйте и удаляйте все строки одновременно, tableView.performBatchUpdatesчтобы они происходили одновременно. Основываясь на ответе @iKenndac, используйте такие методы, как:

  • tableView.insertSections
  • tableView.insertRows
  • tableView.deleteSections
  • tableView.deleteRows

Пример:

 tableView.performBatchUpdates({
   tableView.insertSections([0], with: .top)
 })

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

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

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