Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone UIView - Resize Frame to Fit Subviews

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]; 
like image 487
sol Avatar asked Aug 31 '10 01:08

sol


People also ask

How do I resize UIView by dragging from edges?

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

What does sizeToFit do?

sizeToFit()Resizes and moves the receiver view so it just encloses its subviews.

When can I call sizeToFit?

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.


2 Answers

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 
like image 61
user1204936 Avatar answered Sep 22 '22 08:09

user1204936


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:

  1. Move the frame of the view to the top left most position
  2. Move the subviews the opposite direction to negate the effect.

A picture is worth a thousand words.

demonstration

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

playground image

like image 28
Mazyod Avatar answered Sep 19 '22 08:09

Mazyod