iphone uiview – 调整框架,以适应子视图

在添加子视图之后,是不是应该调整UIView框架的大小,以便框架是包含所有子视图所需的大小? 如果您的子视图是dynamic添加的,那么您如何确定容器视图框架需要的大小? 这不起作用:

[myView sizeToFit]; 

退房遇到困难得到UIView sizeToFit做任何有意义的事情

要点是, sizeToFit是子类重写,并不做任何事情在UIView。 它做UILabel东西,因为它重写sizeThatFits:这是由sizeToFit

你也可以添加下面的代码来计算子视图的位置。

 [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. 将视图的框架移动到最左上angular的位置
  2. 移动子视图相反的方向来否定效果。

一张图片胜过千言万语。

示范

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

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

操场certificate:

请注意, visualAidView不会移动,并帮助您在维护子视图位置的同时查看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的回答上面指出了所引用问题中的正确解决scheme,但可能带走了错误的概念。 UIView类的参考绝对build议一个制作sizeToFit与您的自定义视图相关的系统。

覆盖sizeThatFits ,而不是sizeToFit

您的自定义UIView需要覆盖sizeThatFits返回“一个新的大小,适合接收器的子视图”,但是你想计算这个。 你甚至可以使用另一个答案的math来确定你的新的大小(但不重新创build内置的sizeToFit系统)。

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

sizeThatFits如何工作

如果没有覆盖, sizeThatFits只是返回传入的大小参数(默认为self.bounds.size调用self.bounds.size调用。虽然我只有几个 来源的问题,似乎传入的大小不会被看到作为一个严格的要求。

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

老问题,但你也可以做一个recursion函数。

您可能需要一个解决scheme,无论有多less个子视图和子视图,总能正常工作…

更新:以前的一段代码只有一个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) } } } 

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

这里是接受的答案的一个迅速的版本,也是一个小的改变,而不是扩展它的方法获取作为variables的视图,并返回它。

 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) } view.frame = CGRect(x: view.frame.origin.x, y: view.frame.origin.y, width: width, height: height) return view } 

inheritance人的延伸版本:

 /// Extension, resizes this view so it fits the largest subview func resizeToFitSubviews() { var width: CGFloat = 0 var height: CGFloat = 0 for someView in self.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) } frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: width, height: height) } 

更新@ Mazyod的答案迅速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 } } 
 [myView sizeToFit]; 

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