在iOS 7上,CGAffineTransforms的性能非常慢

时间:2022-12-28 20:22:27

At the moment I'm creating some transitions and transform via CGAffineTransform for a panning view and I'm running in troubles because of the transform performance under iOS 7 and an iPhone 4.

目前我正在通过CGAffineTransform创建一些过渡和转换以进行平移视图,并且由于iOS 7和iPhone 4下的转换性能,我遇到了麻烦。

I dived in Istruments and logged the stuff and the heavy lifting is done when I'm applying my transforms to the view.

我潜入了Istruments并记录了这些东西,当我将变换应用到视图时,完成了繁重的工作。

Current Implementation

目前的实施

func handlePan(recognizer : UIPanGestureRecognizer) {

        let drawerLocation = recognizer.locationInView(drawerView!)
        let locationInView = recognizer.locationInView(containerView!)
        let progressMax = containerView!.frame.height - 40 - 20

        if(recognizer.state == .Changed) {

            let offsetDrag = dragStartPosition.y - locationInView.y
            let progress = Float(offsetDrag / progressMax)

            if(offsetDrag >= 0) {

                let positionTransform = CGAffineTransformMakeTranslation(0, -((containerView!.bounds.height - 40 - 20) * CGFloat(normalizedProgress)))
                viewWithTransform.transform = positionTransform // really bad performance here
            } else {
                // reset the transition
            }     
    }
}

Workaround for iOS 7

适用于iOS 7的解决方法

func handlePan(recognizer : UIPanGestureRecognizer) {

        let drawerLocation = recognizer.locationInView(drawerView!)
        let locationInView = recognizer.locationInView(containerView!)
        let progressMax = containerView!.frame.height - 40 - 20

        if(recognizer.state == .Changed) {

            let offsetDrag = dragStartPosition.y - locationInView.y
            let progress = Float(offsetDrag / progressMax)

            if(offsetDrag >= 0) {
                if UIDevice.currentDevice().systemMajorVersion() > 7 {
                    let positionTransform = CGAffineTransformMakeTranslation(0, -((containerView!.bounds.height - 40 - 20) * CGFloat(progress)))
                    viewWithTransform.transform = positionTransform // really bad performance here
                } else {
                    viewWithTransform.frame = CGRectMake(0, -((containerView!.bounds.height - 40 - 20) * CGFloat(progress)), drawerView!.frame.size.width, drawerView!.frame.size.height); // works like a charm on iOS 7
                }

            } else {
                // reset the transition
            }     
    }
}

Question

Why is the performance so bad on iOS 7 and my iPhone 4 with CGAffineTransforms? Because it's doing the same thing with the offset then the frame setting in the workaround. When I use UIView.animateWithDuration() with transform it's performing on 60fps. What can I do not to rewrite the whole implementation on my iOS 7 basis?

为什么在iOS 7和我的iPhone 4上使用CGAffineTransforms的性能如此糟糕?因为它与偏移量做同样的事情然后在变通方法中的框架设置。当我使用UIView.animateWithDuration()进行转换时,它在60fps上执行。我不能在iOS 7上重写整个实现吗?

UPDATE 28th July Found out that AutoLayout is possible involved in this issue. Here is a TimeProfiler Stack from my current calls:

更新7月28日发现AutoLayout可能涉及此问题。这是我当前通话中的TimeProfiler Stack:

在iOS 7上,CGAffineTransforms的性能非常慢

Now I'm facing a big problem in my current implementation, because I rely on AutoLayout. What's the easiest solution to solve this hassle on iOS 7?

现在我在当前的实现中遇到了一个大问题,因为我依赖于AutoLayout。在iOS 7上解决这个麻烦的最简单的解决方案是什么?

1 个解决方案

#1


5  

While you are right that they do the same thing, under the hood, it is not that easy - there are matrix multiplications going all over the place. More on that can be found here.

虽然你说他们做同样的事情是正确的,但它并不容易 - 在整个地方都有矩阵乘法。更多相关信息可以在这里找到。

It is strange that if you are doing just this, it affects your performance - but I guess your layout is complicated and so re-rendering takes a lot of time ; I actually had the same problem like a week ago, so here is what helped me:

