【发布时间】:2016-01-29 08:57:46
【问题描述】:
我需要为表格视图行的加载设置动画。当表重新加载数据时,我需要一行接一个地从左侧进入。我怎样才能做到这一点?
【问题讨论】:
标签: ios uitableview animation cell
我需要为表格视图行的加载设置动画。当表重新加载数据时,我需要一行接一个地从左侧进入。我怎样才能做到这一点?
【问题讨论】:
标签: ios uitableview animation cell
斯威夫特 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()”
【讨论】:
这是我的 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)
}
}
【讨论】:
在您的 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.
}
------- 斯威夫特 ------------------------------------ -------------------------------------------------- ---------------------------------------
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()
}
}
【讨论】:
提供的解决方案都没有帮助我,所以我想出了自己的解决方案。这是一个通用的小类,可用于将动画链接在一起并一个接一个地播放它们。它的语法类似于 UIView.animate() 的语法,一旦被调用,就会将动画异步排队,然后按照添加顺序按顺序开始执行队列:
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
我在 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
}
}
}
}
【讨论】:
这是一个简单漂亮的淡入淡出动画,我主要用在我的 tableview 中
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.alpha = 0
UIView.animate(withDuration: 1) {
cell.alpha = 1.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
}];
}
【讨论】: