Мультимедийная анимация загрузки таблицы

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

Ответ 1

В вашей таблице делегат,

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

Поместите эту анимированную анимацию перевода снизу вверх (упрощено от ответа 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];

Для лучшего UX рекомендуется, чтобы анимация воспроизводилась только один раз для каждой строки, пока представление таблицы не будет освобождено.

Поместите приведенный выше код в

if (![self.shownIndexes containsObject:indexPath]) {
    [self.shownIndexes addObject:indexPath];

    // Your animation code here.
}

------- Swift 3/4.2 версия -------------------------------------- -------------------------------------------------- ------

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()
    }
}

Ответ 2

Swift 4

Добавьте это небольшое симпатичное расширение

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
    }
}
}

Затем вместо "tableView.reloadData()" используйте "tableView.reloadWithAnimation()"

Ответ 3

Здесь мое решение Swift 3 для отображения ячеек один за другим. что приятно, что они загружаются только при первом времени загрузки и только для первоначально отображаемых ячеек (не будут запускаться при прокрутке пользователя).

Наслаждайтесь:)

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)
    }
}

Ответ 4

Ни одно из предложенных решений не помогло мне, поэтому я нашел собственное. Вот небольшой класс общего назначения, который можно использовать для объединения анимаций и воспроизведения их один за другим. Его синтаксис аналогичен синтаксису UIView.animate() и после его вызова асинхронно ставит в очередь анимацию, а затем начинает выполнять очередь в последовательном порядке в порядке их добавления:

Swift 4.1

ChainedAnimationsQueue.swift

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
  })
}

Ответ 5

tableView:willDisplayCell:forRowAtIndexPath метод будет вызываться каждый раз, когда ячейка будет показана, и поскольку они просматриваются одновременно, это означает, что они вызываются в разных потоках, и вы не можете сказать, что SDK для iOS этот метод последовательно. Поэтому я думаю, что способ получить то, что вы хотите, - установить задержку для каждой ячейки, когда она будет показана.

-(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
    }];
}

Ответ 6

Swift 4

Я сделал быстрое расширение на UITableView для анимации ячеек:

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")
    })

Расширение выглядит так:

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
      }
    }
  }
}

Ответ 7

Это простая красивая анимация затухания, которую я в основном использовал в виде таблицы

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        cell.alpha = 0
        UIView.animate(withDuration: 1) {
            cell.alpha = 1.0
        }
    }