34

If I understand correctly scaling a UIView with CGAffineTransform anchors the transformation to its center.

In particular:

self.frame = CGRectMake(0,0,100,100);
self.transform = CGAffineTransformMakeScale(2, 2);
NSLog(@"%f;%f;%f;%f", self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height); 

Prints:

-50;-50;200;200

How do you create a CGAffineTransform scale that uses a specific anchor point (say 0;0)?

Ilanchezhian
  • 17,426
  • 1
  • 53
  • 55
hpique
  • 119,096
  • 131
  • 338
  • 476
  • 13
    It's weird, i'm on iOS 7 and it doesn't scale from the center, but it scales from the top left... Even if I set anchorpoint to (0.5, 0.5) it doesn't scale from center. Anyone has an idea? – Van Du Tran Nov 27 '13 at 16:45
  • 2
    @VanDuTran I found that using `CATransform3D` on the view's `layer` behaves correctly and obeys `anchorPoint`. Try that instead of using `CGAffineTransformMakeScale`, which somehow constrains the view to the window top and left bounds. – Jonathan Lin Apr 11 '14 at 05:51
  • @JonathanLin I use layer with CATransform3D which also with no luck on iOS 7 – ZYiOS Jan 26 '15 at 13:09

3 Answers3

72

(a)

Scale and then translate?

Something like :

CGAffineTransform t = CGAffineTransformMakeScale(2, 2);
t = CGAffineTransformTranslate(t, width/2, height/2);
self.transform = t;

(b)

Set the anchor point (which is probably what you want really)

[self layer].anchorPoint = CGPointMake(0.0f, 0.0f);
self.transform = CGAffineTransformMakeScale(2, 2);

(c)

Set the center again to make sure it's in the same place?

CGPoint center = self.center;
self.transform = CGAffineTransformMakeScale(2, 2);
self.center = center;
deanWombourne
  • 38,189
  • 13
  • 98
  • 110
  • Method (a) prints 50;50;200;200 with a scale of 2. Will test method (b) shortly, but wouldn't that affect all transforms? Might cause side-effects. – hpique Jan 06 '12 at 12:31
  • That's because the first scale transform will affect the second. The order you apply transforms matters ;) And if it's just a translation you're trying to do then my not just set the frame/bounds/center to the new position? – deanWombourne Jan 06 '12 at 12:36
  • Interestingly, method (a) prints 0;0;300;300 with a scale of 3 and --50;-50;400;400 with a scale of 4. Maybe there's something missing? – hpique Jan 06 '12 at 12:37
  • (and yes, you're right about (b) affecting all translations on that object - that might not be what you wanted!) – deanWombourne Jan 06 '12 at 12:39
  • Of course I can change the position manually but mostly I want to understand CGAffineTransform better. :) – hpique Jan 06 '12 at 12:39
  • 4
    Don't forget that if you translate up 100px but your transform has a scale of 2 already then you're actually translating up 200px - you have to compensate for your scale factor when you decide how far to translate! – deanWombourne Jan 06 '12 at 12:41
  • 2
    Take a look at this article : http://iphonedevelopment.blogspot.com/2008/10/demystifying-cgaffinetransform.html - I found it very helpful! – deanWombourne Jan 06 '12 at 12:41
  • 2
    After a few tests it appears the transform for a (0,0) anchor point is: double scale = 2; double translateX = self.frame.size.width * (scale - 1)/2/scale; double translateY = self.frame.size.height * (scale - 1)/2/scale; CGAffineTransform t = CGAffineTransformMakeScale(scale, scale); t = CGAffineTransformTranslate(t, translateX, translateY); self.transform = t; – hpique Jan 06 '12 at 12:56
  • I have asked this question: http://stackoverflow.com/questions/15495592/when-i-make-zoom-center-is-shifted-to-the-left-bottom, and I have tried your answers but they don't work for me. I have to scale a video image with the same center. – VansFannel Mar 19 '13 at 13:21
  • Number (c) is not working, it appears as if the center is the same, do the transformation takes some time to make effect? – htafoya May 23 '17 at 16:23
4

This is the way I found the scale a view while keeping it's origin in place (0,0) in Swift5

func animate() {
    myView.setAnchorPoint(CGPoint(x: 0, y: 0))
    myView.transform = CGAffineTransform(translationX: -0.5, y: -0.5)
    let animator = UIViewPropertyAnimator(duration: 0.3, curve: .easeOut) {
        self.myView.transform = CGAffineTransform(scaleX: 0.25, y: 0.25)
    }
}

extension UIView {
func setAnchorPoint(_ point: CGPoint) {
    var newPoint = CGPoint(x: bounds.size.width * point.x, y: bounds.size.height * point.y)
    var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y);

    newPoint = newPoint.applying(transform)
    oldPoint = oldPoint.applying(transform)

    var position = layer.position

    position.x -= oldPoint.x
    position.x += newPoint.x

    position.y -= oldPoint.y
    position.y += newPoint.y

    layer.position = position
    layer.anchorPoint = point
}
}

credit to Hacking with Swift

nivbp
  • 310
  • 1
  • 11
4

Firstly #import <QuartzCore/QuartzCore.h> and then set the anchor points of your view:

   [[self layer] setAnchorPoint:CGPointMake(0, 0)];
Antoine
  • 23,526
  • 11
  • 88
  • 94
Mudit Bajpai
  • 3,010
  • 19
  • 34
  • 1
    That would change the anchor point for all transforms. I would like to create a scale transform with a specific anchor point. – hpique Jan 06 '12 at 12:45
  • CGAffineTransform tr = CGAffineTransformScale(self.transform, 2, 2); self.transform = tr; self.center = CGPointMake(0,0); – Mudit Bajpai Jan 06 '12 at 12:51