web-dev-qa-db-ja.com

自動レイアウトですべてのサブビューに合うようにスーパービューのサイズを変更する方法は?

自動レイアウトの私の理解は、スーパービューのサイズと、サブビューの位置を計算する制約と固有のサイズに基づいていることです。

このプロセスを逆にする方法はありますか?制約と固有のサイズに基づいてスーパービューのサイズを変更します。これを達成する最も簡単な方法は何ですか?

UITableViewのヘッダーとして使用するXcodeで設計されたビューがあります。このビューには、ラベルとボタンが含まれます。ラベルのサイズはデータによって異なります。制約に応じて、ラベルはボタンを正常に押し下げます。または、ボタンとスーパービューの下部に制約がある場合、ラベルは圧縮されます。

似たような質問をいくつか見つけましたが、うまく簡単に答えられません。

140
DAK

使用する正しいAPIはUIView systemLayoutSizeFittingSize:で、UILayoutFittingCompressedSizeまたはUILayoutFittingExpandedSizeを渡します。

Autolayoutを使用する通常のUIViewの場合、これは制約が正しい限り機能するはずです。 (たとえば、行の高さを決定するために)UITableViewCellで使用する場合は、セルcontentViewに対して呼び出して、高さを取得する必要があります。

ビューに複数行のUILabelが1つ以上ある場合は、さらに考慮する必要があります。これらの場合、ラベルがsystemLayoutSizeFittingSize's計算で使用される正しいpreferredMaxLayoutWidthを提供するように、intrinsicContentSizeプロパティを正しく設定することが不可欠です。

編集:リクエストにより、テーブルビューセルの高さ計算の例を追加

テーブルセルの高さの計算に自動レイアウトを使用することは非常に効率的ではありませんが、特に複雑なレイアウトのセルがある場合は便利です。

上記で述べたように、複数行のUILabelを使用している場合は、preferredMaxLayoutWidthをラベル幅に同期することが不可欠です。これを行うには、カスタムUILabelサブクラスを使用します。

@implementation TSLabel

- (void) layoutSubviews
{
    [super layoutSubviews];

    if ( self.numberOfLines == 0 )
    {
        if ( self.preferredMaxLayoutWidth != self.frame.size.width )
        {
            self.preferredMaxLayoutWidth = self.frame.size.width;
            [self setNeedsUpdateConstraints];
        }
    }
}

- (CGSize) intrinsicContentSize
{
    CGSize s = [super intrinsicContentSize];

    if ( self.numberOfLines == 0 )
    {
        // found out that sometimes intrinsicContentSize is 1pt too short!
        s.height += 1;
    }

    return s;
}

@end

HeightForRowAtIndexPathを示す、不自然なUITableViewControllerサブクラスを次に示します。

#import "TSTableViewController.h"
#import "TSTableViewCell.h"

@implementation TSTableViewController

- (NSString*) cellText
{
    return @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
}

#pragma mark - Table view data source

- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView
{
    return 1;
}

- (NSInteger) tableView: (UITableView *)tableView numberOfRowsInSection: (NSInteger) section
{
    return 1;
}

- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
{
    static TSTableViewCell *sizingCell;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        sizingCell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell"];
    });

    // configure the cell
    sizingCell.text = self.cellText;

    // force layout
    [sizingCell setNeedsLayout];
    [sizingCell layoutIfNeeded];

    // get the fitting size
    CGSize s = [sizingCell.contentView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];
    NSLog( @"fittingSize: %@", NSStringFromCGSize( s ));

    return s.height;
}

- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
{
    TSTableViewCell *cell = (TSTableViewCell*)[tableView dequeueReusableCellWithIdentifier: @"TSTableViewCell" ];

    cell.text = self.cellText;

    return cell;
}

@end

シンプルなカスタムセル:

#import "TSTableViewCell.h"
#import "TSLabel.h"

@implementation TSTableViewCell
{
    IBOutlet TSLabel* _label;
}

- (void) setText: (NSString *) text
{
    _label.text = text;
}

@end

そして、これがストーリーボードで定義された制約の写真です。ラベルには高さ/幅の制約がないことに注意してください-これらはラベルのintrinsicContentSizeから推測されます:

enter image description here

148
TomSwift

Eric Bakerのコメントはビューのサイズをその中に置かれたコンテンツによって決定するために、その中に置かれたコンテンツは含まれているビューと順番に明示的な関係を持たなければならないという中核的なアイデアに私を導きましたその高さ(または幅)を動的に駆動するには。 「サブビューの追加」では、想定されるようにこの関係は作成されません。どのサブビューがコンテナの高さや幅を駆動するかを選択する必要があります...最も一般的なのは、UI全体の右下隅に配置したUI要素です。ポイントを説明するためのコードとインラインコメントを次に示します。

