20

Something I don't understand about transformations. I want to zoom in to say the top right corner of a UIView (the main view). I use CGAffineTransformScale and have tried both setting the center/anchorPoint as well as CGAffineTransformMakeTranslation to no avail.

I can't figure out how to set the translation properly so that it will zoom in on this point.

CGAffineTransform tr = CGAffineTransformScale(self.view.transform, 2, 2);

[UIView animateWithDuration:2.5 delay:0 options:0 animations:^{
            self.view.transform = tr;
            self.view.center = CGPointMake(480,0);

} completion:^(BOOL finished) {}];
halfer
  • 19,824
  • 17
  • 99
  • 186
akaru
  • 6,299
  • 9
  • 63
  • 102

2 Answers2

35

You are setting the center of the view to the top right corner. That means you're zooming in on somewhere to the lower left of the center.

Try this

CGAffineTransform tr = CGAffineTransformScale(self.view.transform, 2, 2);
CGFloat h = self.view.frame.size.height;
[UIView animateWithDuration:2.5 delay:0 options:0 animations:^{
    self.view.transform = tr;
    self.view.center = CGPointMake(0,h);
} completion:^(BOOL finished) {}];

This will set the center of the blown-up view to the left bottom corner, effectively zooming in with the top right corner fixed.

This code doesn't have the height hardcoded, which is a little more portable (to an iPad of iPhone 5).

You need to first save h before setting the transform property, because after that you should not rely on the value of frame.

edit

To make it work for any scale s, use this:

CGFloat s = 3;
CGAffineTransform tr = CGAffineTransformScale(self.view.transform, s, s);
CGFloat h = self.view.frame.size.height;
CGFloat w = self.view.frame.size.width;
[UIView animateWithDuration:2.5 delay:0 options:0 animations:^{
    self.view.transform = tr;
    self.view.center = CGPointMake(w-w*s/2,h*s/2);
} completion:^(BOOL finished) {}];

To make it work for bottom left, use this:

CGFloat s = 3;
CGAffineTransform tr = CGAffineTransformScale(self.view.transform, s, s);
CGFloat h = self.view.frame.size.height;
CGFloat w = self.view.frame.size.width;
[UIView animateWithDuration:2.5 delay:0 options:0 animations:^{
    self.view.transform = tr;
    self.view.center = CGPointMake(w*s/2,h-h*s/2);
} completion:^(BOOL finished) {}];

To make it work for bottom right, use this:

CGFloat s = 3;
CGAffineTransform tr = CGAffineTransformScale(self.view.transform, s, s);
CGFloat h = self.view.frame.size.height;
CGFloat w = self.view.frame.size.width;
[UIView animateWithDuration:2.5 delay:0 options:0 animations:^{
    self.view.transform = tr;
    self.view.center = CGPointMake(w-w*s/2,h-h*s/2);
} completion:^(BOOL finished) {}];

See also: How to scale (zoom) a UIView to a given CGPoint

Community
  • 1
  • 1
mvds
  • 45,755
  • 8
  • 102
  • 111
  • Thanks, this works as is for me, but makes it zoom in more towards the actual center on scales above 3. How would this work, say for bottom right? Oddly enough, this works for me : self.view.center = CGPointMake(-280,-h) – akaru Mar 27 '11 at 06:41
  • 1
    That correct, since the center of the blown up view depends on the scale. I added examples for bottom right/left and for any scale. Just remember: first you blow up the view (making it 320*s,480*s big), and then you are setting the center of that view. – mvds Mar 27 '11 at 13:14
  • Brilliant! That's the simple little algorithm I was missing, thank you. Would MakeTranslate be a better option than setting the center, if I want to easily revert? – akaru Mar 27 '11 at 20:15
  • This is wonderful little piece of code. You should blog it ;) – Eric Brotto Aug 24 '11 at 10:18
  • 2
    This might be out of the scope of the question, but how would you modify this to scale to any arbitrary CGPoint in a view? – glenstorey Aug 19 '13 at 19:03
  • Works great. In the completion I end up with adding `self.view.transform = .identity` to recover distorted few subviews. – byJeevan Jan 02 '22 at 12:52
3

In case anyone is still interested, here is the Swift 3.0 solution, to which I added the possibility to fade out correctly.

(I know there are two enums missing, but the code is still readable and I think you will get the concept)

func animateZoom(direction: ZoomDirection, type: ZoomType, duration: Double = 0.3, scale: CGFloat = 1.4) {
    var cx, cy: CGFloat
    var tr: CGAffineTransform = CGAffineTransform.identity

    if (type == .IN) { tr = self.transform.scaledBy(x: scale, y: scale) }

    let h: CGFloat = self.frame.size.height;
    let w: CGFloat = self.frame.size.width;

    if (type == .IN) {
        switch direction {
        case .LEFT_DOWN:
            cx = w*scale/2
            cy = h-h*scale/2
            break
        case .LEFT_UP:
            cx = w*scale/2
            cy = h*scale/2
            break
        case .RIGHT_UP:
            cx = w-w*scale/2
            cy = h*scale/2
            break
        case .RIGHT_DOWN:
            cx = w-w*scale/2
            cy = h-h*scale/2
            break
        }
    } else {
        cx = w/scale/2
        cy = h/scale/2
    }

    UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseInOut], animations: {
        self.transform = tr
        self.center = CGPoint(x: cx, y: cy)
    }, completion: { finish in })
}
Nighthawk
  • 41
  • 5