CATransaction:视图在完成时闪烁
CATransaction: a view flashes on completion
我正在编写一个有点复杂的动画,分为 2 个步骤:
- 将不需要可见的
UIViews
的不透明度更改为 0,并将 UIImageView
(具有 alpha = 1
)移动到另一个 CGPoint
(位置)。
- 把另一个
UIView
的不透明度改成1,把上一步的UIImageView
的不透明度改成0,然后在这一步的动画结束后,去掉UIImageView
来自 superview.
我是这样做的:
第一步 是在没有显式 CATransaction 的情况下完成的。这 2 个动画只是将 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。
嗯,问题是,如何摆脱这种闪烁?也许这个动画可以用更好的方式来完成?
P.S。我没有使用 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
和一个带有 4 个子视图的 UIScrollView
。我尝试用 timeView
(UIImageView
) 的快照替换 timeView
,但这没有帮助。
编辑 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
图层的不透明度从 0 设置为 1,但您尚未将其 设置 为 1(在完成处理程序中除外,这将在稍后发生)。因此,我们将表示层从 0 设置为 1,然后动画结束,显示真实层的不透明度一直为 0。
因此,在您的动画开始之前,将 timeView
的图层的不透明度设置为 1。此外,由于您使用的是延迟 beginTime
,因此您需要将动画的 fillMode
设置为 "backwards"
。
通过将您的测试代码修改为独立的并且看起来像这样,我能够获得良好的结果;有延迟,view淡入,最后没有闪现:
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。
我正在编写一个有点复杂的动画,分为 2 个步骤:
- 将不需要可见的
UIViews
的不透明度更改为 0,并将UIImageView
(具有alpha = 1
)移动到另一个CGPoint
(位置)。 - 把另一个
UIView
的不透明度改成1,把上一步的UIImageView
的不透明度改成0,然后在这一步的动画结束后,去掉UIImageView
来自 superview.
我是这样做的:
第一步 是在没有显式 CATransaction 的情况下完成的。这 2 个动画只是将 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。
嗯,问题是,如何摆脱这种闪烁?也许这个动画可以用更好的方式来完成?
P.S。我没有使用 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
和一个带有 4 个子视图的 UIScrollView
。我尝试用 timeView
(UIImageView
) 的快照替换 timeView
,但这没有帮助。
编辑 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
图层的不透明度从 0 设置为 1,但您尚未将其 设置 为 1(在完成处理程序中除外,这将在稍后发生)。因此,我们将表示层从 0 设置为 1,然后动画结束,显示真实层的不透明度一直为 0。
因此,在您的动画开始之前,将 timeView
的图层的不透明度设置为 1。此外,由于您使用的是延迟 beginTime
,因此您需要将动画的 fillMode
设置为 "backwards"
。
通过将您的测试代码修改为独立的并且看起来像这样,我能够获得良好的结果;有延迟,view淡入,最后没有闪现:
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。