Как изменить суперпредставление, чтобы оно соответствовало всем подпредставлениям с autolayout?

Мое понимание autolayout состоит в том, что он принимает размер суперпредставления и базируется на ограничениях и внутренних размерах, которые он вычисляет позиции подпредставлений.

Есть ли способ обратить этот процесс вспять? Я хочу изменить размер суперпредставления на основе ограничений и внутренних размеров. Какой самый простой способ добиться этого?

У меня есть вид, разработанный в Xcode, который я использую в качестве заголовка для UITableView, Это представление включает в себя метку и кнопку. Размер этикетки различается в зависимости от данных. В зависимости от ограничений метка успешно нажимает кнопку вниз или, если между кнопкой и нижней частью суперпредставления есть ограничение, метка сжимается.

Я нашел несколько похожих вопросов, но у них нет хороших и простых ответов.

4 ответа

Решение

Правильный API для использования UIView systemLayoutSizeFittingSize:Проходя либо UILayoutFittingCompressedSize или же UILayoutFittingExpandedSize,

Для нормального UIView используя autolayout, это должно работать, пока ваши ограничения верны. Если вы хотите использовать его на UITableViewCell (чтобы определить высоту строки, например), тогда вы должны вызвать его против вашей ячейки contentView и захватить высоту.

Дополнительные соображения существуют, если у вас есть один или несколько UILabel по вашему мнению, которые являются многострочными. Для них непереносимо, что preferredMaxLayoutWidth свойство должно быть установлено правильно, так что метка обеспечивает правильный intrinsicContentSize, который будет использоваться в systemLayoutSizeFittingSize's расчет.

РЕДАКТИРОВАТЬ: по запросу, добавив пример расчета высоты для ячейки табличного представления

Использование autolayout для расчета высоты ячейки таблицы не очень эффективно, но, безусловно, удобно, особенно если у вас есть ячейка со сложным макетом.

Как я уже говорил выше, если вы используете мультилинию 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

И вот картина ограничений, определенных в раскадровке. Обратите внимание, что на метке нет ограничений по высоте / ширине - они выведены из метки intrinsicContentSize:

Комментарий Эрика Бейкера подсказал мне основную идею о том, что для того, чтобы размер представления был определен содержимым, помещенным в него, тогда содержимое, помещенное в него, должно иметь явную связь с содержащим представлением, чтобы поднять его высоту. (или ширина) динамически. "Добавить подпредставление" не создает эти отношения, как вы могли бы предположить. Вы должны выбрать, какое подпредставление будет определять высоту и / или ширину контейнера... чаще всего любой элемент пользовательского интерфейса, который вы поместили в нижнем правом углу вашего общего пользовательского интерфейса. Вот некоторый код и встроенные комментарии, чтобы проиллюстрировать это.

Обратите внимание, что это может иметь особое значение для тех, кто работает с представлениями прокрутки, так как принято проектировать вокруг одного представления содержимого, которое определяет его размер (и передает это представлению прокрутки) динамически на основе того, что вы в него вставили. Удачи, надеюсь, это поможет кому-то там.

//
//  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

Как изменить суперпредставление, чтобы оно соответствовало всем подпредставлениям с autolayout.png

Вы можете сделать это, создав ограничение и подключив его через конструктор интерфейса

См. Объяснение: http://www.techotopia.com/index.php/Working_with_iOS_6_Auto_Layout_Constraints_in_Interface_Builder

raywenderlich начало авто-макета

AutolayoutPG Статьи ограничение Основы

@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

Я надеюсь, что это проясняет это.

Это можно сделать для нормального subview внутри большего UIView, но это не работает автоматически для headerViews, Высота headerView определяется тем, что возвращается tableView:heightForHeaderInSection: так что вы должны рассчитать height на основе height из UILabel плюс место для UIButton и любой padding тебе нужно. Вам нужно сделать что-то вроде этого:

-(CGFloat)tableView:(UITableView *)tableView 
          heightForHeaderInSection:(NSInteger)section {
    NSString *s = self.headeString[indexPath.section];
    CGSize size = [s sizeWithFont:[UIFont systemFontOfSize:17] 
                constrainedToSize:CGSizeMake(281, CGFLOAT_MAX)
                    lineBreakMode:NSLineBreakByWordWrapping];
    return size.height + 60;
}

Вот headerString это любая строка, которую вы хотите заполнить UILabelи число 281 является width из UILabel (как настроено в Interface Builder)

Другие вопросы по тегам