在将视图添加到视图树之前如何计算 UIViewController 高度?
How to calculate UIViewController height before adding view to view tree?
我需要测量给定宽度的基于 VC 的简单自动布局的高度。 例如一个简单的UIViewController
,只有一个标签,使用前导、尾随、顶部和底部约束定位到VC的根视图。 VC 应该 而不是 具有固定大小但会自动适应标签内容。
这只是一个例子,当然VC可以有其他影响大小的内容。
如何在不将给定宽度和标签内容添加到视图层次结构的情况下计算 VCs 大小?
背景:
我正在使用第三方 FormSheet 控件,它可以轻松地将任何 ViewController 显示为具有不同样式、过渡等的表单 sheet。唯一的缺点是,必须指定一个固定 sheet 尺寸 在 VC 出现之前。
虽然这对于具有 "static" 内容/固定大小的 VC 非常有效,但即使是针对不同语言使用不同文本的标签也可能会破坏设计。
因此我正在寻找这样的东西:
ContentViewController *contentVC = [ContentViewController new];
CGRect contentBounds = [SomeClass calculateAutoLayoutHeightForView:contentVC.view withFixedWidth:500];
[ThirPartyFormSheetController presentController:contentVC withSize:contentBounds];
如何实现?
Swift:
如果你只需要根据标签的文本计算高度,你可以使用这个解决方案
给定宽度,您可以使用 systemLayoutSizeFittingSize:UILayoutFittingCompressedSize
确定自动布局完成后的高度。
假设 view-to-show 中的约束设置正确:
CGFloat w = 500.0;
[loadedView.widthAnchor constraintEqualToConstant:w].active = YES;
// caluclate the size using FittingCompressedSize
CGSize contentBounds = [loadedView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
这是一个简单的例子(只需要将 ViewController
class 分配给 Storyboard 中的视图控制器...不需要 IBOutlets)。代码中的大量注释应该使一切都清楚:
//
// ViewController.h
// Created by Don Mag on 4/8/19.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//
// ViewController.m
// Created by Don Mag on 4/8/19.
//
#import "ViewController.h"
#import "FormViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// add a button we'll use to show the form VC
UIButton *b = [UIButton new];
b.translatesAutoresizingMaskIntoConstraints = NO;
[b setTitle:@"Show Form" forState:UIControlStateNormal];
[b setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[b setBackgroundColor:[UIColor redColor]];
[self.view addSubview:b];
[NSLayoutConstraint activateConstraints:
@[
[b.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:20.0],
[b.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:0.75],
[b.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor]
]
];
[b addTarget:self action:@selector(loadAndShowForm:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) loadAndShowForm:(id)sender {
// instantiate the form view controller
FormViewController *vc = [FormViewController new];
// get a reference to its view
UIView *v = vc.view;
// use auto-layout
v.translatesAutoresizingMaskIntoConstraints = NO;
// set the label text in the form view
vc.topLabel.text = @"This is a bunch of text for the TOP label in the Form VC";
vc.bottomLabel.text = @"This is a bunch of text for the BOTTOM label in the Form VC. It's enough text to cause a few lines of word-wrap, assuming we're running on an iPhone.";
// specify a width for the form view
// we'll use width of current view minus 60 (30-pts on each side)
CGFloat w = self.view.frame.size.width - 60.0;
[v.widthAnchor constraintEqualToConstant:w].active = YES;
// caluclate the size using FittingCompressedSize
CGSize contentBounds = [v systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
// because we set the width constraint, we now have the "compressed" height
//[ThirdPartyFormSheetController presentController:contentVC withSize:contentBounds];
// debugging from here down
NSLog(@"Auto-layout resulting size: %@", [NSValue valueWithCGSize:contentBounds]);
// set the height for the form view
[v.heightAnchor constraintEqualToConstant:contentBounds.height].active = YES;
// add it to the view, so we can confirm the height calculation
[self.view addSubview:v];
// center it on the view
[NSLayoutConstraint activateConstraints:
@[
[v.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[v.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor]
]
];
}
@end
//
// FormViewController.h
// Created by Don Mag on 4/8/19.
//
#import <UIKit/UIKit.h>
@interface FormViewController : UIViewController
@property (strong, nonatomic) UILabel *topLabel;
@property (strong, nonatomic) UITextField *theTextField;
@property (strong, nonatomic) UILabel *bottomLabel;
@end
//
// FormViewController.m
// Created by Don Mag on 4/8/19.
//
#import "FormViewController.h"
@interface FormViewController ()
@end
@implementation FormViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
// create a multi-line "top label"
_topLabel = [UILabel new];
_topLabel.backgroundColor = [UIColor cyanColor];
_topLabel.text = @"Hello Top Label";
_topLabel.numberOfLines = 0;
// create a text field
_theTextField = [UITextField new];
_theTextField.backgroundColor = [UIColor greenColor]; // just to make it easy to see
_theTextField.borderStyle = UITextBorderStyleRoundedRect;
_theTextField.text = @"The Text Field";
// create a multi-line "bottom label"
_bottomLabel = [UILabel new];
_bottomLabel.backgroundColor = [UIColor cyanColor];
_bottomLabel.text = @"Hello Bottom Label";
_bottomLabel.numberOfLines = 0;
// we're using auto-layout and constraints
_topLabel.translatesAutoresizingMaskIntoConstraints = NO;
_theTextField.translatesAutoresizingMaskIntoConstraints = NO;
_bottomLabel.translatesAutoresizingMaskIntoConstraints = NO;
// add to view
[self.view addSubview:_topLabel];
[self.view addSubview:_theTextField];
[self.view addSubview:_bottomLabel];
// these elements and constraints will define the height of the content
[NSLayoutConstraint activateConstraints:
@[
// constrain top label leading, trailing and top to top of view, all at 20-pts
[_topLabel.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20.0],
[_topLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
[_topLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0],
// constrain text field leading and trailing, and top to bottom of top label, all at 20-pts
[_theTextField.topAnchor constraintEqualToAnchor:_topLabel.bottomAnchor constant:20.0],
[_theTextField.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
[_theTextField.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0],
// constrain bottom label leading, trailing and top to bottom of text field, all at 20-pts
[_bottomLabel.topAnchor constraintEqualToAnchor:_theTextField.bottomAnchor constant:20.0],
[_bottomLabel.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20.0],
[_bottomLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
// AND constrain bottom label to bottom of view at 20-pts
[_bottomLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0]
]
];
}
@end
结果(将加载的 VC 的视图添加为子视图 - 参见代码中的注释):
并用更多文字显示自动高度计算:
如果更改标签的文本量(在 ViewController.m
中设置),您会发现高度计算正确。
我需要测量给定宽度的基于 VC 的简单自动布局的高度。 例如一个简单的UIViewController
,只有一个标签,使用前导、尾随、顶部和底部约束定位到VC的根视图。 VC 应该 而不是 具有固定大小但会自动适应标签内容。
这只是一个例子,当然VC可以有其他影响大小的内容。
如何在不将给定宽度和标签内容添加到视图层次结构的情况下计算 VCs 大小?
背景:
我正在使用第三方 FormSheet 控件,它可以轻松地将任何 ViewController 显示为具有不同样式、过渡等的表单 sheet。唯一的缺点是,必须指定一个固定 sheet 尺寸 在 VC 出现之前。
虽然这对于具有 "static" 内容/固定大小的 VC 非常有效,但即使是针对不同语言使用不同文本的标签也可能会破坏设计。
因此我正在寻找这样的东西:
ContentViewController *contentVC = [ContentViewController new];
CGRect contentBounds = [SomeClass calculateAutoLayoutHeightForView:contentVC.view withFixedWidth:500];
[ThirPartyFormSheetController presentController:contentVC withSize:contentBounds];
如何实现?
Swift: 如果你只需要根据标签的文本计算高度,你可以使用这个解决方案
给定宽度,您可以使用 systemLayoutSizeFittingSize:UILayoutFittingCompressedSize
确定自动布局完成后的高度。
假设 view-to-show 中的约束设置正确:
CGFloat w = 500.0;
[loadedView.widthAnchor constraintEqualToConstant:w].active = YES;
// caluclate the size using FittingCompressedSize
CGSize contentBounds = [loadedView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
这是一个简单的例子(只需要将 ViewController
class 分配给 Storyboard 中的视图控制器...不需要 IBOutlets)。代码中的大量注释应该使一切都清楚:
//
// ViewController.h
// Created by Don Mag on 4/8/19.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//
// ViewController.m
// Created by Don Mag on 4/8/19.
//
#import "ViewController.h"
#import "FormViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// add a button we'll use to show the form VC
UIButton *b = [UIButton new];
b.translatesAutoresizingMaskIntoConstraints = NO;
[b setTitle:@"Show Form" forState:UIControlStateNormal];
[b setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[b setBackgroundColor:[UIColor redColor]];
[self.view addSubview:b];
[NSLayoutConstraint activateConstraints:
@[
[b.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:20.0],
[b.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:0.75],
[b.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor]
]
];
[b addTarget:self action:@selector(loadAndShowForm:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) loadAndShowForm:(id)sender {
// instantiate the form view controller
FormViewController *vc = [FormViewController new];
// get a reference to its view
UIView *v = vc.view;
// use auto-layout
v.translatesAutoresizingMaskIntoConstraints = NO;
// set the label text in the form view
vc.topLabel.text = @"This is a bunch of text for the TOP label in the Form VC";
vc.bottomLabel.text = @"This is a bunch of text for the BOTTOM label in the Form VC. It's enough text to cause a few lines of word-wrap, assuming we're running on an iPhone.";
// specify a width for the form view
// we'll use width of current view minus 60 (30-pts on each side)
CGFloat w = self.view.frame.size.width - 60.0;
[v.widthAnchor constraintEqualToConstant:w].active = YES;
// caluclate the size using FittingCompressedSize
CGSize contentBounds = [v systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
// because we set the width constraint, we now have the "compressed" height
//[ThirdPartyFormSheetController presentController:contentVC withSize:contentBounds];
// debugging from here down
NSLog(@"Auto-layout resulting size: %@", [NSValue valueWithCGSize:contentBounds]);
// set the height for the form view
[v.heightAnchor constraintEqualToConstant:contentBounds.height].active = YES;
// add it to the view, so we can confirm the height calculation
[self.view addSubview:v];
// center it on the view
[NSLayoutConstraint activateConstraints:
@[
[v.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[v.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor]
]
];
}
@end
//
// FormViewController.h
// Created by Don Mag on 4/8/19.
//
#import <UIKit/UIKit.h>
@interface FormViewController : UIViewController
@property (strong, nonatomic) UILabel *topLabel;
@property (strong, nonatomic) UITextField *theTextField;
@property (strong, nonatomic) UILabel *bottomLabel;
@end
//
// FormViewController.m
// Created by Don Mag on 4/8/19.
//
#import "FormViewController.h"
@interface FormViewController ()
@end
@implementation FormViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
// create a multi-line "top label"
_topLabel = [UILabel new];
_topLabel.backgroundColor = [UIColor cyanColor];
_topLabel.text = @"Hello Top Label";
_topLabel.numberOfLines = 0;
// create a text field
_theTextField = [UITextField new];
_theTextField.backgroundColor = [UIColor greenColor]; // just to make it easy to see
_theTextField.borderStyle = UITextBorderStyleRoundedRect;
_theTextField.text = @"The Text Field";
// create a multi-line "bottom label"
_bottomLabel = [UILabel new];
_bottomLabel.backgroundColor = [UIColor cyanColor];
_bottomLabel.text = @"Hello Bottom Label";
_bottomLabel.numberOfLines = 0;
// we're using auto-layout and constraints
_topLabel.translatesAutoresizingMaskIntoConstraints = NO;
_theTextField.translatesAutoresizingMaskIntoConstraints = NO;
_bottomLabel.translatesAutoresizingMaskIntoConstraints = NO;
// add to view
[self.view addSubview:_topLabel];
[self.view addSubview:_theTextField];
[self.view addSubview:_bottomLabel];
// these elements and constraints will define the height of the content
[NSLayoutConstraint activateConstraints:
@[
// constrain top label leading, trailing and top to top of view, all at 20-pts
[_topLabel.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20.0],
[_topLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
[_topLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0],
// constrain text field leading and trailing, and top to bottom of top label, all at 20-pts
[_theTextField.topAnchor constraintEqualToAnchor:_topLabel.bottomAnchor constant:20.0],
[_theTextField.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
[_theTextField.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0],
// constrain bottom label leading, trailing and top to bottom of text field, all at 20-pts
[_bottomLabel.topAnchor constraintEqualToAnchor:_theTextField.bottomAnchor constant:20.0],
[_bottomLabel.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20.0],
[_bottomLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
// AND constrain bottom label to bottom of view at 20-pts
[_bottomLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0]
]
];
}
@end
结果(将加载的 VC 的视图添加为子视图 - 参见代码中的注释):
并用更多文字显示自动高度计算:
如果更改标签的文本量(在 ViewController.m
中设置),您会发现高度计算正确。