Otomatik altyazıyla tüm alt görünümlere sığacak şekilde denetimin boyutunu nasıl değiştiririm?


142

Otomatik yerleşim anlayışım, denetimin boyutunu ve alt görünümlerin konumlarını hesapladığı kısıtlamalara ve gerçek boyutlara dayandırmasıdır.

Bu süreci tersine çevirmenin bir yolu var mı? Denetimleri kısıtlamalar ve iç boyutlar temelinde yeniden boyutlandırmak istiyorum. Bunu başarmanın en basit yolu nedir?

Üstbilgi olarak kullandığım Xcode'da tasarlanmış görünümüm var UITableView. Bu görünüm bir etiket ve bir düğme içerir. Etiketin boyutu verilere bağlı olarak değişir. Kısıtlamalara bağlı olarak, etiket düğmeyi başarıyla aşağı iter veya düğme ile denetimin alt kısmı arasında bir kısıtlama varsa etiket sıkıştırılır.

Birkaç benzer soru buldum, ancak iyi ve kolay cevapları yok.


28
Tom Swifts'in sorunuzu yanıtladığından emin olmak için aşağıdaki cevaplardan birini seçmelisiniz. Tüm posterler size yardımcı olmak için çok zaman harcadı, şimdi rolünüzü yapmalı ve en iyi şekilde istediğiniz cevabı seçmelisiniz.
David H

Yanıtlar:


149

Kullanılacak doğru API UIView systemLayoutSizeFittingSize:ya UILayoutFittingCompressedSizeya geçiyor UILayoutFittingExpandedSize.

Otomatik UIViewyerleşim kullanan normal bir durum için, bu sadece kısıtlamalarınız doğru olduğu sürece çalışmalıdır. Bir UITableViewCell(örneğin satır yüksekliğini belirlemek için) üzerinde kullanmak istiyorsanız, onu hücrenize karşı çağırmalı contentViewve yüksekliği yakalamalısınız.

Görüşünüzde çok satırlı bir veya daha fazla UILabel varsa, dikkate alınması gereken başka noktalar da vardır. Bunlar için, preferredMaxLayoutWidthözelliğin, etiketin hesaplamada intrinsicContentSizekullanılacak bir doğru sağlayacağı şekilde doğru ayarlanması önemsizdir systemLayoutSizeFittingSize's.

EDIT: istek üzerine, tablo görünümü hücresi için yükseklik hesaplaması örneği ekleme

Tablo hücresi yükseklik hesaplaması için otomatik düzenleme kullanmak süper verimli değildir, ancak özellikle karmaşık bir düzene sahip bir hücreniz varsa, uygun olduğundan emin olun.

Yukarıda söylediğim gibi, çok satırlı bir etiket kullanıyorsanız, etiket genişliğini UILabelsenkronize etmek zorunludur preferredMaxLayoutWidth. Bunu UILabelyapmak için özel bir alt sınıf kullanın:

@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

HeightForRowAtIndexPath'i gösteren, onaylanmış bir UITableViewController alt sınıfı:

#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

Basit bir özel hücre:

#import "TSTableViewCell.h"
#import "TSLabel.h"

@implementation TSTableViewCell
{
    IBOutlet TSLabel* _label;
}

- (void) setText: (NSString *) text
{
    _label.text = text;
}

@end

Ve burada Storyboard'da tanımlanan kısıtlamaların bir resmi. Etikette yükseklik / genişlik sınırlaması bulunmadığına dikkat edin - bunlar etiketten çıkarılır intrinsicContentSize:

resim açıklamasını buraya girin


1
HeightForRowAtIndexPath: uygulamasının çok satırlı etiket içeren bir hücrede bu yöntemi kullanmak gibi bir örneğini verebilir misiniz? Onunla biraz uğraştım ve işe yaramadım. Bir hücreyi nasıl alırsınız (özellikle hücre bir film şeridinde kuruluysa)? Çalışması için hangi kısıtlamalara ihtiyacınız var?
rdelmar

@rdelmar - tabii. Cevabıma bir örnek ekledim.
TomSwift

7
Hücrenin altından hücrenin en alt alt görünümünün altına son bir dikey kısıtlama ekleyene kadar bu benim için işe yaramadı. Dikey sınırlamaların, hücre yüksekliği hesaplamasının başarılı olması için hücre ve içeriği arasındaki üst ve alt dikey aralığı içermesi gerektiği görülmektedir.
Eric Baker

1
Çalışması için sadece bir numara daha gerekiyordu. Ve @EricBaker'ın ucu sonunda çivilenmişti. O adamı paylaştığın için teşekkürler. Ben sıfır olmayan boyutu şimdi ya karşı sizingCellya da onun var contentView.
MkVal

1
Doğru yüksekliği elde edemeyenlerde, sizingCellgenişliğinizin genişliğinizle eşleştiğinden emin olun tableView.
MkVal

30

Eric Baker'ın yorumu beni , bir görünümün boyutunun içine yerleştirilen içerikle belirlenebilmesi için, içine yerleştirilen içeriğin yüksekliğini sürdürebilmek için içeren görünümle açık bir ilişkisi olması gerektiği fikrini bozdu. (veya genişlik) dinamik olarak . "Alt görünüm ekle", tahmin edebileceğiniz gibi bu ilişkiyi oluşturmaz. Hangi alt görünümün kabın yüksekliğini ve / veya genişliğini kullanacağını seçmelisiniz ... en genel olarak genel kullanıcı arayüzünüzün sağ alt köşesine yerleştirdiğiniz kullanıcı arayüzü öğesini. İşte noktayı göstermek için bazı kod ve satır içi yorumlar.

