Swift 中 headerView 内的动画
Animation within headerView in Swift
我目前正在尝试为用户的经验条添加动画。
如果我在 collectionViewController 中有动画,动画就会很好地加载(一次)。但是,如果我在 headerView 中有动画(因为我想在个人资料图片上添加栏),栏会多次启动:
这是我的代码 (headerViewCell):
let shapeLayerXp = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
showUserXp()
self.animateXp(toValue: 1)
}
func showUserXp() {
let center = profileImage.center
let circularPath = UIBezierPath(arcCenter: center, radius: 40, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
shapeLayerXp.path = circularPath.cgPath
let color = UIColor(red: 122 / 255, green: 205 / 255, blue: 186 / 255, alpha: 1)
shapeLayerXp.strokeColor = color.cgColor
shapeLayerXp.lineWidth = 4
shapeLayerXp.fillColor = UIColor.clear.cgColor
shapeLayerXp.lineCap = kCALineCapRound
shapeLayerXp.strokeEnd = 0
self.contentView.layer.addSublayer(shapeLayerXp)
}
func animateXp(toValue: Int) {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = toValue
basicAnimation.duration = 2
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayerXp.add(basicAnimation, forKey: "urSoBasic")
}
headerViewCell 是这样启动的:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerViewCell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
// ....
return cell
}
假设控制器中只有一个 header 单元格,除非数据发生变化(例如,不同的用户),那么您可以在视图控制器上设置一个 属性 来指示是否已显示动画。
像这样的事情会做:
var animatedHeader = false // Starts false because we want an animation the first time.
然后在第一次获取header单元格时,你可以决定是否触发动画:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerViewCell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
if !self.animatedHeader {
cell.showUserXp()
cell.animateXp()
self.animatedHeader = true
}
return cell
}
showUserXp()
和 animateXp()
方法当然需要 public。
使用此方法,header 单元格只会在第一次出列并因此显示时设置动画。
如果您确实想再次为其设置动画,您只需重置 animateHeader
属性 并重新加载 collection 视图(或只是 header)。
如果有多个header,那么您将需要分别跟踪每一个。
编辑: 由于 showXP 和 animateXP 函数的定义方式,这确实需要(意外地)使用相同的单元格。如果我自己这样做,我可能会使用更像这样的方法:
func showUserXp(animated: Bool) {
let center = profileImage.center
let circularPath = UIBezierPath(arcCenter: center, radius: 40, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
shapeLayerXp.path = circularPath.cgPath
let color = UIColor(red: 122 / 255, green: 205 / 255, blue: 186 / 255, alpha: 1)
shapeLayerXp.strokeColor = color.cgColor
shapeLayerXp.lineWidth = 4
shapeLayerXp.fillColor = UIColor.clear.cgColor
shapeLayerXp.lineCap = kCALineCapRound
shapeLayerXp.strokeEnd = 0
self.contentView.layer.addSublayer(shapeLayerXp)
if animated {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = toValue
basicAnimation.duration = 2
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayerXp.add(basicAnimation, forKey: "urSoBasic")
} else {
shapeLayerXp.strokeEnd = 1
}
}
然后你会像这样使用它:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerViewCell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
if self.animatedHeader {
cell.showUserXp(animated: false)
} else {
cell.showUserXp(animated: true)
self.animatedHeader = true
}
return cell
}
所以现在您可以使用动画显示 header 单元格,是否使用动画由 animatedHeader 属性 控制。现在,这不再依赖于出列的特定单元格。
我目前正在尝试为用户的经验条添加动画。
如果我在 collectionViewController 中有动画,动画就会很好地加载(一次)。但是,如果我在 headerView 中有动画(因为我想在个人资料图片上添加栏),栏会多次启动:
这是我的代码 (headerViewCell):
let shapeLayerXp = CAShapeLayer()
override func layoutSubviews() {
super.layoutSubviews()
showUserXp()
self.animateXp(toValue: 1)
}
func showUserXp() {
let center = profileImage.center
let circularPath = UIBezierPath(arcCenter: center, radius: 40, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
shapeLayerXp.path = circularPath.cgPath
let color = UIColor(red: 122 / 255, green: 205 / 255, blue: 186 / 255, alpha: 1)
shapeLayerXp.strokeColor = color.cgColor
shapeLayerXp.lineWidth = 4
shapeLayerXp.fillColor = UIColor.clear.cgColor
shapeLayerXp.lineCap = kCALineCapRound
shapeLayerXp.strokeEnd = 0
self.contentView.layer.addSublayer(shapeLayerXp)
}
func animateXp(toValue: Int) {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = toValue
basicAnimation.duration = 2
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayerXp.add(basicAnimation, forKey: "urSoBasic")
}
headerViewCell 是这样启动的:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerViewCell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
// ....
return cell
}
假设控制器中只有一个 header 单元格,除非数据发生变化(例如,不同的用户),那么您可以在视图控制器上设置一个 属性 来指示是否已显示动画。
像这样的事情会做:
var animatedHeader = false // Starts false because we want an animation the first time.
然后在第一次获取header单元格时,你可以决定是否触发动画:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerViewCell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
if !self.animatedHeader {
cell.showUserXp()
cell.animateXp()
self.animatedHeader = true
}
return cell
}
showUserXp()
和 animateXp()
方法当然需要 public。
使用此方法,header 单元格只会在第一次出列并因此显示时设置动画。
如果您确实想再次为其设置动画,您只需重置 animateHeader
属性 并重新加载 collection 视图(或只是 header)。
如果有多个header,那么您将需要分别跟踪每一个。
编辑: 由于 showXP 和 animateXP 函数的定义方式,这确实需要(意外地)使用相同的单元格。如果我自己这样做,我可能会使用更像这样的方法:
func showUserXp(animated: Bool) {
let center = profileImage.center
let circularPath = UIBezierPath(arcCenter: center, radius: 40, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true)
shapeLayerXp.path = circularPath.cgPath
let color = UIColor(red: 122 / 255, green: 205 / 255, blue: 186 / 255, alpha: 1)
shapeLayerXp.strokeColor = color.cgColor
shapeLayerXp.lineWidth = 4
shapeLayerXp.fillColor = UIColor.clear.cgColor
shapeLayerXp.lineCap = kCALineCapRound
shapeLayerXp.strokeEnd = 0
self.contentView.layer.addSublayer(shapeLayerXp)
if animated {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = toValue
basicAnimation.duration = 2
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayerXp.add(basicAnimation, forKey: "urSoBasic")
} else {
shapeLayerXp.strokeEnd = 1
}
}
然后你会像这样使用它:
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let headerViewCell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath) as! UserHeaderView
if self.animatedHeader {
cell.showUserXp(animated: false)
} else {
cell.showUserXp(animated: true)
self.animatedHeader = true
}
return cell
}
所以现在您可以使用动画显示 header 单元格,是否使用动画由 animatedHeader 属性 控制。现在,这不再依赖于出列的特定单元格。