J'ai besoin d'animer la charge des lignes de la table. Lorsque la table recharge les données, j'ai besoin que les lignes entrent par la gauche l'une après l'autre. Comment puis-je atteindre cet objectif?
Dans votre délégué tableview,
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Mettez cette animation de traduction en fondu vers le bas (simplifiée à partir de la réponse Anbu.Karthik),
//1. Define the initial state (Before the animation)
cell.transform = CGAffineTransformMakeTranslation(0.f, CELL_HEIGHT);
cell.layer.shadowColor = [[UIColor blackColor]CGColor];
cell.layer.shadowOffset = CGSizeMake(10, 10);
cell.alpha = 0;
//2. Define the final state (After the animation) and commit the animation
[UIView beginAnimations:@"rotation" context:NULL];
[UIView setAnimationDuration:0.5];
cell.transform = CGAffineTransformMakeTranslation(0.f, 0);
cell.alpha = 1;
cell.layer.shadowOffset = CGSizeMake(0, 0);
[UIView commitAnimations];
Pour une meilleure UX, il est conseillé de ne lire l'animation que ne fois pour chaque ligne, jusqu'à ce que la vue de table soit désallouée.
Mettez le code ci-dessus dans
if (![self.shownIndexes containsObject:indexPath]) {
[self.shownIndexes addObject:indexPath];
// Your animation code here.
}
------- Swift ----------------------------- -------------------------------------------------- ----------------------------------
var shownIndexes : [IndexPath] = []
let CELL_HEIGHT : CGFloat = 40
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if (shownIndexes.contains(indexPath) == false) {
shownIndexes.append(indexPath)
cell.transform = CGAffineTransform(translationX: 0, y: CELL_HEIGHT)
cell.layer.shadowColor = UIColor.black.cgColor
cell.layer.shadowOffset = CGSize(width: 10, height: 10)
cell.alpha = 0
UIView.beginAnimations("rotation", context: nil)
UIView.setAnimationDuration(0.5)
cell.transform = CGAffineTransform(translationX: 0, y: 0)
cell.alpha = 1
cell.layer.shadowOffset = CGSize(width: 0, height: 0)
UIView.commitAnimations()
}
}
Swift 4
Ajoutez cette petite extension mignonne
extension UITableView {
func reloadWithAnimation() {
self.reloadData()
let tableViewHeight = self.bounds.size.height
let cells = self.visibleCells
var delayCounter = 0
for cell in cells {
cell.transform = CGAffineTransform(translationX: 0, y: tableViewHeight)
}
for cell in cells {
UIView.animate(withDuration: 1.6, delay: 0.08 * Double(delayCounter),usingSpringWithDamping: 0.6, initialSpringVelocity: 0, options: .curveEaseInOut, animations: {
cell.transform = CGAffineTransform.identity
}, completion: nil)
delayCounter += 1
}
}
}
Ensuite, au lieu de "tableView.reloadData ()", utilisez "tableView.reloadWithAnimation ()"
Voici ma solution Swift 3 pour afficher les cellules une par une. Ce qui est bien, c'est qu'elles ne se chargent qu'au premier chargement et uniquement pour les cellules initialement affichées (ne fonctionneront pas lorsque l'utilisateur défile vers le bas).
Prendre plaisir :)
private var finishedLoadingInitialTableCells = false
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
var lastInitialDisplayableCell = false
//change flag as soon as last displayable cell is being loaded (which will mean table has initially loaded)
if yourTableData.count > 0 && !finishedLoadingInitialTableCells {
if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows,
let lastIndexPath = indexPathsForVisibleRows.last, lastIndexPath.row == indexPath.row {
lastInitialDisplayableCell = true
}
}
if !finishedLoadingInitialTableCells {
if lastInitialDisplayableCell {
finishedLoadingInitialTableCells = true
}
//animates the cell as it is being displayed for the first time
cell.transform = CGAffineTransform(translationX: 0, y: self.rowHeight/2)
cell.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0.05*Double(indexPath.row), options: [.curveEaseInOut], animations: {
cell.transform = CGAffineTransform(translationX: 0, y: 0)
cell.alpha = 1
}, completion: nil)
}
}
Aucune des solutions fournies ne m'a aidé, alors j'ai trouvé la mienne. Voici une petite classe à usage général qui peut être utilisée pour enchaîner des animations et les jouer l'une après l'autre. Sa syntaxe est similaire à celle de UIView.animate () et une fois appelée, met en file d'attente de manière asynchrone l'animation, puis commence à exécuter la file d'attente de manière séquentielle dans l'ordre où elles ont été ajoutées:
import UIKit
import Foundation
class ChainedAnimationsQueue {
private var playing = false
private var animations = [(TimeInterval, () -> Void, () -> Void)]()
init() {
}
/// Queue the animated changes to one or more views using the specified duration and an initialization block.
///
/// - Parameters:
/// - duration: The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them.
/// - initializations: A block object containing the changes to commit to the views to set their initial state. This block takes no parameters and has no return value. This parameter must not be NULL.
/// - animations: A block object containing the changes to commit to the views. This is where you programmatically change any animatable properties of the views in your view hierarchy. This block takes no parameters and has no return value. This parameter must not be NULL.
func queue(withDuration duration: TimeInterval, initializations: @escaping () -> Void, animations: @escaping () -> Void) {
self.animations.append((duration, initializations, animations))
if !playing {
playing = true
DispatchQueue.main.async {
self.next()
}
}
}
private func next() {
if animations.count > 0 {
let animation = animations.removeFirst()
animation.1()
UIView.animate(withDuration: animation.0, animations: animation.2, completion: { finished in
self.next()
})
} else {
playing = false
}
}
}
var animationsQueue = ChainedAnimationsQueue()
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.alpha = 0.0
animationsQueue.queue(withDuration: 0.2, initializations: {
cell.layer.transform = CATransform3DTranslate(CATransform3DIdentity, cell.frame.size.width, 0, 0)
}, animations: {
cell.alpha = 1.0
cell.layer.transform = CATransform3DIdentity
})
}
C'est une belle animation de fondu simple que j'ai surtout utilisée dans ma table
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.alpha = 0
UIView.animate(withDuration: 1) {
cell.alpha = 1.0
}
}
Swift 4
J'ai fait une extension rapide sur UITableView pour animer les cellules:
tableView.reloadData() // To make sure tableView.visibleCells is not empty
tableView.animateCells(
cells: tableView.visibleCells,
duration: 0.3,
delay: 0.5,
dampingRatio: 0.8,
configure: { cell -> (prepare: () -> Void, animate: () -> Void)? in
guard let customCell = cell as? CustomCell else { return nil }
let preparations = {
customCell.iconImageView.alpha = 0
}
let animations = {
customCell.iconImageView.alpha = 1
}
return (preparations, animations)
}, completion: {
print("Cell animations are completed")
})
L'extension ressemble à ceci:
extension UITableView {
func animateCells<Cell: UITableViewCell>(cells: [Cell],
duration: TimeInterval,
delay: TimeInterval = 0,
dampingRatio: CGFloat = 0,
configure: @escaping (Cell) -> (prepare: () -> Void, animate: () -> Void)?,
completion: @escaping () -> Void) {
var cellDelay: TimeInterval = 0
var completionCount: Int = 0
for cell in cells {
if let callbacks = configure(cell) {
callbacks.prepare()
let animator = UIViewPropertyAnimator(duration: duration, dampingRatio: dampingRatio)
animator.addAnimations(callbacks.animate)
let completionTime = cellDelay + (duration * TimeInterval(dampingRatio))
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + completionTime) {
completionCount += 1
if completionCount == cells.count {
completion()
}
}
animator.startAnimation(afterDelay: cellDelay)
cellDelay += delay
} else {
completionCount += 1
}
}
}
}
tableView:willDisplayCell:forRowAtIndexPath
la méthode sera appelée chaque fois qu'une cellule sera affichée, et puisqu'elles sont affichées en même temps, cela signifie qu'elles sont appelées dans différents threads et vous ne pouvez pas dire à iOS SDK d'appeler cette méthode séquentiellement . Je pense donc que la façon d'obtenir ce que vous voulez est de définir un délai pour chaque cellule lorsqu'elle est affichée.
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
CGFloat delay = indexPath.row * yourSupposedAnimationDuration;
[UIView animateWithDuration:yourSupposedAnimationDuration delay:delay options:UIViewAnimationOptionCurveEaseIn animations:^{
//Your animation code
}completion:^(BOOL finished) {
//Your completion Code
}];
}