我对使用progressView(使用此子类:Linear Progress View)设置动画有点困惑。
只是一点点背景,所以下面发布的代码是有道理的。我创建了一个UICollectionViewCell
的子类,其中包含一些基本功能。主要原因是减少样板代码。在我的集合视图中,我将单元格设置为我的子类并将其传递给模型对象。所有数据都正确显示,但进度视图没有动画效果。我在集合视图上尝试了viewWillDisplay
之类的东西,但无济于事。
任何建议都将不胜感激。 (以下代码)
CollectionViewCell类:
import UIKit
import ChameleonFramework
import Material
class MacrocycleCell: Cell {
var macrocycle:Macrocycle? = nil {
didSet{
if let macro = macrocycle, let start = macrocycle?.start, let completion = macrocycle?.completion {
title.text = macro.title
let percentage = Date().calculatePercentageComplete(startDate: start, completionDate: completion)
progress.setProgress(percentage, animated: true)
let difference = Date.calculateDifferenceInMonthsAndDaysBetween(start: start, end: completion)
if let monthDiff = difference.month, let dayDiff = difference.day {
if monthDiff > 0 {
detail.text = monthDiff > 1 ? "\(monthDiff) months left" : "\(monthDiff) month left"
}else{
detail.text = dayDiff > 1 ? "\(dayDiff) days left" : "\(dayDiff) day left"
}
}
}
}
}
let dropView:View = {
let view = View()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
view.depthPreset = DepthPreset.depth2
return view
}()
let title:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 18)
label.textColor = UIColor.flatBlack()
return label
}()
let progress:LinearProgressView = {
let progress = LinearProgressView()
progress.isCornersRounded = true
progress.barColor = UIColor.flatWhiteColorDark()
progress.trackColor = UIColor.flatGreen()
progress.barInset = 0
progress.minimumValue = 0
progress.maximumValue = 1
progress.animationDuration = 1
progress.translatesAutoresizingMaskIntoConstraints = false
return progress
}()
let detail:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 14, weight: UIFont.Weight.light)
label.textColor = UIColor.flatGray()
return label
}()
override func drawView() {
addSubview(dropView)
dropView.addSubview(title)
dropView.addSubview(progress)
dropView.addSubview(detail)
dropView.topAnchor.constraint(equalTo: topAnchor).isActive = true
dropView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
dropView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
dropView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -2).isActive = true
progress.centerXAnchor.constraint(equalTo: dropView.centerXAnchor).isActive = true
progress.centerYAnchor.constraint(equalTo: dropView.centerYAnchor).isActive = true
progress.heightAnchor.constraint(equalToConstant: 10).isActive = true
progress.widthAnchor.constraint(equalTo: dropView.widthAnchor, multiplier: 0.8).isActive = true
title.topAnchor.constraint(equalTo: dropView.topAnchor).isActive = true
title.leftAnchor.constraint(equalTo: dropView.safeAreaLayoutGuide.leftAnchor).isActive = true
title.rightAnchor.constraint(equalTo: dropView.safeAreaLayoutGuide.rightAnchor).isActive = true
title.bottomAnchor.constraint(equalTo: progress.topAnchor, constant: 6).isActive = true
detail.topAnchor.constraint(equalTo: progress.bottomAnchor, constant: 14).isActive = true
detail.leftAnchor.constraint(equalTo: dropView.leftAnchor).isActive = true
detail.rightAnchor.constraint(equalTo: dropView.rightAnchor).isActive = true
detail.heightAnchor.constraint(equalToConstant: 14).isActive = true
}
}
CollectionViewController类:
import UIKit
private let reuseIdentifier = "Cell"
class MacrocycleController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
var athlete:Athlete? = nil {
didSet{
self.title = athlete?.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .flatWhiteColorDark()
self.collectionView!.register(MacrocycleCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 90)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return athlete?.macrocycles?.count ?? 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MacrocycleCell
cell.macrocycle = athlete?.macrocycles?[indexPath.item]
return cell
}
}
我确信这很简单,我忘了实施。
谢谢,你这个美丽的人!
答案 0 :(得分:1)
您看不到任何动画,因为没有动画可见的更改。具体来说,您在显示单元格之前从didSet调用setProgress,然后再也不再调用它。
要查看动画,您必须允许视图显示一些初始值(0%),然后再次调用setProgress,将更改设置为真值。
从UI的角度来看,您尝试做的事情并不是一个好主意 - 原则上,只有在出现一些实际有意义的进展(用户操作,下载等)时,进度条才会移动。因此,我对你的问题的回答是在这种情况下使用没有动画的进度条。
话虽如此,有些合法的情况可能需要更新单元格中的进度条而无需重新加载单元格。 例如,如果您运行了一些后台任务,则会定期报告与每个单元格相关的进度。有大量的答案证明了进行这种更新的不同方法。
此外,还有一些建议用于触发在显示单元格后立即发生更新的黑客攻击。搜索不存在的问题" DidDisplayCell"委托方法,如果你想看到一些这些想法。你绝对可以在单元格显示后制作动画,但我想引导你远离诱惑,将动画用于人工目的。
这里有一些代码片段来说明如何使用来自WillDisplay的asyncAfter(我用tableview测试过这个):
// This would be in your cell class
var progress: Float = 0.0
@IBOutlet var progressBar: LinearProgressView!
func animateIfNeeded() {
if progressBar.progress != self.progress {
progressBar.setProgress(progress, animated: true)
}
}
// This would be in your delegate (I tested with tableview)
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
let animateCell = cell as! Cell
animateCell.animateIfNeeded()
}
}
显然,在didSet中,您只需将计算值保存到实例变量(progress:Float)中,并调用progressBar.setProgress(0,animated:false)。
答案 1 :(得分:0)
这是另一个答案,它将所有逻辑保留在单元类中,并且不使用集合控制器的WillDisplayCell委托方法。
var progress: Float = 0.0
@IBOutlet var progressBar: LinearProgressView!
var drawnAtLeastOnce = false
func animateIfNeeded() {
if progressBar.progress != self.progress {
progressBar.setProgress(progress, animated: true)
}
}
override func layoutSubviews() {
super.layoutSubviews()
if drawnAtLeastOnce {
animateIfNeeded()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
if !drawnAtLeastOnce {
drawnAtLeastOnce = true
setNeedsLayout()
}
}
这个答案的关键是检测第一个绘图过程的结束并在此之前抑制动画。
这适用于第一批屏幕的细胞。滚动到视图中的单元格可能已预先绘制,因此您无法在这些单元格上看到动画。 (至少那是我用tableview测试时观察到的)。