iPhone UIView - 调整框架以适应子视图

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iPhone UIView - 调整框架以适应子视图相关的知识,希望对你有一定的参考价值。

在添加子视图后,是否应该有一种方法来调整UIView的框架大小,以便框架是包含所有子视图所需的大小?如果动态添加子视图,您如何确定容器视图的框架需要的大小?这不起作用:

[myView sizeToFit];
答案

看看Having trouble getting UIView sizeToFit to do anything meaningful

要点是sizeToFit用于子类来覆盖,并且在UIView中没有做任何事情。它为UILabel做了一些东西,因为它覆盖了由sizeThatFits:调用的sizeToFit

另一答案
[myView sizeToFit];

应该工作,为什么不在前后检查CGRect?

另一答案

您还可以添加以下代码来计算子视图位置。

[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
另一答案

我需要使子视图具有负的原点,而CGRectUnion是ObjC的做法,老实说,正如评论中提到的那样。首先,让我们看看它是如何工作的:

正如您在下面所看到的,我们假设一些子视图位于外部,因此我们需要做两件事来使其看起来很好,而不会影响子视图的定位:

  1. 将视图框移动到最左上方的位置
  2. 将子视图移动到相反的方向以否定效果。

一张图片胜过千言万语。

代码值十亿字。这是解决方案:

@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

我觉得用Swift 2.0编写会很有趣..我是对的!

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

和操场证明:

请注意visualAidView不会移动,并帮助您了解superview如何在保持子视图位置的同时调整大小。

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

另一答案

它看起来像Kenny 's answer指向referenced question的正确解决方案,但可能已经带走了错误的概念。 UIView class reference绝对建议一个使sizeToFit与您的自定义视图相关的系统。

Override sizeThatFits, not sizeToFit

您的自定义UIViews需要覆盖sizeThatFits以返回“适合接收者子视图的新大小”,但是您希望计算此值。你甚至可以使用another answer的数学来确定你的新尺寸(但没有重新创建内置的sizeToFit系统)。

sizeThatFits返回与其状态相关的数字后,在您的自定义视图上调用sizeToFit将开始导致预期的调整大小。

How sizeThatFits works

没有覆盖,sizeThatFits只返回传入的大小参数(默认为self.bounds.size来自sizeToFit的调用。虽然我在这个问题上只有couple sources,但似乎传入的大小不被视为严格的需求。

[sizeThatFits]返回适合传递给它的约束的控件的“最合适”大小。如果不能满足约束,该方法可以(强调他们的)决定忽略约束。

另一答案

更新了@Mazyod对Swift 3.0的回答,就像一个魅力!

extension UIView {

func resizeToFitSubviews() {

    let subviewsRect = subviews.reduce(CGRect.zero) {
        $0.union($1.frame)
    }

    let fix = subviewsRect.origin
    subviews.forEach {
        $0.frame.offsetBy(dx: -fix.x, dy: -fix.y)
        }

        frame.offsetBy(dx: fix.x, dy: fix.y)
        frame.size = subviewsRect.size
    }
}
另一答案

旧问题,但您也可以使用递归函数执行此操作。

无论有多少子视图和子子视图,您可能想要一个始终有效的解决方案......

更新:上一段代码只有一个getter函数,现在也是一个setter。

extension UIView {

    func setCGRectUnionWithSubviews() {
        frame = getCGRectUnionWithNestedSubviews(subviews: subviews, frame: frame)
        fixPositionOfSubviews(subviews, frame: frame)
    }

    func getCGRectUnionWithSubviews() -> CGRect {
        return getCGRectUnionWithNestedSubviews(subviews: subviews, frame: frame)
    }

    private func getCGRectUnionWithNestedSubviews(subviews subviews_I: [UIView], frame frame_I: CGRect) -> CGRect {

        var rectUnion : CGRect = frame_I
        for subview in subviews_I {
            rectUnion = CGRectUnion(rectUnion, getCGRectUnionWithNestedSubviews(subviews: subview.subviews, frame: subview.frame))
        }
        return rectUnion
    }

    private func fixPositionOfSubviews(subviews: [UIView], frame frame_I: CGRect) {

        let frameFix : CGPoint = frame_I.origin
        for subview in subviews {
            subview.frame = CGRectOffset(subview.frame, -frameFix.x, -frameFix.y)
        }
    }
}
另一答案

无论动态添加所有这些子视图的模块都必须知道放置它们的位置(因此它们正确关联,或者它们不重叠等)。一旦你知道了,加上当前视图的大小,加上大小的在子视图中,您可以确定是否需要修改封闭视图。

另一答案

这是一个接受答案的快速版本,也是一个小变化,而不是扩展它,这个方法将视图作为一个变量并返回它。

func resizeToFitSubviews(#view: UIView) -> UIView {
    var width: CGFloat = 0
    var height: CGFloat = 0

    for someView in view.subviews {
        var aView = someView as UIView
        var newWidth = aView.frame.origin.x + aView.frame.width
        var newHeight = aView.frame.origin.y + aView.frame.height
        width = max(width, newWidth)
        height = max(height, newHeight)
 

以上是关于iPhone UIView - 调整框架以适应子视图的主要内容,如果未能解决你的问题,请参考以下文章

无法让 UIImageView 调整大小以适应屏幕

调整 UIView 的大小以适应 CGPath

UIView - 调整大小以适应内容

调整 UIView 的大小以适应 uiScrollView 中的所有内容

UIView 未调整大小以填充 iPhone 6 窗口

将 UITableView 大小调整为 iPhone 5/5s