How can I increase the size of a CGRect by a certain percent value?
Asked Answered
C

7

22

How can I increase the size of a CGRect by a certain percent value? Should I use some form of CGRectInset to do it?

Example:

Assume I have a CGRect: {10, 10, 110, 110}

I want to increase its size (retaining the same center point) by 20% to:

{0, 0, 120, 120}

Commentator answered 13/11, 2014 at 21:36 Comment(1)
for 2017, it's insetByBin
D
43

You can use CGRectInset if you like:

double pct = 0.2;
CGRect newRect = CGRectInset(oldRect, -CGRectGetWidth(oldRect)*pct/2, -CGRectGetHeight(oldRect)*pct/2);

To decrease the size, remove the -s.

Side note: A CGRect that is 20% bigger than {10, 10, 100, 100} is {0, 0, 120, 120}.


Edit: If the intention is to increase by area, then this'll do it (even for rectangles that aren't square):

CGFloat width = CGRectGetWidth(oldRect);
CGFloat height = CGRectGetHeight(oldRect);
double pct = 1.2; // 20% increase
double newWidth = sqrt(width * width * pct);
double newHeight = sqrt(height * height * pct);
CGRect newRect = CGRectInset(oldRect, (width-newWidth)/2, (height-newHeight)/2);
Doralynn answered 13/11, 2014 at 21:42 Comment(6)
Re the sidenote: I think remus is correct that (110,110) gives an area 20% bigger than (100,100).Hermie
I suppose it depends on your interpretation of "20% bigger". By area, the resulting square has edges of length 109.5445, which isn't as pretty as just assuming he meant to increase the edge size by 20%.Doralynn
I meant increase edge sizes, but you're right, area would be an entirely different calculation, and this is a good answer.Commentator
Good thing I included both. :)Doralynn
Selected as answer because ^Commentator
this is insetBy in the latest Swift, cheers @IanMacDonaldBin
C
12

In Swift:

func increaseRect(rect: CGRect, byPercentage percentage: CGFloat) -> CGRect {
    let startWidth = CGRectGetWidth(rect)
    let startHeight = CGRectGetHeight(rect)
    let adjustmentWidth = (startWidth * percentage) / 2.0
    let adjustmentHeight = (startHeight * percentage) / 2.0
    return CGRectInset(rect, -adjustmentWidth, -adjustmentHeight)
}

let rect = CGRectMake(0, 0, 10, 10)
let adjusted = increaseRect(rect, byPercentage: 0.1)
// -0.5, -0.5, 11, 11

In ObjC:

- (CGRect)increaseRect:(CGRect)rect byPercentage:(CGFloat)percentage
{
    CGFloat startWidth = CGRectGetWidth(rect);
    CGFloat startHeight = CGRectGetHeight(rect);
    CGFloat adjustmentWidth = (startWidth * percentage) / 2.0;
    CGFloat adjustmentHeight = (startHeight * percentage) / 2.0;
    return CGRectInset(rect, -adjustmentWidth, -adjustmentHeight);
}

CGRect rect = CGRectMake(0,0,10,10);
CGRect adjusted = [self increaseRect:rect byPercentage:0.1];
// -0.5, -0.5, 11, 11
Choreographer answered 13/11, 2014 at 21:44 Comment(5)
The question is tagged Objective-C. Best to give answers in the desired language.Athletics
+1 for being the only answerer so far to have obeyed Apple's statement that "your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics."developer.apple.com/Library/ios/documentation/GraphicsImaging/…Absorbed
Apple's statement is silly. I updated my answer to conform to it anyways. :/Doralynn
Close second, and I actually like this answer too since it's got both Objc and Swift ;)Commentator
Looking at this one again, shouldn't the output rect be -1, -1, 11, 11?Commentator
A
6

Sure, using CGRectInset works:

CGRect someRect = CGRectMake(10, 10, 100, 100);
someRect = CGRectInset(someRect, someRect.size.width * -0.2, someRect.size.height * -0.2);
Athletics answered 13/11, 2014 at 21:43 Comment(0)
A
6

Swift 4 extension inspired by several of the answers here with simplified calculations:

extension CGRect {
    func scaleLinear(amount: Double) -> CGRect {
        guard amount != 1.0, amount > 0.0 else { return self }
        let ratio = ((1.0 - amount) / 2.0).cgFloat
        return insetBy(dx: width * ratio, dy: height * ratio)
    }

    func scaleArea(amount: Double) -> CGRect {
        return scaleLinear(percent: sqrt(amount))
    }

    func scaleLinear(percent: Double) -> CGRect {
        return scaleLinear(amount: percent / 100)
    }

    func scaleArea(percent: Double) -> CGRect {
        return scaleArea(amount: percent / 100)
    }
}

Usage is simply:

rect.scaleLinear(percent: 120.0)  OR (amount: 1.2)
rect.scaleArea(percent: 120.0)  OR (amount: 1.2)

If you are interested in trying my testing methods:

/// Testing
extension CGRect {
    var area: CGFloat { return width * height }
    var center: CGPoint { return CGPoint(x: origin.x + width/2, y: origin.y + height/2)
    }

    func compare(_ r: CGRect) {
        let centered = center.x == r.center.x && center.y == r.center.y
        print("linear = \(r.width / width), area = \(r.area / area) centered \(centered)")
    }

    static func ScaleTest() {
        let rect = CGRect(x: 17, y: 24, width: 200, height: 100)
        let percent = 122.6
        rect.compare(rect.scaleLinear(percent: percent))
        rect.compare(rect.scaleArea(percent: percent))
    }
}
Aldrich answered 25/10, 2018 at 19:49 Comment(0)
A
3

I'm using CGRect > insetBy in my Swift code

https://developer.apple.com/documentation/coregraphics/cgrect/1454218-insetby

With this, your percent value will be the scaleX as my example.

    let dx = rectWidth*scaleX
    let dy = rectHeight*scaleX
    let rectangle = CGRect(x: rectX,
                           y: rectY,
                           width: rectWidth,
                           height: rectHeight).insetBy(dx: -dx, dy: -dy)
  • use positive value to scale down
  • use negative value to scale up
Alika answered 28/1, 2019 at 6:39 Comment(0)
A
2

Using Swift you can retain the center point (relative to the source Rect) and increase/decrease the size as follows using an extension:

extension CGRect {
    func centerAndAdjustPercentage(percentage p: CGFloat) -> CGRect {
        let x = self.origin.x
        let y = self.origin.y
        let w = self.width
        let h = self.height

        let newW = w * p
        let newH = h * p
        let newX = (w - newW) / 2
        let newY = (h - newH) / 2

        return CGRect(x: newX, y: newY, width: newW, height: newH)
    }
}
let newRect = oldRect.centerAndAdjustPercentage(percentage: 0.25)
Achromatin answered 16/3, 2015 at 20:40 Comment(1)
CGRect extension is convenient, but this code does not work correctly. newX and newY should be added to the origin (x, y). let newX = x + (w - newW) / 2Aldrich
G
0
let scale = percent / 100
let newRect = oldRect.applying(CGAffineTransform(scaleX: scale, y: scale))

Beware that this approach also will change x and y of rectangle.

Gimel answered 28/8, 2017 at 12:50 Comment(2)
This does not appear to scale relative to the center of the rectangle, thus does not change the x,y origin.Soll
As the above comment states, this doesn't keep the center point of the rect!Phillipphillipe

© 2022 - 2024 — McMap. All rights reserved.