UILabel sizeToFit, otomatik yerleşim ios6 ile çalışmaz


152

Yüksekliği metnine bağlı olan bir UILabel'i programsal olarak (ve hangi yöntemde) yapılandırmam gerekir? Ben Storyboard ve kod bir arada kullanarak kurmaya çalışıyorum, ama boşuna. Herkes önerir sizeToFitayarlanırken lineBreakModeve numberOfLines. Ancak, ne olursa olsun ben bu kodu koyarsanız viewDidLoad:, viewDidAppear:ya viewDidLayoutSubviewsben işe atamıyorum. Kutuyu uzun metin için çok küçük yapıyorum ve büyümiyor ya da çok büyük yapıyorum ve küçülmüyor.


Aynı kodu FWIW: label.sizeToFit()Xcode / viewController kullanmak zorunda değildi , kısıtlamalar yeterliydi. Playground'da etiket oluşturmuyordu . Şimdiye kadar Playground çalışmak için buldum tek yolu yapmaktırlabel.sizeToFit()
Bal

Yanıtlar:


407

Çoğu durumda Matt'in çözümünün beklendiği gibi çalıştığını lütfen unutmayın . Ancak bu sizin için işe yaramıyorsa, lütfen daha fazla bilgi edinin.

Etiketinizin yüksekliği otomatik olarak yeniden boyutlandırması için aşağıdakileri yapmanız gerekir:

  1. Etiket için düzen sınırlamaları ayarlama
  2. Yükseklik sınırıyla yükseklik sınırını ayarlayın. ContentCompressionResistancePriority değerinden daha düşük olmalıdır
  3. Set numarasıOfLines = 0
  4. ContentHuggingPriority öğesini etiketin yükseklik önceliğinden daha yüksek olarak ayarlama
  5. Etiket için tercih edilen MaxLayoutWidth değerini ayarlayın. Bu değer etiket tarafından yüksekliğini hesaplamak için kullanılır

Örneğin:

self.descriptionLabel = [[UILabel alloc] init];
self.descriptionLabel.numberOfLines = 0;
self.descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.descriptionLabel.preferredMaxLayoutWidth = 200;

[self.descriptionLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.descriptionLabel];

NSArray* constrs = [NSLayoutConstraint constraintsWithVisualFormat:@"|-8-[descriptionLabel_]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)];
[self addConstraints:constrs];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[descriptionLabel_]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
[self.descriptionLabel addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[descriptionLabel_(220@300)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];

Interface Builder'ı kullanma

  1. Dört sınırlama belirleyin. Yükseklik kısıtı zorunludur. resim açıklamasını buraya girin

  2. Ardından etiketin nitelik denetçisine gidin ve satır sayısını 0 olarak ayarlayın. resim açıklamasını buraya girin

  3. Etiketin boyut denetçisine gidin ve dikey ContentHuggingPriority ve dikey ContentCompressionResistancePriority'yi artırın.
    resim açıklamasını buraya girin

  4. Yükseklik sınırlamasını seçin ve düzenleyin.
    resim açıklamasını buraya girin

  5. Ve yükseklik kısıtlaması önceliğini azaltın.
    resim açıklamasını buraya girin

Zevk almak. :)


4
EVET! Aradığım cevap tam olarak buydu. Yapmam gereken tek şey contentHuggingPriority ve contentCompressionResistancePriority'yi gerekli olarak ayarlamaktı. Hepsini IB'de yapabilirim! Çok teşekkür ederim.
circuitlego

43
Bunu çok düşünüyorsun. preferredMaxLayoutWidthEtiketin genişliğini (veya sağ ve sol kenarlarını) ayarlayın veya sabitleyin. Bu kadar! Daha sonra içeriğine uyacak şekilde otomatik olarak dikey olarak büyüyecek ve küçülecektir.
matt

Ben tercih edilenMaxLayoutWidth özelliği ayarlamak zorunda + etiket genişliğini pin. Bir cazibe gibi çalışır :)
MartinMoizard

tercih edilenMaxLayoutWidth ve / veya sol ve sağ tarafları sabitlemek mutlak değildir. İçerik sarılma ve sıkıştırma öncelikleri, öncelikleri kısıtlamaların geri kalanından daha yüksek olduğu sürece her zaman çalışır.
Eric Alford

2
^ Ve nihayet neden olduğunu anlamaya değer olabilir, hücrenin görünümün altına yapışmasını sağladığınızda, "aşağıdan" kısıtlamanın önceliğini 1000'den daha azına ayarlamanız gerekir. her şey mükemmel çalışıyor. Sanırım manzarayı daralttı.
Mathijs Segers

