Jak zmienić rozmiar superview, aby pasował do wszystkich podwizów za pomocą autolayout?
Moje zrozumienie autolayout jest to, że przyjmuje rozmiar superview i opiera się na ograniczeniach i wewnętrznych rozmiarach oblicza pozycje podview.
Czy istnieje sposób na odwrócenie tego procesu? Chcę zmienić rozmiar superview na podstawie ograniczeń i wewnętrznych rozmiarów. Jaki jest najprostszy sposób na osiągnięcie tego celu?Mam widok zaprojektowany w Xcode, który używam jako nagłówek dla UITableView
. Ten widok zawiera etykietę i przycisk. Rozmiar etykiety różni się w zależności od danych. W zależności od ograniczeń Etykieta skutecznie naciska przycisk w dół lub jeśli istnieje ograniczenie między przyciskiem a spodem superview etykieta jest skompresowana.
Znalazłem kilka podobnych pytań, ale nie mają one dobrych i łatwych odpowiedzi.
4 answers
Poprawnym API do użycia jest UIView systemLayoutSizeFittingSize:
, przekazujący UILayoutFittingCompressedSize
lub UILayoutFittingExpandedSize
.
Dla normalnego UIView
używanie autolayout powinno działać tak długo, jak długo Twoje ograniczenia są poprawne. Jeśli chcesz użyć go na UITableViewCell
(aby określić wysokość wiersza na przykład), powinieneś wywołać go przeciwko komórce contentView
i chwycić wysokość.
Istnieją dalsze rozważania, jeśli masz jedną lub więcej UILabel, które są Wielowierszowe. Dla nich jest bezwzględne, że właściwość preferredMaxLayoutWidth
będzie ustawiona poprawnie tak, aby etykieta zawierała poprawną intrinsicContentSize
, która zostanie użyta w obliczeniach systemLayoutSizeFittingSize's
.
EDIT: na życzenie, dodanie przykładu obliczenia wysokości dla komórki widoku tabeli
Używanie autolayout do obliczania wysokości komórki tabeli nie jest zbyt wydajne, ale na pewno jest wygodne, zwłaszcza jeśli masz komórkę o złożonym układzie.
Jak powiedziałem powyżej, jeśli używasz wielolinii UILabel
konieczne jest zsynchronizowanie preferredMaxLayoutWidth
z szerokością etykiety. Używam podklasa UILabel
do tego:
@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
Oto wymyślona podklasa UITableViewController demonstrująca 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
Prosta komórka niestandardowa:
#import "TSTableViewCell.h"
#import "TSLabel.h"
@implementation TSTableViewCell
{
IBOutlet TSLabel* _label;
}
- (void) setText: (NSString *) text
{
_label.text = text;
}
@end
A oto obraz ograniczeń zdefiniowanych w Storyboardzie. Należy pamiętać, że nie ma ograniczeń wysokości/szerokości na etykiecie - są one wnioskowane z etykiety intrinsicContentSize
:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-10 17:09:28
Komentarz Erica Bakera podpowiedział mi, że aby rozmiar widoku był określony przez zawartość umieszczoną w nim, zawartość umieszczona w nim musi mieć wyraźny związek z zawartością widoku, aby dynamicznie napędzać jego wysokość (lub szerokość) . "Dodaj subview" nie tworzy tej relacji, jak można by przypuszczać. Musisz wybrać, który subview będzie wyświetlał wysokość i / lub szerokość kontenera... najczęściej niezależnie od interfejsu użytkownika element umieszczony w prawym dolnym rogu ogólnego interfejsu użytkownika. Oto trochę kodu i komentarzy inline, aby zilustrować punkt.
Uwaga, Może to mieć szczególną wartość dla osób pracujących z widokami przewijania, ponieważ powszechne jest projektowanie wokół pojedynczego widoku zawartości, który określa jego rozmiar (i przekazuje go do widoku przewijania) dynamicznie na podstawie tego, co w nim umieścisz. Powodzenia, mam nadzieję, że to komuś pomoże.
//
// 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-28 16:32:38
Możesz to zrobić, tworząc ograniczenie i łącząc je za pomocą narzędzia interface builder
Zobacz Wyjaśnienie: 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;
Podłącz to ograniczenie z ograniczeniem sub views lub podłącz również ograniczenie super views i ustaw je zgodnie z Twoimi wymaganiami w ten sposób
self.leadingSpaceConstraint.constant = 10.0;//whatever you want to assign
Mam nadzieję, że to wyjaśnia to.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-29 14:39:56
Można to zrobić dla normalnego subview
wewnątrz większego UIView
, ale nie działa to automatycznie dla headerViews
. Wysokość headerView
jest określona przez to, co jest zwrócone przez tableView:heightForHeaderInSection:
, więc musisz obliczyć height
na podstawie height
UILabel
plus przestrzeń dla UIButton
i dowolnych padding
, których potrzebujesz. Musisz zrobić coś takiego:
-(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;
}
Tutaj headerString
jest dowolny ciąg znaków, który chcesz wypełnić UILabel
, a liczba 281 jest {[13] } z UILabel
(jako setup w Interface Builder
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-04 11:15:54