これは、スクロールビューで作業する人にとって特に価値があるかもしれないことに注意してください。サイズを決定する(そしてスクロールビューにこれを伝える)1つのコンテンツビューを中心に設計するのが一般的です。幸運を祈ります。これが誰かの助けになることを願っています。

//
//  ViewController.m
//  AutoLayoutDynamicVerticalContainerHeight
//

#import "ViewController.h"

@interface ViewController ()
@property (strong, nonatomic) UIView *contentView;
@property (strong, nonatomic) UILabel *myLabel;
@property (strong, nonatomic) UILabel *myOtherLabel;
@end

@implementation ViewController

- (void)viewDidLoad
{
    // INVOKE SUPER
    [super viewDidLoad];

    // INIT ALL REQUIRED UI ELEMENTS
    self.contentView = [[UIView alloc] init];
    self.myLabel = [[UILabel alloc] init];
    self.myOtherLabel = [[UILabel alloc] init];
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_contentView, _myLabel, _myOtherLabel);

    // TURN AUTO LAYOUT ON FOR EACH ONE OF THEM
    self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
    self.myLabel.translatesAutoresizingMaskIntoConstraints = NO;
    self.myOtherLabel.translatesAutoresizingMaskIntoConstraints = NO;

    // ESTABLISH VIEW HIERARCHY
    [self.view addSubview:self.contentView]; // View adds content view
    [self.contentView addSubview:self.myLabel]; // Content view adds my label (and all other UI... what's added here drives the container height (and width))
    [self.contentView addSubview:self.myOtherLabel];

    // LAYOUT

    // Layout CONTENT VIEW (Pinned to left, top. Note, it expects to get its vertical height (and horizontal width) dynamically based on whatever is placed within).
    // Note, if you don't want horizontal width to be driven by content, just pin left AND right to superview.
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_contentView]" options:0 metrics:0 views:viewsDictionary]]; // Only pinned to left, no horizontal width yet
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_contentView]" options:0 metrics:0 views:viewsDictionary]]; // Only pinned to top, no vertical height yet

    /* WHATEVER WE ADD NEXT NEEDS TO EXPLICITLY "Push OUT ON" THE CONTAINING CONTENT VIEW SO THAT OUR CONTENT DYNAMICALLY DETERMINES THE SIZE OF THE CONTAINING VIEW */
    // ^To me this is what's weird... but okay once you understand...

    // Layout MY LABEL (Anchor to upper left with default margin, width and height are dynamic based on text, font, etc (i.e. UILabel has an intrinsicContentSize))
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_myLabel]" options:0 metrics:0 views:viewsDictionary]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_myLabel]" options:0 metrics:0 views:viewsDictionary]];

    // Layout MY OTHER LABEL (Anchored by vertical space to the sibling label that comes before it)
    // Note, this is the view that we are choosing to use to drive the height (and width) of our container...

    // The LAST "|" character is KEY, it's what drives the WIDTH of contentView (red color)
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_myOtherLabel]-|" options:0 metrics:0 views:viewsDictionary]];

    // Again, the LAST "|" character is KEY, it's what drives the HEIGHT of contentView (red color)
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_myLabel]-[_myOtherLabel]-|" options:0 metrics:0 views:viewsDictionary]];

    // COLOR VIEWS
    self.view.backgroundColor = [UIColor purpleColor];
    self.contentView.backgroundColor = [UIColor redColor];
    self.myLabel.backgroundColor = [UIColor orangeColor];
    self.myOtherLabel.backgroundColor = [UIColor greenColor];

    // CONFIGURE VIEWS

    // Configure MY LABEL
    self.myLabel.text = @"HELLO WORLD\nLine 2\nLine 3, yo";
    self.myLabel.numberOfLines = 0; // Let it flow

    // Configure MY OTHER LABEL
    self.myOtherLabel.text = @"My OTHER label... This\nis the UI element I'm\narbitrarily choosing\nto drive the width and height\nof the container (the red view)";
    self.myOtherLabel.numberOfLines = 0;
    self.myOtherLabel.font = [UIFont systemFontOfSize:21];
}

@end

How to resize superview to fit all subviews with autolayout.png

29
John Erck

これを行うには、制約を作成し、インターフェイスビルダー経由で接続します。

説明を参照してください: Auto_Layout_Constraints_in_Interface_Builder

raywenderlich starting-auto-layout

AutolayoutPG Articles constraint Fundamentals

@interface ViewController : UIViewController {
    IBOutlet NSLayoutConstraint *leadingSpaceConstraint;
    IBOutlet NSLayoutConstraint *topSpaceConstraint;
}
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leadingSpaceConstraint;

このConstraintアウトレットをサブビューConstraintに接続するか、スーパービューConstraintも接続して、このような要件に従って設定します

 self.leadingSpaceConstraint.constant = 10.0;//whatever you want to assign

これで明らかになることを願っています。

3
chandan