65

İOS 6'da, otomatik yerleşim kullanarak, bir UILabel'in kenarları (veya genişliği) ve üst kısmı sabitlenirse, hiç kod olmadan ve sıkıştırma direnci veya herhangi bir şeyle uğraşmadan içeriğine uyacak şekilde otomatik olarak dikey olarak büyür ve küçülür . Ölü basit.

Daha karmaşık durumlarda, etiketlerin ayarlanması yeterlidir preferredMaxLayoutWidth.

Her iki durumda da, doğru olan şey otomatik olarak gerçekleşir.


12
Ayrıca numberOfLines değerini 0 olarak ayarladığınızdan emin olmanız gerekir, aksi takdirde kaydırma yapılmaz.
devongovett

2
Bu benim için yeterli değildi. Ayrıca @ Mark'ın cevabından 2. noktaya ihtiyaç vardı.
Danyal Aytekin

kod yazmadan birkaç satırda etiketin otomatik olarak büyümesini sağlamak mümkün müdür?
Noor

Bu, kabul edilen cevaptan çok daha az karmaşıktır. Bu cevabı açıklayan iki örnek ( burada ve burada ) yaptım .
Suragch

@Suragch Bu kısıtlamaları kullanarak çalışır. Ancak Playground'da aynı kod için çalışmaz . Oyun alanında olması gerekirlabel.sizeToFit()
Bal

42

Her ne kadar soru programlı olarak aynı sorunla karşılaşmış ve Arayüz Oluşturucu'da çalışmayı tercih etse de, bir Arayüz Oluşturucu çözümü ile mevcut cevaplara eklemenin yararlı olabileceğini düşündüm.

İlk şey unutmak sizeToFit. Otomatik Mizanpaj, asıl içerik boyutuna göre bunu sizin adınıza halleder.

Bu nedenle sorun, Otomatik Mizanpaj ile içeriğine uyacak bir etiket nasıl edinilir? Özellikle - sorudan bahsedildiği için - yükseklik. Aynı ilkelerin genişlik için de geçerli olduğunu unutmayın.

Şimdi yüksekliği 41 piksel yüksekliğe ayarlanmış bir örnek UILabel ile başlayalım:

resim açıklamasını buraya girin

Yukarıdaki ekranda gördüğünüz gibi, "This is my text"üstte ve altta dolgu var. Bu, UILabel'ın yüksekliği ile içeriği, metin arasında bir dolgudur.

Uygulamayı simülatörde çalıştırırsak, yeterince eminim, aynı şeyi görüyoruz:

resim açıklamasını buraya girin

Şimdi, Interface Builder'da UILabel'i seçelim ve Boyut denetçisindeki varsayılan ayarlara göz atalım:

resim açıklamasını buraya girin

Yukarıdaki vurgulanan kısıtlamaya dikkat edin. Yani İçerik Hugging Önceliği . Erica Sadun'un mükemmel iOS Otomatik Yerleşim Demystified'de açıkladığı gibi , bu:

bir görünümün temel içeriği etrafında fazladan dolgu yapılmasını önleme biçimi

Bizim için, UILabel ile temel içerik metindir.

İşte bu temel senaryonun kalbine geliyoruz. Metin etiketimize iki kısıtlama getirdik. Çatışıyorlar. Biri "yükseklik 41 piksel yüksekliğe eşit olmalı" diyor . Diğeri ise "içeriğine bak, böylece fazladan dolgu yapmıyoruz" diyor . Bizim durumumuzda, görünümü fazladan doldurmak için metne sarın.

Şimdi, Otomatik Düzen ile, farklı şeyler yapın diyen iki farklı talimatla, çalışma zamanının birini veya diğerini seçmesi gerekir. İkisini birden yapamaz. UILabel 41 piksel yüksekliğinde olamaz ve dolgu içermez.

Bunun çözüm yolu, öncelik belirtmektir. Bir talimat diğerinden daha yüksek önceliğe sahip olmalıdır. Her iki talimat da farklı şeyler söylüyorsa ve aynı önceliğe sahipse, bir istisna oluşacaktır.

Hadi bir deneyelim. Boyum kısıt bir önceliğe sahiptir 1000 edilir gerekli . İçerik sarılma yüksekliği 250'dir ve bu zayıftır . Yükseklik sınırlama önceliğini 249'a düşürürsek ne olur ?

