xcode CGAffineTransform 缩放和平移 - 在动画之前跳转
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27931421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
CGAffineTransform scale and translation - jump before animation
提问by matteok
I am struggling with an issue regarding CGAffineTransform scale and translation where when I set a transform in an animation block on a view that already has a transform the view jumps a bit before animating.
我正在努力解决有关 CGAffineTransform 缩放和平移的问题,当我在已经具有变换的视图上的动画块中设置变换时,视图在动画之前会跳跃一点。
Example:
例子:
// somewhere in view did load or during initialization
var view = UIView()
view.frame = CGRectMake(0,0,100,100)
var scale = CGAffineTransformMakeScale(0.8,0.8)
var translation = CGAffineTransformMakeTranslation(100,100)
var concat = CGAffineTransformConcat(translation, scale)
view.transform = transform
// called sometime later
func buttonPressed() {
var secondScale = CGAffineTransformMakeScale(0.6,0.6)
var secondTranslation = CGAffineTransformMakeTranslation(150,300)
var secondConcat = CGAffineTransformConcat(secondTranslation, secondScale)
UIView.animateWithDuration(0.5, animations: { () -> Void in
view.transform = secondConcat
})
}
Now when buttonPressed() is called the view jumps to the top left about 10 pixels before starting to animate. I only witnessed this issue with a concat transform, using only a translation transform works fine.
现在当 buttonPressed() 被调用时,视图会在开始动画之前跳转到左上角大约 10 个像素。我只用 concat 变换目睹了这个问题,只使用平移变换工作正常。
Edit: Since I've done a lot of research regarding the matter I think I should mention that this issue appears regardless of whether or not auto layout is turned on
编辑:由于我对此事进行了大量研究,因此我想我应该提到无论是否打开自动布局都会出现此问题
回答by Theo
I ran into the same issue, but couldn't find the exact source of the problem. The jump seems to appear only in very specific conditions: If the view animates from a transform t1
to a transform t2
and both transforms are a combination of a scale and a translation (that's exactly your case). Given the following workaround, which doesn't make sense to me, I assume it's a bug in Core Animation.
我遇到了同样的问题,但找不到问题的确切根源。跳跃似乎只出现在非常特定的条件下:如果视图从变换t1
到变换进行动画处理,t2
并且这两种变换都是缩放和平移的组合(这正是您的情况)。鉴于以下对我来说没有意义的解决方法,我认为这是 Core Animation 中的一个错误。
First, I tried using CATransform3D
instead of CGAffineTransform
.
首先,我尝试使用CATransform3D
代替CGAffineTransform
.
Old code:
旧代码:
var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, 1.1, 1.1)
transform = CGAffineTransformTranslate(transform, 10, 10)
view.layer.setAffineTransform(transform)
New code:
新代码:
var transform = CATransform3DIdentity
transform = CATransform3DScale(transform, 1.1, 1.1, 1.0)
transform = CATransform3DTranslate(transform, 10, 10, 0)
view.layer.transform = transform
The new code should be equivalent to the old one (the fourth parameter is set to 1.0
or 0
so that there is no scaling/translation in z
direction), and in fact it shows the same jumping. However, here comes the black magic: In the scale transformation, change the z
parameter to anything different from 1.0
, like this:
新代码应该与旧代码等效(第四个参数设置为1.0
or0
以便在z
方向上没有缩放/平移),实际上它显示相同的跳跃。然而,黑魔法来了:在比例变换中,将z
参数更改为任何不同于的参数1.0
,如下所示:
transform = CATransform3DScale(transform, 1.1, 1.1, 1.01)
This parameter should have no effect, but now the jump is gone.
这个参数应该没什么作用,但是现在跳转没了。
?
?
回答by k06a
Looks like Apple UIView animation internal bug. When Apple interpolates CGAffineTransform
changes between two values to create animation it should do following steps:
看起来像 Apple UIView 动画内部错误。当 AppleCGAffineTransform
在两个值之间插入变化以创建动画时,它应该执行以下步骤:
- Extract translation, scale, and rotation
- Interpolate extracted values form start to end
- Assemble
CGAffineTransform
for each interpolation step
- 提取平移、缩放和旋转
- 从开始到结束插入提取的值
CGAffineTransform
为每个插值步骤组装
Assembling should be in following order:
组装应按以下顺序进行:
- Translation
- Scaling
- Rotation
- 翻译
- 缩放
- 回转
But looks like Apple make translation after scaling and rotation. This bug should be fixed by Apple.
但看起来苹果在缩放和旋转后进行平移。这个错误应该由Apple修复。
回答by lbsweek
I dont know why, but this code can work
我不知道为什么,但这段代码可以工作
update:
更新:
I successfully combine scale, translate, and rotation together, from any transform state to any new transform state.
我成功地将缩放、平移和旋转结合在一起,从任何变换状态到任何新的变换状态。
I think the transform is reinterpreted at the start of the animation.
我认为转换是在动画开始时重新解释的。
the anchor of start transform is considered in new transform, and then we convert it to old transform.
在新变换中考虑开始变换的锚点,然后我们将其转换为旧变换。
self.v = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50))
self.v?.backgroundColor = .blue
self.view.addSubview(v!)
func buttonPressed() {
let view = self.v!
let m1 = view.transform
let tempScale = CGFloat(arc4random()%10)/10 + 1.0
let tempRotae:CGFloat = 1
let m2 = m1.translatedBy(x: CGFloat(arc4random()%30), y: CGFloat(arc4random()%30)).scaledBy(x: tempScale, y: tempScale).rotated(by:tempRotae)
self.animationViewToNewTransform(view: view, newTranform: m2)
}
func animationViewToNewTransform(view: UIView, newTranform: CGAffineTransform) {
// 1. pointInView.apply(view.transform) is not correct point.
// the real matrix is mAnchorToOrigin.inverted().concatenating(m1).concatenating(mAnchorToOrigin)
// 2. animation begin trasform is relative to final transform in final transform coordinate
// anchor and mAnchor
let normalizedAnchor0 = view.layer.anchorPoint
let anchor0 = CGPoint(x: normalizedAnchor0.x * view.bounds.width, y: normalizedAnchor0.y * view.bounds.height)
let mAnchor0 = CGAffineTransform.identity.translatedBy(x: anchor0.x, y: anchor0.y)
// 0->1->2
//let origin = CGPoint(x: 0, y: 0)
//let m0 = CGAffineTransform.identity
let m1 = view.transform
let m2 = newTranform
// rotate and scale relative to anchor, not to origin
let matrix1 = mAnchor0.inverted().concatenating(m1).concatenating(mAnchor0)
let matrix2 = mAnchor0.inverted().concatenating(m2).concatenating(mAnchor0)
let anchor1 = anchor0.applying(matrix1)
let mAnchor1 = CGAffineTransform.identity.translatedBy(x: anchor1.x, y: anchor1.y)
let anchor2 = anchor0.applying(matrix2)
let txty2 = CGPoint(x: anchor2.x - anchor0.x, y: anchor2.y - anchor0.y)
let txty2plusAnchor2 = CGPoint(x: txty2.x + anchor2.x, y: txty2.y + anchor2.y)
let anchor1InM2System = anchor1.applying(matrix2.inverted()).applying(mAnchor0.inverted())
let txty2ToM0System = txty2plusAnchor2.applying(matrix2.inverted()).applying(mAnchor0.inverted())
let txty2ToM1System = txty2ToM0System.applying(mAnchor0).applying(matrix1).applying(mAnchor1.inverted())
var m1New = m1
m1New.tx = txty2ToM1System.x + anchor1InM2System.x
m1New.ty = txty2ToM1System.y + anchor1InM2System.y
view.transform = m1New
UIView.animate(withDuration: 1.4) {
view.transform = m2
}
}
I also try the zScale solution, it seems also work if set zScale non-1 at the first transform or at every transform
我也尝试了 zScale 解决方案,如果在第一次变换或每次变换时将 zScale 设置为非 1,它似乎也有效
let oldTransform = view.layer.transform
let tempScale = CGFloat(arc4random()%10)/10 + 1.0
var newTransform = CATransform3DScale(oldTransform, tempScale, tempScale, 1.01)
newTransform = CATransform3DTranslate(newTransform, CGFloat(arc4random()%30), CGFloat(arc4random()%30), 0)
newTransform = CATransform3DRotate(newTransform, 1, 0, 0, 1)
UIView.animate(withDuration: 1.4) {
view.layer.transform = newTransform
}
回答by Dave Batton
Instead of CGAffineTransformMakeScale() and CGAffineTransformMakeTranslation(), which create a transform based off of CGAffineTransformIdentity (basically no transform), you want to scale and translate based on the view's current transform using CGAffineTransformScale() and CGAffineTransformTranslate(), which start with the existing transform.
代替 CGAffineTransformMakeScale() 和 CGAffineTransformMakeTranslation(),它们创建基于 CGAffineTransformIdentity 的变换(基本上没有变换),您希望使用 CGAffineTransformScale() 和 CGAffineTransformTranslate() 根据视图的当前变换进行缩放和平移,它们从现有的转变。
回答by Snit
The source of the issue is the lack of perspective information to the transform.
问题的根源在于缺乏转换的透视信息。
You can add perspective information modifying the m34
property of your 3d transform
您可以添加透视信息来修改m34
3d 变换的属性
var transform = CATransform3DIdentity
transform.m34 = 1.0 / 200 //your own perspective value here
transform = CATransform3DScale(transform, 1.1, 1.1, 1.0)
transform = CATransform3DTranslate(transform, 10, 10, 0)
view.layer.transform = transform