2013-08-08 2 views
133

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

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

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

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

+28

Вы должны выбрать один из приведенных ниже ответов, так как для уверен, Том Свифт ответил на ваш вопрос. Все плакаты потратили огромное количество времени, чтобы помочь вам, теперь вы должны сделать свою часть и выбрать ответ, который вам больше нравится. –

ответ

146

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

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

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

EDIT: по запросу, добавив пример расчета высоты для табличного ячейки

Использование 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

+1

Можете ли вы привести пример того, что реализация heightForRowAtIndexPath: будет выглядеть с использованием этого метода с ячейкой, содержащей многострочную метку? Я немного перепутал его и не получил работу. Как вы получаете ячейку (особенно если ячейка настроена в раскадровке)? Какие ограничения вам необходимы, чтобы заставить его работать? – rdelmar

+0

@rdelmar - уверен. Я добавил пример моего ответа. – TomSwift

+0

Хм ... это намного сложнее, чем мой путь, и я до сих пор не получил работу. Журнал fitSize всегда дает мне (0,0), и я получаю предупреждения ограничения компоновки. Если я удалю нижнее ограничение, предупреждения исчезнут, и я увижу ячейку, но если я верну 5 для числа строк, я вижу только 1. Я вижу, что у вас есть представление содержимого под ячейкой представления таблицы в вашем изображении IB, я не знаю У меня есть та - какая версия Xcode вы используете? – rdelmar

3

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

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

raywenderlich beginning-auto-layout

AutolayoutPG Articles constraint Fundamentals

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

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

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

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

+0

Пожалуйста, дайте мне знать, что это работа для вас или нет? – chandan

+0

Таким образом, можно сконфигурировать ограничения, созданные в XCode, из исходного кода. Но вопрос заключается в том, как настроить их для изменения размера супервизора. – DAK

+0

ДА @DAK. убедитесь, что вы просматриваете макет супервизора. Положите Constraint на супервизор и сделайте высоту Constraint слишком, чтобы всякий раз, когда ваш subview увеличивал его автоматически, увеличивайте высоту Constiew. – chandan

-1

Это может быть сделано для нормального 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)

+0

К сожалению, это не сработает. «Размер для соответствия содержимому» в супервизии удаляет ограничение, которое соединяет нижнюю часть кнопки с надписью, как вы предсказывали. Во время выполнения этикетка изменяется, и она нажимает кнопку вниз, но супервизор не изменяется автоматически. – DAK

+0

@DAK, извините, проблема в том, что ваше представление является заголовком для табличного представления. Я неправильно понял вашу ситуацию.Я думал, что представление, содержащее кнопку и метку, находилось внутри другого представления, но похоже, что вы используете его как заголовок (а не как представление внутри заголовка). Итак, мой оригинальный ответ не сработает. Я изменил свой ответ на то, что, как я думаю, должен работать. – rdelmar

+0

Это похоже на мою текущую реализацию, но я надеялся, что есть лучший способ. Я бы предпочел не обновлять этот код каждый раз, когда меняю заголовок. – DAK

26
комментарий

Эрика Бейкера наконечниками меня к основной идее, что для того, чтобы в целях, чтобы иметь его размер определяется содержимым, размещенным внутри него, тогда содержимое, размещенное внутри него, должно иметь явное отношение к содержащему виду, чтобы динамически его высота (или ширина) динамически составляла. «Добавить 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'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

+3

Это отличный трюк и малоизвестен. Повторить: если внутренние представления имеют внутреннюю высоту и закреплены сверху и снизу, внешний вид не должен указывать его высоту и фактически обнимает ее содержимое. Возможно, вам придется настроить сжатие содержимого и обнимать внутренние представления, чтобы получить желаемые результаты. – phatmann

+0

Это деньги! Один из лучших лаконичных примеров VFL, которые я видел. –

+0

Это то, что я искал (спасибо @John), поэтому я опубликовал Swift версию этого [здесь] (https://gist.github.com/docherty/b3229cd099472ca3324e3aaf6a6f7330) – James

Смежные вопросы