-
-
Save lanephillips/6874933 to your computer and use it in GitHub Desktop.
CGFloat ScaleToAspectFitRectInRect(CGRect rfit, CGRect rtarget) | |
{ | |
// first try to match width | |
CGFloat s = CGRectGetWidth(rtarget) / CGRectGetWidth(rfit); | |
// if we scale the height to make the widths equal, does it still fit? | |
if (CGRectGetHeight(rfit) * s <= CGRectGetHeight(rtarget)) { | |
return s; | |
} | |
// no, match height instead | |
return CGRectGetHeight(rtarget) / CGRectGetHeight(rfit); | |
} | |
CGRect AspectFitRectInRect(CGRect rfit, CGRect rtarget) | |
{ | |
CGFloat s = ScaleToAspectFitRectInRect(rfit, rtarget); | |
CGFloat w = CGRectGetWidth(rfit) * s; | |
CGFloat h = CGRectGetHeight(rfit) * s; | |
CGFloat x = CGRectGetMidX(rtarget) - w / 2; | |
CGFloat y = CGRectGetMidY(rtarget) - h / 2; | |
return CGRectMake(x, y, w, h); | |
} | |
CGFloat ScaleToAspectFitRectAroundRect(CGRect rfit, CGRect rtarget) | |
{ | |
// fit in the target inside the rectangle instead, and take the reciprocal | |
return 1 / ScaleToAspectFitRectInRect(rtarget, rfit); | |
} | |
CGRect AspectFitRectAroundRect(CGRect rfit, CGRect rtarget) | |
{ | |
CGFloat s = ScaleToAspectFitRectAroundRect(rfit, rtarget); | |
CGFloat w = CGRectGetWidth(rfit) * s; | |
CGFloat h = CGRectGetHeight(rfit) * s; | |
CGFloat x = CGRectGetMidX(rtarget) - w / 2; | |
CGFloat y = CGRectGetMidY(rtarget) - h / 2; | |
return CGRectMake(x, y, w, h); | |
} |
Converted to Swift 3:
extension CGRect {
func scaleToAspectFit(in rtarget: CGRect) -> CGFloat {
// first try to match width
let s = rtarget.width / self.width;
// if we scale the height to make the widths equal, does it still fit?
if self.height * s <= rtarget.height {
return s
}
// no, match height instead
return rtarget.height / self.height
}
func aspectFit(in rtarget: CGRect) -> CGRect {
let s = scaleToAspectFit(in: rtarget)
let w = width * s
let h = height * s
let x = rtarget.midX - w / 2
let y = rtarget.midY - h / 2
return CGRect(x: x, y: y, width: w, height: h)
}
func scaleToAspectFit(around rtarget: CGRect) -> CGFloat {
// fit in the target inside the rectangle instead, and take the reciprocal
return 1 / rtarget.scaleToAspectFit(in: self)
}
func aspectFit(around rtarget: CGRect) -> CGRect {
let s = scaleToAspectFit(around: rtarget)
let w = width * s
let h = height * s
let x = rtarget.midX - w / 2
let y = rtarget.midY - h / 2
return CGRect(x: x, y: y, width: w, height: h)
}
}
The AspectFitRectInRect(_, _) function behave closely to AVFoundation's AVMakeRectWithAspectRatioInsideRect() and has the same problem with images that has a close enough size.
For example, a (0, 0, 750, 1334) in a (0, 0, 1080, 1920) rectangle becomes a (0.26986506746618488, -1.1368683772161603e-13, 1079.4602698650676, 1920.0000000000002), which is close but not in a 1080 1920 rect -- especially when you try to get a CGRectIntegral() on it: (0, -1, 1080, 1921).
I believe the problem is with the approximation inherent to the CGFloat returned by the scaleToAspectFit(). Still, I'm pretty sure your solution will be enough for 99.9% of users, thanks for providing it.
I fix that little problem by not using the approx in the computation of the major dimension (the Height in the exemple) but by setting it directly to the major dimension of the target. Main con, the resulting rectangle ratio is not the same as the original.
CGRect AspectFitRectInRect(CGRect rfit, CGRect rtarget)
{
CGFloat scale = 1;
//default values to the target
CGFloat w = CGRectGetWidth(rtarget);
CGFloat h = CGRectGetHeight(rtarget);
CGFloat s = CGRectGetWidth(rtarget) / CGRectGetWidth(rfit);
// The greatest dim was preset, compute the lesser one
if (CGRectGetHeight(rfit) * s <= CGRectGetHeight(rtarget))
{
scale = s;
h = CGRectGetHeight(rfit) * scale;
} else {
scale = CGRectGetHeight(rtarget) / CGRectGetHeight(rfit);
w = CGRectGetWidth(rfit) * scale;
}
// Center the resulting rect in the target
CGFloat x = CGRectGetMidX(rtarget) - w / 2;
CGFloat y = CGRectGetMidY(rtarget) - h / 2;
return CGRectMake(x, y, w, h);
}
Thank you. If I had to re-write this kind of code one time I might have lost it....