iPhone UIView - Resize Frame to Fit Subviews

前端 未结 11 1530
时光取名叫无心
时光取名叫无心 2020-12-04 17:51

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

11条回答
  •  無奈伤痛
    2020-12-04 18:37

    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

提交回复
热议问题