How to calculate UIViewController height before adding view to view tree?

时间:2019-04-08 13:22:51

标签: ios objective-c autolayout

I need to measure the height of a simple autolayout based VC for a given width. For example a simple UIViewController with only one label which is positioned using leading, trailing, top and bottom constraints to the VCs root view. The VC should not have a fixed size but automatically adapt to the label content.

This is only an example, of course the VC could have other content which is influences the size.

How can I calculate the VCs size for a given width and label content without adding it to the view hierarchy?

Background:

I am using a third party FormSheet control which allows to easily show any ViewController as form sheet with different styles, transitions, etc. The only downside is, that one has to specify a fixed sheet size before the VC is presented.

While this works great for VCs with "static" content / fixed sizes even a label with different texts for different languages might break the design.

Thus I am look for something like this:

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

[ThirPartyFormSheetController presentController:contentVC withSize:contentBounds];

How can this be done?

2 个答案:

答案 0 :(得分:0)

Swift: If all you need is to calculate height on the basis of label's text, you can use this solution

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

答案 1 :(得分:0)

给出宽度,您可以使用systemLayoutSizeFittingSize:UILayoutFittingCompressedSize确定自动布局完成工作后的高度。

假设要正确显示视图中的约束,

CGFloat w = 500.0;
[loadedView.widthAnchor constraintEqualToConstant:w].active = YES;

// caluclate the size using FittingCompressedSize
CGSize contentBounds = [loadedView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

这是一个简单的示例(只需要将ViewController类分配给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的视图添加为子视图-请参见代码中的注释):

enter image description here

并显示更多文本以显示自动高度计算:

enter image description here

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