【问题标题】:Animation within headerView in SwiftSwift 中 headerView 中的动画
【发布时间】:2018-06-07 11:51:11
【问题描述】:

我目前正在尝试向用户的 xp 栏添加动画。

如果我在 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

}

【问题讨论】:

  • 您在layoutSubViews() 中调用showUserXp()animateXp(),因此每次系统决定布局标题单元格时,动画都会再次发生。显然,当集合视图滚动时会发生这种情况,但也可能在其他时间发生,例如设备轮换,标题单元重用。你想达到什么目的?是否应该仅在控制器首次显示时才进行动画处理,然后仅在其他特定时间进行动画处理?
  • 我只想在 viewController 启动时(一次)为栏设置动画
  • 这个控制器只有一个标题单元格吗?单元格是如何定义的(故事板、xib 文件、手动)?
  • 我编辑了我的第一篇文章,向您展示如何定义单元格

标签: ios swift animation swift3 uicollectionviewcell


【解决方案1】:

假设控制器中只有一个标题单元格,除非数据发生更改(例如不同的用户),那么您可以在视图控制器上设置一个属性以指示动画是否已显示。

这样的事情会做:

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

那么在第一次获取标题单元格时,你可以决定是否触发动画:

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() 方法当然需要公开。

使用此方法,标题单元格只会在第一次出列并因此显示时进行动画处理。

如果您确实想再次为其设置动画,您只需重置 animateHeader 属性并重新加载集合视图(或只是标题)。

如果有多个标题,那么您需要分别跟踪每个标题。

编辑:这确实需要(意外地)使用相同的单元格,因为 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
}

所以现在您可以使用动画或不使用动画来显示标题单元格,并且您是否使用动画由 animatedHeader 属性控制。现在,这不再依赖于出队的特定单元格。

【讨论】:

  • 在这种情况下,我宁愿持有一个指向 VC 中的 headerViewCell 的指针。 collectionView 没有义务返回每次调用的相同实例。这取决于实现细节
  • 使用我的方法很容易扩展。例如,假设您现在在列表中有 500 个项目,您只需保留一个包含 50 个布尔值的数组来控制它们是否已呈现给用户(因此标题动画)。如果您保留对标题单元格的引用,那么您现在需要保留对 500 个标题单元格的引用,并且现在绕过出于性能和内存原因而完成的出列的整个想法。
  • 我担心你的方法有缺陷。它仅在集合视图使正确的单元格出列时才有效。所以你依靠集合视图不重用视图。 [删除了我的帖子并投了反对票]
  • 您是部分正确的,因为单元格以未显示的用户 XP 开头(我没有完全检查出来)。我只是真的使用给出的示例来展示如何只制作一次动画。如果我真的这样做,我将有一个函数来设置带有动画属性的 XP 值。然后第一次看到它,我会展示它动画,然后每隔一段时间就没有动画。这样你就不用关心使用什么单元格了。我将更新我的答案以更清楚地表明这一点。
猜你喜欢
  • 2020-10-08
  • 1970-01-01
  • 2020-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多