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

30,189

Solution 1

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);

Solution 2

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

Solution 3

Sure, using CGRectInset works:

CGRect someRect = CGRectMake(10, 10, 100, 100);
someRect = CGRectInset(someRect, someRect.size.width * -0.2, someRect.size.height * -0.2);

Solution 4

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))
    }
}

Solution 5

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
Share:
30,189

Related videos on Youtube

brandonscript
Author by

brandonscript

Quantum foam traveler. Purveyor of opinions. Product design https://delving.com. UX & Code. Advisor https://pacificaviator.co. Previously Google, Apigee. ADHD. Be kind. (He/him)

Updated on July 09, 2022

Comments

  • brandonscript
    brandonscript almost 2 years

    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}

    • Fattie
      Fattie about 7 years
      for 2017, it's insetBy
  • rmaddy
    rmaddy over 9 years
    The question is tagged Objective-C. Best to give answers in the desired language.
  • thelaws
    thelaws over 9 years
    Re the sidenote: I think remus is correct that (110,110) gives an area 20% bigger than (100,100).
  • Ian MacDonald
    Ian MacDonald over 9 years
    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%.
  • Tommy
    Tommy over 9 years
    +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/GraphicsImagin‌​g/…
  • Ian MacDonald
    Ian MacDonald over 9 years
    Apple's statement is silly. I updated my answer to conform to it anyways. :/
  • brandonscript
    brandonscript over 9 years
    I meant increase edge sizes, but you're right, area would be an entirely different calculation, and this is a good answer.
  • Ian MacDonald
    Ian MacDonald over 9 years
    Good thing I included both. :)
  • brandonscript
    brandonscript over 9 years
    Selected as answer because ^
  • brandonscript
    brandonscript over 9 years
    Close second, and I actually like this answer too since it's got both Objc and Swift ;)
  • brandonscript
    brandonscript over 7 years
    Looking at this one again, shouldn't the output rect be -1, -1, 11, 11?
  • Fattie
    Fattie about 7 years
    this is insetBy in the latest Swift, cheers @IanMacDonald
  • lhunath
    lhunath about 6 years
    This does not appear to scale relative to the center of the rectangle, thus does not change the x,y origin.
  • Harry Bloom
    Harry Bloom about 6 years
    As the above comment states, this doesn't keep the center point of the rect!
  • gheclipse
    gheclipse over 5 years
    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) / 2