Question

I scale UIBezierPath (built of one [0,0 - 1x1] rect) by factor 2 in both x and y directions. UIBezierPath ".bounds" is alright (i.e. scaled as expected), while ".CGPath" remains unchanged...

Code:

#import <UIKit/UIKit.h>

int main(int argc, char *argv[])
{
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0,
                                                                     1, 1)];
    NSLog(@"path.bounds box before transform:%@",
          NSStringFromCGRect(path.bounds));
    NSLog(@"path.CGPath box before transform:%@",
          NSStringFromCGRect(CGPathGetBoundingBox(path.CGPath)));

    [path applyTransform:CGAffineTransformMakeScale(2, 2)];

    NSLog(@"path.bounds box after transform:%@",
          NSStringFromCGRect(path.bounds));
    NSLog(@"path.CGPath box after transform:%@",
          NSStringFromCGRect(CGPathGetBoundingBox(path.CGPath)));

    return 0;        
}

Output:

path.bounds box before transform:{{0, 0}, {1, 1}}
path.CGPath box before transform:{{0, 0}, {1, 1}}
path.bounds box after transform:{{0, 0}, {2, 2}}
path.CGPath box after transform:{{0, 0}, {1, 1}}

Why?

Was it helpful?

Solution

The reason for this difference is because the call to applyTransform simply stores the transform matrix in the path object. It does not cause the path itself to be modified. path.bounds is a calculated property that is derived using the transform, whereas the call the CGPathGetBoundingBox is simply iterating over the elements of the passed in CGPath object.

Because there could potentially be a great number of path elements, storing the transform matrix, and not modifying all of the the path elements every time a new matrix is assigned, is done as an optimization. This work is only done once, or minimally, when only certain properties, such as the bounds, of the UIBezierPath are queried.

OTHER TIPS

As of iOS 5.1, the CGPath returned from UIBezier's .CGPath property is indeed updated when the UIBezierPath has a new transform applied.

However, this doesn't preclude a solution for older iOS versions. You can get the CGPath from the UIBezierPath, transform it directly, then set it back onto the UIBezierPath. Lo and behold, all the other properties, like bounds and origins, will update correctly and immediately.

UIBezierPath* path = [UIBezierPath bezierPathWithRect:CGRectMake(0.0f, 0.0f,
                                                                 1.0f, 1.0f)];

CGAffineTransform transform = CGAffineTransformMakeScale(2.0f, 2.0f);
CGPathRef intermediatePath = CGPathCreateCopyByTransformingPath(path.CGPath,
                                                                &transform);

path.CGPath = intermediatePath;

CGPathRelease(intermediatePath);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top