Dave G Dave G - 3 months ago 23
iOS Question

Animation: Using Autolayout, Frame.Origin & Broken Constraints, Or 3rd Option?

I've used auto layout to build all my app layouts to adapt to all iPhone and iPad models. So I get that auto layout is important. However, now I have a keyframe automation where a view needs to animate to a different location. I achieved this by getting the location of the other view:

let last_Pos = destinationImg.superview?.convertPoint(destinationImg.frame.origin, toView: nil)


And then doing a keyframe animation where in the frames I set the new coordinates, for example:

myView.frame.origin.x = targetViews_lastPos!.x


This is breaking auto layout constraints though. I've read about this and know many people suggest animating a change in auto layout constraints (instead of frame position). But isn't there a better way? Doing this would be a nightmare as The UIView I'm animating has a set of complex constraints that are set just right:

enter image description here

And the view who's location I'm targeting are completely unrelated (they're nested in completely different superviews). Wouldn't this require a ton of code to create an animation that I can do in a couple of lines with frame.origin? And also note, I make the view disappear after it reaches the destination so it's only a temporary position anyhow. Messing with 15 constraints to do this seems nuts.

How are most people handling this nightmare of animating UIViews which have complex auto layout constraints??

Answer

In my opinion, the easiest solution is to leave your existing auto layout constraints. Finding an alternative auto layout arrangement can be a pain if you put a lot of work into making your layout adapt to various size classes.

So how do you keep your animation from breaking the constraints? Well as long as your view returns to its original position, animates off the screen, or is removed after the animation completes... take a picture!

I found this code in another thread:

extension UIView {
    func takeSnapshot() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)

        drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)

        // old style: layer.renderInContext(UIGraphicsGetCurrentContext())

        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

You can call it like this:

let picToAnimate = UIImageView(image: originalView.takeSnapshot())

Then make its size and location the same as what you took a picture of:

picToAnimate.frame = CGRectMake(originalView.frame.origin.x, originalView.frame.origin.y, originalView.frame.width, originalView.frame.height)
self.viewToAddItTo.addSubview(picToAnimate)

Don't forget to hide the original:

originalView.hidden = true

Hope this helps.

Comments