Как изменить суперпредставление, чтобы оно соответствовало всем подпредставлениям с 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
Вы можете сделать это, создав ограничение и подключив его через конструктор интерфейса
См. Объяснение: 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
)