【问题标题】:How to calculate UIViewController height before adding view to view tree?如何在将视图添加到视图树之前计算 UIViewController 高度?
【发布时间】:2019-08-29 16:00:14
【问题描述】:

我需要针对给定宽度测量基于简单自动布局的 VC 的高度。 例如一个简单的UIViewController,只有一个标签,使用前导、尾随、顶部和底部约束定位到 VC 根视图。 VC 应该具有固定大小,而是自动适应标签内容。

这只是一个例子,当然VC可以有其他影响大小的内容。

如何计算给定宽度和标签内容的 VC 大小而不将其添加到视图层次结构中?

背景:

我正在使用第三方 FormSheet 控件,它可以轻松地将任何 ViewController 显示为具有不同样式、过渡等的表单。唯一的缺点是,必须指定 固定的表单大小在 VC 出现之前。

虽然这对于具有“静态”内容/固定大小的 VC 非常有效,但即使是具有不同语言的不同文本的标签也可能会破坏设计。

因此我正在寻找这样的东西:

ContentViewController *contentVC = [ContentViewController new];
CGRect contentBounds = [SomeClass calculateAutoLayoutHeightForView:contentVC.view withFixedWidth:500];

[ThirPartyFormSheetController presentController:contentVC withSize:contentBounds];

如何做到这一点?

【问题讨论】:

  • 你试过sizeToFit吗?这是UIView 上的一种方法,可将标签调整为所需大小。调用后可以从frame 属性中获取结果。不确定是否可以在 VC 根视图上调用它,但它应该可以在标签视图上使用。
  • 对不起,但我不明白这与问题有什么关系 :-) 这不是关于自动调整标签大小,而是关于自动调整 VC 视图到给定宽度和标签内容。
  • @AndreiHerford - “第三方”控件不适用于自动布局?如果是这种情况,您是否想根据您自己确定的“目标宽度”来获取已加载的ContentViewController 视图的高度?您是否在ContentViewController 中正确设置了自动布局约束?
  • @AndreiHerford - 您使用的是什么“第三方表单表单”控件?
  • 我正在使用 MZFormSheetController,据我所知它只支持固定大小的表单。但是,这只是我需要以编程方式计算给定宽度的布局高度的一个用例。解决表单的问题会很好,但一般的解决方案会是完美的。

标签: ios objective-c autolayout


【解决方案1】:

给定宽度,您可以使用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 类分配给Storyboard 中的视图控制器...不需要IBOutlets)。代码中的大量 cmets 应该让一切都清楚:

//
//  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的视图添加为子视图-见代码中的cmets):

并用更多文字显示自动高度计算:

如果您更改标签的文本数量(在ViewController.m 中设置),您将看到高度计算正确。

【讨论】:

    【解决方案2】:

    斯威夫特: 如果您只需要根据标签的文本计算高度,您可以使用此解决方案

    https://stackoverflow.com/a/25187891/7848711

    【讨论】:

    • 谢谢,但这不是衡量一个标签(这只是一个例子),而是一个完整的 VC 大小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    相关资源
    最近更新 更多