我有一个带有图像集合(视图)的集合视图。布局导致在每个可见项上发生转换。中心的项目既可以缩放也可以翻译,而其他项目只需翻译。
关键是,布局是为每个项目设置图层的变换属性。
稍后,当用户触摸某个项目时,我想使用关键帧动画为该项目设置动画。
我遇到的行为是项目似乎恢复到未转换状态,动画发生在未转换状态,然后项目的布局转换返回。
为什么?
我尝试使用视图的转换属性,支持图层的仿射转换属性以及支持图层的3D转换属性都具有相同的行为。关键帧动画使用3D变换。
我对变换和动画的理解显然存在差距,但我已经看到这个问题针对iOS以外的平台提出而没有答案。
布局转换代码(在流布局子类中):
// Calculate the distance from the center of the visible rect to the center of the attributes.
// Then normalize it so we can compare them all. This way, all items further away than the
// active get the same transform.
let distanceFromVisibleRectToItem = CGRectGetMidX(visibleRect) - attributes.center.x
let normalizedDistance = distanceFromVisibleRectToItem / ACTIVE_DISTANCE
let isLeft = distanceFromVisibleRectToItem > 0
var transform = CATransform3DIdentity
var maskAlpha: CGFloat = 0.0
if (abs(distanceFromVisibleRectToItem) < ACTIVE_DISTANCE) {
// We're close enough to apply the transform in relation to how far away from the center we are.
transform = CATransform3DTranslate(transform,
(isLeft ? -FLOW_OFFSET : FLOW_OFFSET) * abs(distanceFromVisibleRectToItem/TRANSLATE_DISTANCE),
0, (1 - abs(normalizedDistance)) * 40000 + (isLeft ? 200 : 0))
// Set the zoom factor.
let zoom = 1 + ZOOM_FACTOR * (1 - abs(normalizedDistance))
transform = CATransform3DScale(transform, zoom, zoom, 1)
attributes.zIndex = 1
let ratioToCenter = (ACTIVE_DISTANCE - abs(distanceFromVisibleRectToItem)) / ACTIVE_DISTANCE
// Interpolate between 0.0f and INACTIVE_GREY_VALUE
maskAlpha = INACTIVE_GREY_VALUE + ratioToCenter * (-INACTIVE_GREY_VALUE)
} else {
// We're too far away - just apply a standard perspective transform.
transform = CATransform3DTranslate(transform, isLeft ? -FLOW_OFFSET : FLOW_OFFSET, 0, 0)
attributes.zIndex = 0
maskAlpha = INACTIVE_GREY_VALUE
}
attributes.transform3D = transform
动画代码(注意这是UIView类的Swift扩展):
func bounceView(completion: (() -> Void)? = nil) {
var animation = bounceAnimation(frame.height)
animation.delegate = AnimationDelegate(completion: completion)
layer.addAnimation(animation, forKey: "bounce")
}
func bounceAnimation(itemHeight: CGFloat) -> CAKeyframeAnimation {
let factors: [CGFloat] = [0, 32, 60, 83, 100, 114, 124, 128, 128, 124, 114, 100, 83, 60, 32,
0, 24, 42, 54, 62, 64, 62, 54, 42, 24, 0, 18, 28, 32, 28, 18, 0]
var transforms: [AnyObject] = [NSValue(CATransform3D: self.layer.transform)]
for factor in factors {
let positionOffset = factor/256.0 * itemHeight
let transform = CATransform3DMakeTranslation(0, -positionOffset, 0)
transforms.append(NSValue(CATransform3D: transform))
}
let animation = CAKeyframeAnimation(keyPath: "transform")
animation.repeatCount = 1
animation.duration = CFTimeInterval(factors.count)/30.0
animation.fillMode = kCAFillModeForwards
animation.values = transforms
animation.removedOnCompletion = true // final stage is equal to starting stage
animation.autoreverses = false
return animation;
}
注意:我应该更简单地说明我希望这个过程以集合视图布局转换状态开始和结束。我希望动画也能在布局转换状态下发生(即根本不会恢复到未转换状态)。
答案 0 :(得分:0)
在您的动画中,您似乎没有将模型与表示层同步。
当您使用Core Animation(例如CABasicAnimation)进行动画处理时,它只会更改表示层(您在屏幕上看到的内容),但实际上您的图层所具有的状态与可见图层不同。
(您可以在this objc.io issue)
中详细了解相关内容所以基本上要修复此问题,您需要在CAAnimation中更新模型。您需要设置from和toValue,然后在设置之后更新转换,使其与表示层的最终状态相匹配。
修改
现在我们有了一些代码,我可以具体了
要将模型图层同步到表示层,您需要在添加动画之前将模型设置为最终状态:
var animation = bounceAnimation(frame.height)
animation.delegate = AnimationDelegate(completion: completion)
layer.transform = CATransform3DMakeTranslation(0, 0, 0)
layer.addAnimation(animation, forKey: "bounce")
DavidRönnqvist有一个很好的咆哮about this issue in this gist。
答案 1 :(得分:0)
<强>解:强> Tiago Almeida提供的帮助对于让我思考动画实际上在做什么(它是如何工作的)非常有用。我的基本假设是动画将从当前模型层转换开始是不正确的。正如Tiago所指出的,表示层是与模型层分离且不同的层。
一旦我通过我的厚头,我意识到我必须手动连接当前的模型变换与为动画帧和中提琴构建的变换!事情按预期工作。
更新的动画代码:
var transforms: [AnyObject] = []
for factor in factors {
let positionOffset = factor/256.0 * itemHeight
var transform = CATransform3DMakeTranslation(0, -positionOffset, 0)
transform = CATransform3DConcat(self.layer.transform, transform)
transforms.append(NSValue(CATransform3D: transform))
}