Shouldn't there be a way to resize the frame of a UIView after you've added subviews so that the frame is the size needed to enclose all the subviews? If your subviews are added dynamically how can you determine what size the frame of the container view needs to be? This doesn't work:
[myView sizeToFit];
You can do this by checking the touch-start point. If it hits one of your four corners you can resize based on the distance between that touch-start point and the current-touch point. (If the touch-start point didn't hit a corner, we just move the view instead of resizing.)
sizeToFit()Resizes and moves the receiver view so it just encloses its subviews.
The function sizeToFit is used when you want to automatically resize a label based on the content stored within it. As you can see, there is no change as the text is perfectly fitting in labelOne. sizeToFit only changes the label's frame.
You could also add the following code to calculate subviews position.
[myView resizeToFitSubviews]
UIViewUtils.h
#import <UIKit/UIKit.h> @interface UIView (UIView_Expanded) -(void)resizeToFitSubviews; @end
UIViewUtils.m
#import "UIViewUtils.h" @implementation UIView (UIView_Expanded) -(void)resizeToFitSubviews { float w = 0; float h = 0; for (UIView *v in [self subviews]) { float fw = v.frame.origin.x + v.frame.size.width; float fh = v.frame.origin.y + v.frame.size.height; w = MAX(fw, w); h = MAX(fh, h); } [self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, w, h)]; } @end
I needed to fit subviews had a negative origin point, and CGRectUnion
is the ObjC way of doing it, honestly, as someone mentioned in the comments. First, let's see how it works:
As you can see below, we assume some subviews are lying outside, so we need to do two things to make this look good, without affecting the positioning of the subviews:
A picture is worth a thousand words.
Code is worth a billion words. Here is the solution:
@interface UIView (UIView_Expanded) - (void)resizeToFitSubviews; @end @implementation UIView (UIView_Expanded) - (void)resizeToFitSubviews { // 1 - calculate size CGRect r = CGRectZero; for (UIView *v in [self subviews]) { r = CGRectUnion(r, v.frame); } // 2 - move all subviews inside CGPoint fix = r.origin; for (UIView *v in [self subviews]) { v.frame = CGRectOffset(v.frame, -fix.x, -fix.y); } // 3 - move frame to negate the previous movement CGRect newFrame = CGRectOffset(self.frame, fix.x, fix.y); newFrame.size = r.size; [self setFrame:newFrame]; } @end
I thought it would be fun to write in Swift 2.0 .. I was right!
extension UIView { func resizeToFitSubviews() { let subviewsRect = subviews.reduce(CGRect.zero) { $0.union($1.frame) } let fix = subviewsRect.origin subviews.forEach { $0.frame.offsetInPlace(dx: -fix.x, dy: -fix.y) } frame.offsetInPlace(dx: fix.x, dy: fix.y) frame.size = subviewsRect.size } }
And the playground proof:
Notice the visualAidView
doesn't move, and helps you see how the superview
resizes while maintaining the positions of the subviews.
let canvas = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80)) canvas.backgroundColor = UIColor.whiteColor() let visualAidView = UIView(frame: CGRect(x: 5, y: 5, width: 70, height: 70)) visualAidView.backgroundColor = UIColor(white: 0.8, alpha: 1) canvas.addSubview(visualAidView) let superview = UIView(frame: CGRect(x: 15, y: 5, width: 50, height: 50)) superview.backgroundColor = UIColor.purpleColor() superview.clipsToBounds = false canvas.addSubview(superview) [ { let view = UIView(frame: CGRect(x: -10, y: 0, width: 15, height: 15)) view.backgroundColor = UIColor.greenColor() return view }(), { let view = UIView(frame: CGRect(x: -10, y: 40, width: 35, height: 15)) view.backgroundColor = UIColor.cyanColor() return view }(), { let view = UIView(frame: CGRect(x: 45, y: 40, width: 15, height: 30)) view.backgroundColor = UIColor.redColor() return view }(), ].forEach { superview.addSubview($0) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With