resim açıklamasını buraya girin

Şimdi büyünün gerçekleşmeye başladığını görebiliyoruz. Sim'de deneyelim:

resim açıklamasını buraya girin

Müthiş! İçerik sarılma sağlandı. Yalnızca yükseklik önceliği 249 , içerik sarılma önceliğinden 250 daha az olduğu için . Temel olarak, "Burada belirttiğim yükseklik, içerik sarılma için belirttiğimden daha az önemli" diyorum . Böylece, sarılma içeriği kazanır.

Alt satırda, etiketi metne sığdırmak, yükseklik veya genişlik kısıtlamasını belirtmek kadar basit olabilir ve bu eksenin o eksenin içerik öncelik önceliği kısıtlamasıyla ilişkili olarak doğru ayarını yapmak kadar basit olabilir.

Okuyucu için bir egzersiz olarak genişlik eşdeğerini yapmayı bırakacak!


3
Harika açıklamanız için çok teşekkürler! Son olarak İçerik Sarılma Önceliğini anlıyorum.
kernix

İçerik Sıkıştırma Direnci Önceliği'nin ne olduğunu da açıklar mısınız? Teşekkürler!
kernix

2
Excelente cevap Max! Bu şekilde çalışan hat sayısını sınırlamak için bir yol var mı?
rafaeljuzo

7

IOS7'de fark edilen sizeToFit de çalışmadı - belki de çözüm size yardımcı olabilir

[textView sizeToFit];
[textView layoutIfNeeded];

1
@Rambatino Bu benim için çalışıyor. layoutIfNeededİhtiyacınız olduğunda ekleyin setNeedsUpdateConstraints. Ancak durum biraz farklı olabilir.
Gon

Bir popover yaptığımda bu benim için en iyi çalıştı. Yine de düzen yapmam gerekiyordu
Jason

4

Etiketin tercih edilen MaxLayoutWidth değerini sağlamak için başka bir seçenek, etiketin genişliği ile senkronize olarak tutulur:

#import "MyLabel.h"

@implementation MyLabel

-(void)setBounds:(CGRect)bounds
{
    [super setBounds:bounds];

    // This appears to be needed for iOS 6 which doesn't seem to keep
    // label preferredMaxLayoutWidth in sync with its width, which 
    // means the label won't grow vertically to encompass its text if 
    // the label's width constraint changes.
    self.preferredMaxLayoutWidth = self.bounds.size.width;
}

@end

4

Doğru çözümü bulmam biraz zaman aldığım için katkıda bulunmam gerektiğini hissediyorum:

  • Amaç, Auto Layout'un işini size sizeToFit () öğesini çağırmadan yapmasına izin vermek, bunu doğru kısıtlamaları belirterek yapacağız:
  • UILabel'inizde üst, alt ve öndeki / arkadaki boşluk kısıtlamalarını belirtme
  • Satır sayısı özelliğini 0 olarak ayarlayın
  • İçerik Sarma Önceliğini 1000'e Artırın
  • İçerik Sıkıştırma Direnci Önceliğini 500'e düşürün
  • Alt kapsayıcı kısıtlamanızda önceliği 500'e düşürün

Temel olarak, UILabel'e sabit bir yükseklik kısıtlaması olmasına rağmen, içeriği sarmak için kendini küçültmek için kısıtlamayı kırabileceğini (örneğin tek bir satırınız varsa) söylersiniz, ancak daha büyük yapmak için kısıtlamayı kırın.


3

Benim durumumda (bilinmeyen uzunlukta) bir UILabel içeren bir UIView alt sınıfı oluşturuyordum. İOS7'de kod basitti: kısıtlamaları ayarlayın, içerik sarılma veya sıkıştırma direnci hakkında endişelenmeyin ve her şey beklendiği gibi çalıştı.

Ancak iOS6'da UILabel her zaman tek bir satıra kırpıldı. Yukarıdaki cevapların hiçbiri benim için işe yaramadı. İçerik sarılma ve sıkıştırma direnci ayarları yoksayıldı. Kırpmayı önleyen tek çözüm etikete tercih edilen bir MaxLayoutWidth eklemekti. Ancak, üst görünümünün boyutu bilinmediğinden, tercih edilen genişliği ne ayarlayacağımı bilmiyordum (gerçekten, içerik tarafından tanımlanacaktır).

Sonunda çözümü burada buldum . Özel bir görünüm üzerinde çalıştığım için, kısıtlamalar bir kez hesaplandıktan sonra tercih edilen düzen genişliğini ayarlamak için aşağıdaki yöntemi ekleyebilirim ve sonra yeniden hesaplayabilirim:

