简体   繁体   English

Swift 中 headerView 中的动画

[英]Animation within headerView in Swift

I'm currently trying to add an animation to the user's xp bar.我目前正在尝试向用户的 xp 栏添加动画。

If I had the animation in the collectionViewController, the animation is well loaded (once).如果我在 collectionViewController 中有动画,则动画加载良好(一次)。 However, if I had the animation within the headerView (because I would like to add the bar on the profile picture) the bar is launched more than one time:但是,如果我在 headerView 中有动画(因为我想在个人资料图片上添加栏),则该栏会多次启动:

在此处输入图片说明

This is my code (headerViewCell):这是我的代码(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")
}

The headerViewCell is launched like that: 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

}

Right assuming there is only one header cell ever in the controller unless the data is changed (eg a different user) then you can set a property on the view controller to indicate if the animation has been shown.假设控制器中只有一个标题单元格,除非数据发生更改(例如,不同的用户),那么您可以在视图控制器上设置一个属性以指示动画是否已显示。

Something like this would do:像这样的事情会做:

var animatedHeader = false // Starts false because we want an animation the first time.

Then when obtaining the header cell for the first time you can decide to fire the animation or not like this:然后在第一次获取标题单元格时,您可以决定是否触发动画,如下所示:

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
}

The showUserXp() and animateXp() methods will need to be public of course. showUserXp()animateXp()方法当然需要公开。

Using this method the header cell will only animate the first time it is dequeued and therefore displayed.使用此方法,标题单元格只会在第一次出列并因此显示时进行动画处理。

If you did want to animate it again you just need to reset the animateHeader property and reload the collection view (or just the header).如果您确实想再次为其设置动画,则只需重置animateHeader属性并重新加载集合视图(或仅加载标题)。

If there is more than one header then you will need to keep track of each of them separately.如果有多个标题,则您需要分别跟踪每个标题。

Edit: This does require (accidentally) the same cell to be used because of how the showXP and animateXP functions are defined.编辑:由于 showXP 和 animateXP 函数的定义方式,这确实需要(偶然)使用相同的单元格。 If I was doing this myself I would probably use something more like this approach:如果我自己这样做,我可能会使用更像这种方法的方法:

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

Then you would use it like this:然后你会像这样使用它:

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
}

So now you can show the header cell using an animation or not and whether you do use the animation is controlled by the animatedHeader property.所以现在您可以使用动画或不显示标题单元格,并且您是否使用动画由动画标题属性控制。 This now no longer relies on a specific cell being dequeued.这现在不再依赖于特定的单元出列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM