表视图单元格加载动画一个接一个

时间:2015-10-29 09:34:26

标签: ios uitableview animation cell

我需要设置表视图行的负载动画。当表重新加载数据时,我需要一行接一行地从左边进入。我怎样才能做到这一点?

7 个答案:

答案 0 :(得分:24)

在你的tableview委托中,

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

为了获得更好的用户体验,建议动画只能为每一行一次播放,直到表视图被解除分类为止。

将上述代码放入

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

答案 1 :(得分:23)

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.reloadWithAnimation()"

而不是" tableView.reloadData()"

答案 2 :(得分:20)

这是我的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)
    }
}

答案 3 :(得分:2)

所提供的解决方案都没有帮助我,所以我提出了自己的解决方案。这是一个通用类,可用于将动画链接在一起并一个接一个地播放。它的语法类似于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
  })
}

答案 4 :(得分:0)

每次要显示一个单元格时都会调用

tableView:willDisplayCell:forRowAtIndexPath方法,并且由于它们同时被查看,这意味着它们在不同的线程中被调用而你无法告诉iOS SDK按顺序调用此方法。因此,我认为获得所需内容的方法是在显示每个单元格时设置延迟。

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

答案 5 :(得分:0)

快捷键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
      }
    }
  }
}

答案 6 :(得分:0)

这是简单美观的淡入淡出动画,我主要在桌面视图中使用

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