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


183

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

Ось код, який я спробував, але він нічого не робить:

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

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

Будь-які думки про те, як я міг це зробити?

Відповіді:


400

Насправді це дуже просто:

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

З документації :

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


13
І просто найкраща відповідь на цій сторінці!
Маттіас Д

41
Це не зовсім правильно, тому що це перезавантажить лише перший розділ. Якщо у вашій таблиці є кілька розділів, це не працюватиме
Nosrettap

4
Можливо, ви отримаєте виняток, якщо жодні дані не змінилися. Дивіться мою відповідь
Матей

8
@Nosrettap: якщо ви хочете мати більше або всі розділи свого перегляду таблиці TableView, все, що вам потрібно зробити, це розширити свій NSIndexSet на всі індекси розділів, які також потрібно
оновити

Версія Swift: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)Примітно, що вона оновлює лише розділ 0, який є першим розділом за замовчуванням.
kbpontius

264

Ви можете скористатися:

Ціль-С

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

Швидкий

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

Швидкий 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
Це корисно, якщо стан початку / кінця перегляду таблиці буде дуже різним (і було б складно обчислити розділи та рядки, щоб додати / вилучити), але ви будете робити щось менше, ніж те, що не анімоване перезавантаження.
Бен Пакард

1
Це не така приємна анімація, як перезавантаження перегляду таблиці за допомогою вбудованих методів, але на відміну від вищезгаданого методу, згаданого тут, цей працює, коли у вас є кілька розділів.
Марк Бридж

1
Вау після спроб усього іншого, це для мене виклично ідеально
Лукас Гуссен

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

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

78

Майте більше свободи за допомогою 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()

Довідка для переходу CAT

Швидкий 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.
Матей

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

@Agos хм ні, це не слід робити. Він не може скласти лише половину таблиці, оскільки QuartzCore змінює подання безпосередньо. Ви можете спробувати отримати клітинки з подання таблиці, а потім застосувати цю анімацію до кожної, хоча (але не впевнений, що це спрацює)
Matej

2
працював як шарм з 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

Прості речі, але настільки прості, щоб перейти безпосередньо до StackOverflow та отримати потрібний фрагмент, ніж док-трали. . Дякую!
Джаспер Блюз

24

Усі ці відповіді припускають, що ви використовуєте UITableView лише з 1 розділом.

Для точного вирішення ситуацій, коли у вас більше 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 = NSMakeRange (0, myTableView.numberOfSections);
Данял Айтекін

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](або що завгодно), це дозволить досягти ефекту, який ви шукаєте.


16

Я не можу коментувати основну відповідь, але швидка реалізація буде такою:

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

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

Інші анімації, доступні в документах: https://developer.apple.com/reference/uikit/uitableviewrowanimation

зникати Вставлений або видалений рядок або рядки зникають у або з подання таблиці.

праворуч Вставлений рядок або рядки ковзають праворуч; видалений рядок або рядки висуваються праворуч.

зліва Вставлений рядок або рядки просуваються зліва; видалений рядок або рядки висуваються зліва.

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

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

case none Вставлені або видалені рядки використовують анімації за замовчуванням.

середина Перегляд таблиці намагається зберегти старі та нові клітини в центрі місця, яке вони займали чи будуть займати. Доступно в iPhone 3.2.

автоматичний Перегляд таблиці вибирає відповідний для вас стиль анімації. (Введено в iOS 5.0.)


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

14

Версія 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двічі?
Панг

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метод і перевірить наявність нової комірки в цій позиції. Ви можете перезавантажити весь вид таблиці таким чином за допомогою конкретних анімацій.

Що стосується питання OP - потрібен умовний прапор, щоб показати клітинки для альтернативного стану перегляду таблиці.

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