自動レイアウトの私の理解は、スーパービューのサイズと、サブビューの位置を計算する制約と固有のサイズに基づいていることです。
このプロセスを逆にする方法はありますか?制約と固有のサイズに基づいてスーパービューのサイズを変更します。これを達成する最も簡単な方法は何ですか?
UITableView
のヘッダーとして使用するXcodeで設計されたビューがあります。このビューには、ラベルとボタンが含まれます。ラベルのサイズはデータによって異なります。制約に応じて、ラベルはボタンを正常に押し下げます。または、ボタンとスーパービューの下部に制約がある場合、ラベルは圧縮されます。
似たような質問をいくつか見つけましたが、うまく簡単に答えられません。
使用する正しい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
から推測されます:
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
これを行うには、制約を作成し、インターフェイスビルダー経由で接続します。
説明を参照してください: 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
これで明らかになることを願っています。