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

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

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

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

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

Ответ 1

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

Для обычного UIView с использованием автозапуска это должно работать только до тех пор, пока ваши ограничения верны. Если вы хотите использовать его на 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:

enter image description here

Ответ 2

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

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

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

Ответ 3

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

См. объяснение: Auto_Layout_Constraints_in_Interface_Builder

start-auto-layout raywenderlich

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

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

подключите эту розетку Constraint к вашим подвидным объектам. Ограничьте или подключите супер-просмотр. Ограничьте и установите его в соответствии с вашими требованиями, такими как

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

Надеюсь, это прояснит это.

Ответ 4

Это можно сделать для обычного subview внутри большего UIView, но оно не работает автоматически для headerViews. Высота a headerView определяется тем, что возвращается tableView:heightForHeaderInSection:, поэтому вам нужно вычислить height на основе height пространства UILabel plus для 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)