- (void)layoutSubviews
{
    // Autolayout hack required for iOS6
    [super layoutSubviews];
    self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
    [super layoutSubviews];
}

Tam senaryom buydu… bazen yalnız olmadığınızı bilmek güzel.
daveMac

Adam, ios7'de mükemmel çalıştığını söylediğin şeyle mücadele ediyorum. Bir uiview ve bir uilabel ile özel bir hücre var. Etiketi içeriğe sığdırmak için alabilirim, ancak hangi kısıtlamaları uygulasam da görünüm almaz. İşe nasıl başladın? Yemin ederim her şeyi denedim ama etiketin yüksekliğini
almıyor

3

UILabelProgramlı olarak ekledim ve benim durumumda bu yeterliydi:

label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0

2

xCode6 ile otomatik olarak "Tercih edilen Genişlik" koyarak çözdüm ve etiket üst, lider ve sondaki pin resim açıklamasını buraya girin


0
UIFont *customFont = myLabel.font;
CGSize size = [trackerStr sizeWithFont:customFont
                             constrainedToSize:myLabel.frame.size // the size here should be the maximum size you want give to the label
                                 lineBreakMode:UILineBreakModeWordWrap];
float numberOfLines = size.height / customFont.lineHeight;
myLabel.numberOfLines = numberOfLines;
myLabel.frame = CGRectMake(258, 18, 224, (numberOfLines * customFont.lineHeight));

0

Ben de iç öğeleri bir UILabel içeren bir UIView alt sınıfı ile bu sorunla karşılaştı. Otomatik düzenleme ve önerilen çözümlerin tümünü denemede bile, etiket metnin yüksekliğini sıkmaz. Benim durumumda, etiketin sadece bir satır olmasını istiyorum.

Her neyse, benim için çalışan UILabel için gerekli bir yükseklik kısıtlaması eklemek ve intrinsicContentSize çağrıldığında bunu manuel olarak doğru yüksekliğe ayarlamaktı. UILabel başka bir UIView içinde bulunmuyorsa, UILabel alt sınıflamasını deneyebilir ve önce yükseklik
sınırını ayarlayıp ardından [super instrinsicContentSize] döndürerek benzer bir uygulama sağlayabilirsiniz ; [self.containerview intrinsiceContentSize] yerine; UIView alt sınıfıma özgü olan aşağıda yaptığım gibi.

- (CGSize)intrinsicContentSize
{
    CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
    self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
    return [self.containerView intrinsicContentSize];

}

Şimdi iOS 7 ve iOS 8'de mükemmel çalışıyor.


Görünümünüzü doğru şekilde ayarladıysanız (yani kısıtlamalarınız doğruysa ve her şeyi görünümün yaşam döngüsünde doğru aşamalar sırasında kurduysanız), asla geçersiz kılmamalısınız intrinsicContentSize. Çalışma zamanı, doğru kurulum kısıtlamalarınıza dayanarak bunu hesaplayabilmelidir.
John Rogers

Teorik olarak doğru olmalı, ancak Apple'ın tüm özel API'lerine ve uygulamalarına erişimimiz yok ve yanılmaz değiller ve kodlarında hatalar var.
n8tr

... ama [super intrinsicContentSize] bununla ilgilenmeli, söylediklerim bu. Otomatik düzeninizi doğru şekilde uyguladıysanız.
John Rogers

0

Benim için çalışan bir çözüm; Senin UILabel sabit genişliğe sahipse, gelen kısıtlamayı değiştirmek constant =için constant <=arayüz dosyasında

resim açıklamasını buraya girin


-1

Benim durumumda bir UITableViewCell etiketleri kullanırken, adresindeki etiket yeniden boyutlandırılır, ancak yükseklik tablo hücre yüksekliğini aşar. Bu benim için işe yaradı. Max MacLeod göre yaptım ve sonra hücre yüksekliği UITableViewAutomaticDimension olarak ayarlandığından emin yaptı.

Bunu init veya awakeFromNib'inize ekleyebilirsiniz,

self.tableView.rowHeight = UITableViewAutomaticDimension.  

Film şeridinde hücreyi seçin, Boyut denetçisini açın ve 'Özel' onay kutusunu işaretleyerek satır yüksekliğinin "Varsayılan" olarak ayarlandığından emin olun.

8.0'da kodda ayarlanmasını gerektiren bir sorun var.

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.