我正在努力解决有关CGAffineTransform缩放和平移的问题,当我在已经有变换的视图上的动画块中设置变换时,视图会在动画制作之前跳过一点。
示例:
// 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
})
}
现在,当调用buttonPressed()时,视图会在开始制作动画之前跳转到左上角约10个像素。我只用一个concat变换来见证这个问题,只使用一个转换变换就可以了。
编辑:由于我已经就此事做了很多研究,我想我应该提一下,无论是否开启了自动布局,都会出现此问题
答案 0 :(得分:39)
我遇到了同样的问题,但无法找到问题的确切来源。跳转似乎仅在非常特定的条件下出现:如果视图从变换t1
动画到变换t2
,并且两个变换都是缩放和平移的组合(这完全是准确的)你的情况)。鉴于以下对我没有意义的解决方法,我认为它是Core Animation中的一个错误。
首先,我尝试使用CATransform3D
代替CGAffineTransform
。
旧代码:
var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, 1.1, 1.1)
transform = CGAffineTransformTranslate(transform, 10, 10)
view.layer.setAffineTransform(transform)
新代码:
var transform = CATransform3DIdentity
transform = CATransform3DScale(transform, 1.1, 1.1, 1.0)
transform = CATransform3DTranslate(transform, 10, 10, 0)
view.layer.transform = transform
新代码应与旧代码等效(第四个参数设置为1.0
或0
,以便z
方向上没有缩放/转换),并且事实上它显示了相同的跳跃。但是,这里出现了黑魔法:在缩放转换中,将z
参数更改为与1.0
不同的任何内容,如下所示:
transform = CATransform3DScale(transform, 1.1, 1.1, 1.01)
此参数应无效,但现在跳转已消失。
✨
答案 1 :(得分:4)
看起来像Apple UIView动画内部的bug。当Apple在两个值之间插入CGAffineTransform
更改以创建动画时,应执行以下步骤:
CGAffineTransform
组装应遵循以下顺序:
但看起来像Apple在缩放和轮换后进行翻译。这个错误应该由Apple修复。
答案 2 :(得分:0)
而不是CGAffineTransformMakeScale()和CGAffineTransformMakeTranslation(),它创建一个基于CGAffineTransformIdentity(基本上没有变换)的变换,你想要使用CGAffineTransformScale()和CGAffineTransformTranslate(基于视图的当前变换进行缩放和平移) ),从现有的变换开始。
答案 3 :(得分:0)
问题的根源是缺乏转化的透视信息。
您可以添加修改3d转换的m34
属性的透视信息
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
答案 4 :(得分:0)
我不知道为什么,但是此代码可以正常工作
更新:
我成功地将缩放,平移和旋转结合在一起,从任何变换状态到任何新的变换状态。
我认为变换是在动画开始时重新解释的。
在新的转换中考虑开始转换的锚点,然后将其转换为旧的转换。
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
}
}
我也尝试了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
}