Widok tabeli animacja ładowania komórek jeden po drugim

Muszę animować Ładowanie wierszy widoku tabeli. Kiedy tabela przeładowuje dane potrzebuję wiersze dostać się z lewej jeden po drugim. Jak mogę to osiągnąć?

Author: salo.dm, 2015-10-29

7 answers

W delegacie tableview,

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

Umieść tę animację tłumaczenia od dołu do góry (uproszczoną z Anbu.Karthik odpowiedz),

    //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];

Dla lepszego UX, zaleca się, aby animacja była odtwarzana tylko raz dla każdego wiersza, dopóki widok tabeli nie zostanie zmniejszony.

Wstaw powyższy kod w

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()
    }
}
 27
Author: felixwcf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-30 03:28:05

Swift 4

Add this little cute extension

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

Wtedy zamiast " tableView.reloadData ()", użyj " tableView.reloadWithAnimation () "

 34
Author: Ahmadreza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-28 14:01:14

Oto moje rozwiązanie Swift 3 do wyświetlania komórek jeden po drugim. co jest miłe w tym, że ładują się tylko przy pierwszym ładowaniu i tylko dla początkowo wyświetlanych komórek (nie będą działać, gdy użytkownik przewija się w dół).

Enjoy:)

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)
    }
}
 29
Author: chikko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-25 09:02:10

Żadne z dostarczonych rozwiązań mi nie pomogło, więc wymyśliłem własne. Oto mała klasa ogólnego przeznaczenia, którą można wykorzystać do łączenia animacji i odtwarzania ich jedna po drugiej. Jego składnia jest podobna do składni UIView.animate() i po wywołaniu, asynchronicznie kolejkuje animację, a następnie rozpoczyna wykonywanie kolejki sekwencyjnie w kolejności, w jakiej została dodana:

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

Przykładowe Użycie:

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
  })
}
 2
Author: Omid Ariyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 16:28:36

Swift 4

Zrobiłem szybkie rozszerzenie na UITableView do animowania komórek:

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

Rozszerzenie wygląda tak:

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
      }
    }
  }
}
 2
Author: billgert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-05 16:19:09

Jest to prosta, piękna animacja fade, którą najczęściej używałem w moim tableview

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        cell.alpha = 0
        UIView.animate(withDuration: 1) {
            cell.alpha = 1.0
        }
    }
 1
Author: IMRAN RASHEED,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-17 21:43:17

tableView:willDisplayCell:forRowAtIndexPath metoda będzie wywoływana za każdym razem, gdy komórka zostanie pokazana, a ponieważ są one wyświetlane w tym samym czasie, oznacza to, że są wywoływane w różnych wątkach i nie możesz powiedzieć iOS SDK, aby wywoływał tę metodę sekwencyjnie. Więc myślę, że sposobem na uzyskanie tego, czego chcesz, jest ustawienie opóźnienia dla każdej komórki, gdy jest pokazywana.

-(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
    }];
}
 0
Author: Mehdi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-02 06:50:43