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 属性 控制。现在,这不再依赖于出列的特定单元格。