奇怪的是,如果你这样做,它会影响你的表现 - 但我猜你的布局很复杂,因此重新渲染需要花费很多时间;我实际上遇到了像一周前一样的问题,所以这里有什么帮助我:

  • removing AutoLayout from that particular view for some reason helps a lot
  • 出于某种原因从特定视图中删除AutoLayout有很大帮助
  • manipulation with frame should be used when you are not rotating / scaling view. It really becomes need when you are using those.
  • 当您不旋转/缩放视图时,应使用框架操作。当你使用它时,它确实变得需要。
  • removing shadows and semi-transparent views, especially if there is more of them behind each other, REALLY drops the FPS when transform is used
  • 去除阴影和半透明视图,特别是如果它们之间有更多的视图,在使用变换时真的会丢弃FPS

Also, you can try to assign that transform in async call and see if that helps:

此外,您可以尝试在异步调用中分配该转换,看看是否有帮助:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            // Transform view
        })
    })

And if you really want to be fancy, use POP Framework from Facebook. It is great for what you want, and it allows you to do some fancy stuff like bounce, springiness etc. Here is how you can use it:

如果你真的想要花哨,可以使用Facebook的POP Framework。它非常适合你想要的东西,它可以让你做一些像弹跳,弹跳等花哨的东西。以下是你如何使用它:

// Create spring animation (there are more types, if you want)
let animation = POPSpringAnimation(propertyNamed: kPOPViewCenter)

// Configure it properly
animation.autoreverses = false
animation.removedOnCompletion = true
animation.fromValue = view.center
animation.toValue = finalPositionPoint

// Add new animation to your view - animation key can be whatever you like, serves as reference
view.pop_addAnimation(animation, forKey: "YourAnimationKey")

Edit: If you are just moving view around, use .center property, not the frame. It saves you need to define height / width again and gives clearer idea about your intentions.

编辑:如果您只是移动视图,请使用.center属性,而不是框架。它可以节省您再次定义高度/宽度的需要,并让您更清楚地了解您的意图。

#1


5  

While you are right that they do the same thing, under the hood, it is not that easy - there are matrix multiplications going all over the place. More on that can be found here.

虽然你说他们做同样的事情是正确的,但它并不容易 - 在整个地方都有矩阵乘法。更多相关信息可以在这里找到。

It is strange that if you are doing just this, it affects your performance - but I guess your layout is complicated and so re-rendering takes a lot of time ; I actually had the same problem like a week ago, so here is what helped me:

奇怪的是,如果你这样做,它会影响你的表现 - 但我猜你的布局很复杂,因此重新渲染需要花费很多时间;我实际上遇到了像一周前一样的问题,所以这里有什么帮助我:

  • removing AutoLayout from that particular view for some reason helps a lot
  • 出于某种原因从特定视图中删除AutoLayout有很大帮助
  • manipulation with frame should be used when you are not rotating / scaling view. It really becomes need when you are using those.
  • 当您不旋转/缩放视图时,应使用框架操作。当你使用它时,它确实变得需要。
  • removing shadows and semi-transparent views, especially if there is more of them behind each other, REALLY drops the FPS when transform is used
  • 去除阴影和半透明视图,特别是如果它们之间有更多的视图,在使用变换时真的会丢弃FPS

Also, you can try to assign that transform in async call and see if that helps:

此外,您可以尝试在异步调用中分配该转换,看看是否有帮助:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            // Transform view
        })
    })

And if you really want to be fancy, use POP Framework from Facebook. It is great for what you want, and it allows you to do some fancy stuff like bounce, springiness etc. Here is how you can use it:

如果你真的想要花哨,可以使用Facebook的POP Framework。它非常适合你想要的东西,它可以让你做一些像弹跳,弹跳等花哨的东西。以下是你如何使用它:

// Create spring animation (there are more types, if you want)
let animation = POPSpringAnimation(propertyNamed: kPOPViewCenter)

// Configure it properly
animation.autoreverses = false
animation.removedOnCompletion = true
animation.fromValue = view.center
animation.toValue = finalPositionPoint

// Add new animation to your view - animation key can be whatever you like, serves as reference
view.pop_addAnimation(animation, forKey: "YourAnimationKey")

Edit: If you are just moving view around, use .center property, not the frame. It saves you need to define height / width again and gives clearer idea about your intentions.

编辑:如果您只是移动视图,请使用.center属性,而不是框架。它可以节省您再次定义高度/宽度的需要,并让您更清楚地了解您的意图。