Bu, kaydırma görünümleriyle çalışanlar için belirli bir değer taşıyabilir, çünkü boyutunu belirleyen (ve bunu kaydırma görünümüne ileten) tek bir içerik görünümü etrafında, içine koyduğunuza göre dinamik olarak tasarlamak yaygındır. İyi şanslar, umarım bu orada birine yardım eder.

//
//  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 ile denetimin tüm alt görünümlere sığması için yeniden boyutlandırma


3
Bu harika bir hile ve iyi bilinmiyor. Tekrarlamak için: iç görünümler iç yüksekliğe sahipse ve üst ve alt tarafa sabitlenmişse, dış görünümün yüksekliğini belirtmesine gerek yoktur ve aslında içeriğini kucaklar. İstenilen sonuçları almak için iç görünümler için içerik sıkıştırma ve sarılmayı ayarlamanız gerekebilir.
phatmann

Bu para! Gördüğüm daha iyi özlü VFL örneklerinden biri.
Evan R

Aradığım şey bu (Thanks @John) Bu yüzden burada
James

1
"Konteynerin yüksekliğini ve / veya genişliğini hangi alt görünümün süreceğini seçmelisiniz ... en genel olarak genel kullanıcı arayüzünüzün sağ alt köşesine yerleştirdiğiniz kullanıcı arayüzü öğesi" .. PureLayout kullanıyorum, ve bu benim için anahtardı. Bir ebeveyn görünümü için, bir çocuk görünümü ile, aniden ebeveyn görüntüsüne bir boyut veren alt sağa sabitlemek için çocuk görünümü oturuyordu. Teşekkürler!
Steven Elliott

1
Genişliği çalıştırmak için bir görünüm seçmek tam olarak yapamadığım şeydir. Bazen bir alt görünüm daha geniş, bazen başka bir alt görünüm daha geniştir. Bu dava için bir fikrin var mı?
Henning

3

Bunu bir sınırlama oluşturarak ve arayüz oluşturucu ile bağlayarak yapabilirsiniz

Açıklamaya bakın: Auto_Layout_Constraints_in_Interface_Builder

raywenderlich başlangıç-otomatik düzen

AutolayoutPG Makaleler kısıtlama Temelleri

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

bu Kısıtlama çıkışını alt görünümlerinizle bağlayın Kısıtlama veya süper görünümler de bağlayın Kısıtlama ve bunun gibi gereksinimlerinize göre ayarlayın

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

Umarım bu açıklığa kavuşur.


Bu nedenle, kaynak koddan XCode'da oluşturulan kısıtlamaları yapılandırmak mümkündür. Ancak soru, denetimin yeniden boyutlandırılması için nasıl yapılandırılacağıdır.
DAK

EVET @DAK. kontrol düzeni hakkında emin olun. Kısıtlamayı denetim alanına koyun ve yükseklik sınırlaması yapın, böylece alt görünümünüz her arttığında denetim sınırını otomatik olarak artırır.
chandan

Bu da benim için çalıştı. Sabit boyutlu hücrelere (60 piksel yüksekliğe) sahip olmama yardımcı olur. Görünüm yüklendiğinde, IBOutlet'd yükseklik kısıtlamamı 60 * x olarak ayarladım, burada x, hücre sayısıdır. Nihayetinde, her şeyi görebilmeniz için bunu bir kaydırma görünümünde istersiniz.
Sandy Chapman

-1

Bu subviewdaha büyük bir iç için normal bir şekilde yapılabilir UIView, ancak otomatik olarak çalışmaz headerViews. Bir yüksekliği headerViewtarafından döndürülen ne göre belirlenir tableView:heightForHeaderInSection:Hesaplamak zorunda heightdayalı heightbir UILabeliçin artı alanı UIButtonve herhangi paddingsen gerek yoktur. Bunun gibi bir şey yapmanız gerekiyor:

-(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;
}

İşte headerStringsize doldurmak istediğiniz her türlü dizedir UILabelve 281 sayı widtharasında UILabel(ayarlanan, Interface Builder)


Ne yazık ki bu işe yaramıyor. Denetimdeki “içeriğe sığacak boyut”, beklediğiniz gibi düğmenin alt kısmını denetime bağlayan kısıtlamayı kaldırır. Çalışma zamanında etiket yeniden boyutlandırılır ve düğmeyi aşağı iter, ancak denetim otomatik olarak yeniden boyutlandırılmaz.
DAK

@DAK, üzgünüm, sorun şu ki, görünümünüz bir tablo görünümünün başlığıdır. Durumunuzu yanlış anladım. Düğmeyi ve etiketi çevreleyen görünümün başka bir görünüm içinde olduğunu düşünüyordum, ancak üstbilgi olarak kullanıyorsunuz (başlığın içinde bir görünüm olmaktan ziyade). Yani, orijinal cevabım işe yaramayacak. İşe yaraması gerektiğini düşündüğüm yanıtı değiştirdim.
rdelmar

bu benim şu anki uygulamama benziyor ama daha iyi bir yol olmasını umuyordum. Başlığımı her değiştirdiğimde bu kodu güncellemekten kaçınmayı tercih ederim.
DAK

@Dak, Üzgünüm, daha iyi bir yol olduğunu düşünmüyorum, tablo görünümlerinin işleyiş şekli bu.
rdelmar

Lütfen cevabımı gör. "Daha iyi bir yol" UIView systemLayoutSizeFittingSize: kullanmaktır. Bununla birlikte, bir tablo görünümünde gerekli yüksekliği elde etmek için bir görünümde veya hücrede tam bir otomatik düzenleme yapmak oldukça pahalıdır. Karmaşık hücreler için yapardım ama belki daha basit vakalar için sizinki gibi bir çözüme geri dönebilirsiniz.
TomSwift
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.