【问题标题】:CATransaction: a view flashes on completionCATransaction:视图在完成时闪烁
【发布时间】:2015-08-06 01:35:26
【问题描述】:

我正在写一个有点复杂的动画,分两步:

  1. 将不需要可见的UIViews 的不透明度更改为0,并将UIImageView(具有alpha = 1)移动到另一个CGPoint(位置)。
  2. 将另一个UIView的不透明度改为1,将上一步的UIImageView的不透明度改为0,然后在这一步的动画完成后,将UIImageView从superview中移除。

我是这样做的:

第一步是在没有明确的 CATransaction 的情况下完成的。这两个动画只是将beginTime 设置为CACurrentMediaTime()。我在layer.addAnimation(...) 通话后立即对视图应用更改。 这里一切正常。

第二步实现中,我在开始时调用CATransaction.begin()。 在begin/commit 内部调用CATransaction 我创建并添加2 个CABasicAnimations 到2 个不同的层:一个用于将不透明度从0 更改为1(用于UIView),一个用于将不透明度从1 更改为0(用于UIImageView)。两个动画都将beginTime 设置为CACurrentMediaTime() + durationOfThePreviousStep

CATransaction.begin() 之后我调用CATransaction.setCompletionBlock({...}),在这个完成块中我对这两个视图应用更改:设置它们的新alpha 并从superview 中删除UIImageView

问题是,在整个动画结束时,将 alpha 动画设置为 1 的 UIView 闪烁,这意味着它的 alpha 设置回 0(尽管我在完成块中将其 alpha 设置为 1)和在此之后,完成块执行并且它的 alpha 再次上升到 1。

好吧,问题是,如何摆脱这种闪烁?也许这个动画可以做得更好?

附:我没有使用UIView 动画,因为我对这些动画的自定义计时函数感兴趣。

编辑 1: 这是代码。我已经删除了UIImageView alpha 动画,因为它并不是真正需要的。

var totalDuration: CFTimeInterval = 0.0

// Alpha animations.
let alphaAnimation = CABasicAnimation()
alphaAnimation.keyPath = "opacity"
alphaAnimation.fromValue = 1
alphaAnimation.toValue = 0
alphaAnimation.beginTime = CACurrentMediaTime()
alphaAnimation.duration = 0.15

let alphaAnimationName = "viewsFadeOut"
view1.layer.addAnimation(alphaAnimation, forKey: alphaAnimationName)
view1.alpha = 0

view2.layer.addAnimation(alphaAnimation, forKey: alphaAnimationName)
view2.alpha = 0

view3.layer.addAnimation(alphaAnimation, forKey: alphaAnimationName)
view3.alpha = 0

view4.layer.addAnimation(alphaAnimation, forKey: alphaAnimationName)
view4.alpha = 0

// Image View moving animation.
// Add to total duration.
let rect = /* getting rect */
let newImagePosition = view.convertPoint(CGPoint(x: CGRectGetMidX(rect), y: CGRectGetMidY(rect)), fromView: timeView)

let imageAnimation = CABasicAnimation()
imageAnimation.keyPath = "position"
imageAnimation.fromValue = NSValue(CGPoint: imageView!.layer.position)
imageAnimation.toValue = NSValue(CGPoint: newImagePosition)
imageAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
imageAnimation.beginTime = CACurrentMediaTime()
imageAnimation.duration = 0.3

imageView!.layer.addAnimation(imageAnimation, forKey: "moveImage")
imageView!.center = newImagePosition

totalDuration += imageAnimation.duration

// Time View alpha.
CATransaction.begin()
CATransaction.setCompletionBlock {
    self.timeView.alpha = 1
    self.imageView!.removeFromSuperview()
    self.imageView = nil
}

let beginTime = CACurrentMediaTime() + totalDuration
let duration = 0.3

alphaAnimation.fromValue = 0
alphaAnimation.toValue = 1
alphaAnimation.beginTime = beginTime
alphaAnimation.duration = duration
timeView.layer.addAnimation(alphaAnimation, forKey: "timeViewFadeIn")

/* imageView alpha animation is not necessary, so I removed it */

CATransaction.commit()

编辑 2:导致问题的一段代码:

CATransaction.begin()
CATransaction.setCompletionBlock {
    self.timeView.alpha = 1
}

let duration = 0.3

let alphaAnimation = CABasicAnimation()
alphaAnimation.keyPath = "opacity"
alphaAnimation.fromValue = 0.0
alphaAnimation.toValue = 1.0
alphaAnimation.duration = duration
timeView.layer.addAnimation(alphaAnimation, forKey: "timeViewFadeIn")

