【发布时间】:2011-04-06 01:05:12
【问题描述】:
在你添加了子视图之后,是否应该有一种方法来调整 UIView 的框架,以便框架是包含所有子视图所需的大小?如果您的子视图是动态添加的,您如何确定容器视图的框架需要多大的尺寸?这不起作用:
[myView sizeToFit];
【问题讨论】:
在你添加了子视图之后,是否应该有一种方法来调整 UIView 的框架,以便框架是包含所有子视图所需的大小?如果您的子视图是动态添加的,您如何确定容器视图的框架需要多大的尺寸?这不起作用:
[myView sizeToFit];
【问题讨论】:
动态添加所有这些子视图的任何模块都必须知道将它们放在哪里(以便它们正确关联,或者它们不会重叠等)一旦你知道了,加上当前视图的大小,加上子视图的大小,您可以确定是否需要修改封闭视图。
【讨论】:
[myView sizeToFit];
应该可以,为什么不检查前后的CGRect?
【讨论】:
查看Having trouble getting UIView sizeToFit to do anything meaningful
要点是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
【讨论】:
tmpFrame = CGRectUnion(tmpFrame, v.frame),其中 tmpFrame 将在 forloop 之前声明为 CGRectZero
上面的Kenny 's answer 似乎指向referenced question 中的正确解决方案,但可能带走了错误的概念。 UIView class reference 绝对建议使用一个系统来使 sizeToFit 与您的自定义视图相关。
sizeThatFits,而不是sizeToFit
您的自定义 UIViews 需要覆盖 sizeThatFits 以返回“适合接收者子视图的新大小”,但是您希望计算这一点。您甚至可以使用来自 another answer 的数学来确定您的新尺寸(但无需重新创建内置的 sizeToFit 系统)。
在sizeThatFits 返回与其状态相关的数字后,对自定义视图的sizeToFit 调用将开始导致预期的大小调整。
sizeThatFits 的工作原理如果没有覆盖,sizeThatFits 只会返回传入的大小参数(对于来自 sizeToFit 的调用,默认为 self.bounds.size。虽然我只有一个 couple sources 关于这个问题,但似乎传入的大小不应被视为严格要求。
[
sizeThatFits] 返回适合传递给它的约束的控件的“最合适”大小。如果无法满足约束,则方法可以(强调他们的)决定忽略约束。
【讨论】:
我需要拟合子视图有一个负原点,CGRectUnion 是 ObjC 的做法,老实说,正如 cmets 中有人提到的那样。首先,让我们看看它是如何工作的:
正如你在下面看到的,我们假设一些子视图位于外面,所以我们需要做两件事来让它看起来不错,而不影响子视图的定位:
一张图片胜过一千个字。
代码价值十亿字。这是解决方案:
@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) }
【讨论】:
这是已接受答案的 swift 版本,也是小改动,此方法不是扩展它,而是将视图作为变量获取并返回。
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
}
这是扩展版本:
/// 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)
}
【讨论】:
老问题,但您也可以使用递归函数来做到这一点。
您可能想要一个无论有多少子视图和子视图都始终有效的解决方案,...
更新:之前的代码只有一个 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)
}
}
}
【讨论】:
UIView 和CALayer 之间共享此代码时才有用。如果是UIView 子类型,只需使用没有协议的普通扩展即可。
更新了@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
}
}
【讨论】:
Result of call to 'offsetBy(dx:dy:)' is unused 的警告静音,我更新了函数以在违规行的开头使用_ =
当您创建自己的 UIView 类时,请考虑在子类中覆盖 IntrinsicContentSize。操作系统调用此属性以了解建议的视图大小。我将代码 sn-p 用于 C# (Xamarin),但想法是一样的:
[Register("MyView")]
public class MyView : UIView
{
public MyView()
{
Initialize();
}
public MyView(RectangleF bounds) : base(bounds)
{
Initialize();
}
Initialize()
{
// add your subviews here.
}
public override CGSize IntrinsicContentSize
{
get
{
return new CGSize(/*The width as per your design*/,/*The height as per your design*/);
}
}
}
这个返回的大小完全取决于您的设计。例如,如果您刚刚添加了一个标签,那么宽度和高度就是该标签的宽度和高度。如果您有更复杂的视图,则需要返回适合所有子视图的大小。
【讨论】:
虽然已经有很多答案,但没有一个适用于我的用例。如果您使用 autoresizingMasks 并希望调整视图大小并移动子视图,以便矩形的大小与子视图匹配。
public extension UIView {
func resizeToFitSubviews() {
var x = width
var y = height
var rect = CGRect.zero
subviews.forEach { subview in
rect = rect.union(subview.frame)
x = subview.frame.x < x ? subview.frame.x : x
y = subview.frame.y < y ? subview.frame.y : y
}
var masks = [UIView.AutoresizingMask]()
subviews.forEach { (subview: UIView) in
masks.add(subview.autoresizingMask)
subview.autoresizingMask = []
subview.frame = subview.frame.offsetBy(dx: -x, dy: -y)
}
rect.size.width -= x
rect.size.height -= y
frame.size = rect.size
subviews.enumerated().forEach { index, subview in
subview.autoresizingMask = masks[index]
}
}
}
【讨论】: