1. ホーム
  2. ios

[解決済み] autolayoutですべてのサブビューに合うようにスーパービューのサイズを変更する方法?

2022-05-08 19:02:58

質問

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

このプロセスを逆転させる方法はありますか?私は、制約と本質的なサイズに基づいてスーパービューのサイズを変更したいです。これを実現する最も簡単な方法は何でしょうか?

Xcodeでデザインしたビューがあり、それをヘッダーとして UITableView . このビューには、ラベルとボタンが含まれています。ラベルの大きさは、データによって異なります。制約によって、ラベルがボタンを押し下げることに成功するか、ボタンとスーパービューの下部の間に制約がある場合、ラベルは圧縮されます。

同じような質問をいくつか見つけたのですが、良い簡単な答えがありません。

どのように解決するのですか?

使用する正しいAPIは UIView systemLayoutSizeFittingSize: のどちらかを渡します。 UILayoutFittingCompressedSize または UILayoutFittingExpandedSize .

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

ビューに1つ以上のUILabelがあり、それが複数行にわたる場合、さらなる考慮事項があります。 このような場合 preferredMaxLayoutWidth プロパティが正しく設定され、ラベルが正しい intrinsicContentSize で使用されます。 systemLayoutSizeFittingSize's を計算します。

EDIT: ご要望により、テーブルビューセルの高さ計算の例を追加しました。

テーブルセルの高さ計算にオートレイアウトを使用すると、超効率的ではありませんが、特に複雑なレイアウトを持つセルがある場合、確かに便利です。

上で述べたように、複数行の 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

UITableViewController のサブクラスで heightForRowAtIndexPath を実演しています。

#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

そして、Storyboardで定義された制約のイメージです。 ラベルに高さ/幅の制約がないことに注意してください - これらは、ラベルの intrinsicContentSize :