CATransaction.commit()

也许问题是因为timeView 有一个UITextField 和一个UIScrollView 有4 个子视图。我尝试将timeView 替换为timeView (UIImageView) 的快照,但这没有帮助。

编辑 3: 新代码。使用此代码,timeView 始终具有 alpha = 1,并且它还会从 0 变为 1。

CATransaction.begin()
CATransaction.setCompletionBlock {
    self.imageView!.removeFromSuperview()
    self.imageView = nil
}

let alphaAnimation = CABasicAnimation()
alphaAnimation.keyPath = "opacity"
alphaAnimation.fromValue = 0.0
alphaAnimation.toValue = 1.0
alphaAnimation.duration = 0.3
alphaAnimation.beginTime = beginTime

timeView.layer.addAnimation(alphaAnimation, forKey: "timeViewFadeIn")
timeView.alpha = 1.0
CATransaction.commit()

【问题讨论】:

  • 请显示您的代码,或足够相似/简化的代码,以便于重现问题。
  • 嗯,哪个是闪烁的?我们可以去掉其他动画的代码吗?
  • timeView 是闪烁的那个。是的,您可以消除其他代码,但这对我没有帮助。它仍然在闪烁。
  • 是的,但这不是消除它的原因。消除它的原因是我需要一些我能理解的最小的东西,如果有必要,在 Xcode 中重现。你能帮我把示例代码删减一些吗?我不想仅仅为了运行您的代码而制作四个额外的视图。
  • 太好了,我想我现在明白了问题的根源;看我的回答。

标签: ios uiview core-animation cabasicanimation catransaction


【解决方案1】:

只看你的代码,我会预计它会闪烁。为什么?因为您已将timeView 层的不透明度从 0 设置为 1,但您还没有将其 设置 为 1(完成处理程序除外,这将在稍后发生)。因此,我们将表示层从 0 设置为 1,然后动画结束,显示真实层的不透明度一直为 0。

所以,在动画开始之前将timeView 的图层不透明度设置为 1。此外,由于您使用的是延迟的beginTime,因此您需要将动画的fillMode 设置为"backwards"

通过将您的测试代码修改为独立的并看起来像这样,我能够获得良好的结果;有延迟,视图淡入,最后没有闪烁:

    CATransaction.begin()
    let beginTime = CACurrentMediaTime() + 1.0 // arbitrary, just testing
    let alphaAnimation = CABasicAnimation()
    alphaAnimation.keyPath = "opacity"
    alphaAnimation.fromValue = 0.0
    alphaAnimation.toValue = 1.0
    alphaAnimation.duration = 1.0 // arbitrary, just testing
    alphaAnimation.fillMode = "backwards"
    alphaAnimation.beginTime = beginTime
    timeView.layer.addAnimation(alphaAnimation, forKey: "timeViewFadeIn")
    timeView.layer.opacity = 1.0
    CATransaction.commit()

我觉得你的代码还有一些其他的地方很奇怪。以这种方式使用事务完成块有些风险;我不明白你为什么不给你的动画一个委托。此外,您正在多次重复使用alphaAnimation;我不能推荐那个。如果我是你,我会为每个动画创建一个新的 CABasicAnimation。

【讨论】:

  • 谢谢!但为什么不建议重用 CABasicAnimations?
  • let duration = 0.3 - 是的,我在编辑问题时忘记粘贴这一行:alphaAnimation.duration = duration
  • "但是为什么你不能推荐重用 CABasicAnimations" 只是看起来很懒惰而且没必要。另外,这有点冒险。碰巧 CABasicAnimation 在分配给图层时会被复制,因此您以后的更改不会影响先前分配的动画,但这只是一个实现细节。一般来说,一个类实例应该被视为一个类实例——它是可变的,并且突变会影响对它的所有引用。
  • 感谢您的解释。我对这个闪烁有更多的问题。我从完成块中删除了设置 alpha。现在,如果我在addAnimation:: 调用之后立即设置它,timeView 将从整个动画开始时使用 alpha = 1,并且在结束时将其从 0 设置为 1。如果我为这个动画设置一个代理,它会像以前一样闪烁。
  • 添加这一行:alphaAnimation.fillMode = "backwards" - 将该信息添加到我的答案中。
猜你喜欢
  • 1970-01-01
  • 2016-11-07
  • 1970-01-01
  • 2012-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多