【问题标题】:CGAffineTransform scale and translation - jump before animationCGAffineTransform 缩放和平移 - 动画前跳转
【发布时间】:2015-01-13 21:01:51
【问题描述】:

我正在努力解决有关 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 变换见证了这个问题,只使用平移变换就可以了。

编辑:由于我已经对此事进行了大量研究,所以我想我应该提一下,无论是否打开自动布局,都会出现此问题

【问题讨论】:

  • 如果你使用CGAffineTransformTranslate(secondScale,150,300)会怎样?
  • 结果一模一样
  • “有点跳跃”是什么意思?它会返回身份转换吗?它是从跳转动画到secondConcat,还是跳转,返回到concat,然后动画到secondConcat
  • 你解决过这个问题吗?目前在这里调试同样的问题。
  • 不幸的是我没有。我最终将一个视图包裹在另一个视图中,这样我就可以翻译外部视图并缩放内部视图。这样我就能够解决这个问题。

标签: ios xcode scale transformation cgaffinetransform


【解决方案1】:

我遇到了同样的问题,但找不到问题的确切根源。跳转似乎只出现在非常特定的条件下:如果视图从变换 t1 动画到变换 t2 并且两个变换都是比例和平移的组合(这正是你的情况)。鉴于以下对我来说没有意义的解决方法,我认为这是核心动画中的一个错误。

首先,我尝试使用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.00这样在z方向上没有缩放/平移),实际上它显示相同的跳转.然而,黑魔法来了:在比例转换中,将z 参数更改为与1.0 不同的任何值,如下所示:

transform = CATransform3DScale(transform, 1.1, 1.1, 1.01)

这个参数应该没有效果,但是现在跳转没了。

?✨

【讨论】:

  • 我认为这个答案没有得到足够的学分......我已经编码了好几天,无法解决这个问题。
  • ☝️同上。我用简单的比例和翻译遇到了同样的问题。在动画之前,图层会向左“移动”一点点。将1.01 比例应用于z 轴可以解决问题。
  • 对于任何对我的测试感到好奇的人,此问题已在 iOS 13 中得到修复
  • 我运行 iOS 13 的模拟器仍然遇到这个问题。
【解决方案2】:

看起来像 Apple UIView 动画内部错误。当 Apple 在两个值之间插入 CGAffineTransform 变化以创建动画时,它应该执行以下步骤:

  • 提取平移、缩放和旋转
  • 从头到尾插入提取的值
  • 为每个插值步骤组装CGAffineTransform

组装应按以下顺序:

  • 翻译
  • 缩放
  • 旋转

但看起来 Apple 在缩放和旋转后进行平移。这个错误应该由 Apple 修复。

【讨论】:

    【解决方案3】:

    我不知道为什么,但是这段代码可以工作

    更新:

    我成功地将缩放、平移和旋转结合在一起,从任何变换状态到任何新的变换状态。

    我认为变换是在动画开始时重新解释的。

    在新的变换中考虑开始变换的锚点,然后我们将其转换为旧的变换。

    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
        }
    

    【讨论】:

      【解决方案4】:

      您希望使用 CGAffineTransformScale() 和 CGAffineTransformTranslate() 根据视图的当前变换进行缩放和平移,而不是 CGAffineTransformMakeScale() 和 CGAffineTransformMakeTranslation(),它们基于 CGAffineTransformIdentity 创建变换(基本上没有变换),它们开始使用现有的转换。

      【讨论】:

      • 我实际上需要在动画期间设置一个绝对变换,这意味着我不想添加到现有的变换中,而是将其替换为动画
      • 我也刚刚尝试使用 CGAffineTransformTranslate 而不是 CGAffineTransformMakeTranslate 并且问题仍然存在。
      【解决方案5】:

      问题的根源是缺少转换的透视信息。

      您可以添加透视信息来修改 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
      

      【讨论】:

      • 你能解释一下为什么this'神奇地'有效吗?
      猜你喜欢
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-30
      • 2013-12-17
      • 2011-03-31
      • 2012-07-08
      • 2019-02-05
      相关资源
      最近更新 更多