UILabel sizeToFit tidak berfungsi dengan autolayout ios6

152

Bagaimana saya bisa mengkonfigurasi secara terprogram (dan dengan metode apa) UILabel yang tingginya tergantung pada teksnya? Saya sudah mencoba mengaturnya menggunakan kombinasi Storyboard dan kode, tetapi tidak berhasil. Semua orang merekomendasikan sizeToFitsaat mengatur lineBreakModedan numberOfLines. Namun, tidak peduli jika saya menempatkan kode yang di viewDidLoad:, viewDidAppear:, atau viewDidLayoutSubviewssaya tidak bisa mendapatkannya untuk bekerja. Entah saya membuat kotak terlalu kecil untuk teks yang panjang dan tidak tumbuh, atau saya membuatnya terlalu besar dan tidak menyusut.

circuitlego
sumber
FWIW kode yang sama: Saya tidak perlu menggunakan label.sizeToFit()Xcode / viewController, batasannya sudah cukup. tidak membuat label di Playground . Sejauh ini satu-satunya cara saya menemukannya untuk bekerja di Playground adalah dengan melakukanlabel.sizeToFit()
Sayang

Jawaban:

407

Harap dicatat bahwa dalam kebanyakan kasus , solusi Matt berfungsi seperti yang diharapkan. Tetapi jika itu tidak berhasil untuk Anda, silakan baca lebih lanjut.

Untuk membuat label Anda mengubah ukuran ketinggian secara otomatis, Anda perlu melakukan hal berikut:

  1. Tetapkan batasan tata letak untuk label
  2. Tetapkan batasan ketinggian dengan prioritas rendah. Itu harus lebih rendah dari ContentCompressionResistancePriority
  3. Setel numberOfLines = 0
  4. Tetapkan ContentHuggingPriority lebih tinggi dari prioritas tinggi label
  5. Tetapkan preferMaxLayoutWidth untuk label. Nilai itu digunakan oleh label untuk menghitung tingginya

Sebagai contoh:

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_)]];

Menggunakan Interface Builder

  1. Siapkan empat kendala. Batasan ketinggian adalah wajib. masukkan deskripsi gambar di sini

  2. Lalu pergi ke inspektur atribut label dan atur jumlah baris ke 0. masukkan deskripsi gambar di sini

  3. Pergi ke inspektur ukuran label dan tingkatkan ContentHuggingPriority vertikal dan ContentCompressionResistancePriority vertikal.
    masukkan deskripsi gambar di sini

  4. Pilih dan edit batasan ketinggian.
    masukkan deskripsi gambar di sini

  5. Dan mengurangi prioritas batasan ketinggian.
    masukkan deskripsi gambar di sini

Nikmati. :)

Mark Kryzhanouski
sumber
4
IYA! Inilah jawaban yang saya cari. Satu-satunya hal yang perlu saya lakukan adalah mengatur contentHuggingPriority dan contentCompressionResistancePriority sesuai kebutuhan. Saya bisa melakukan semuanya di IB! Terima kasih banyak.
circuitlego
43
Anda terlalu memikirkan ini. Baik atur preferredMaxLayoutWidthatau sematkan lebar label (atau sisi kanan dan kirinya). Itu saja! Kemudian secara otomatis akan tumbuh dan menyusut secara vertikal agar sesuai dengan isinya.
matt
Saya harus mengatur properti preferMaxLayoutWidth + pin lebar label. Bekerja seperti pesona :)
MartinMoizard
preferMaxLayoutWidth dan / atau menyematkan sisi kiri dan kanan tidak mutlak. Prioritas memeluk dan kompresi konten akan selalu berfungsi selama prioritas mereka lebih tinggi daripada kendala lainnya.
Eric Alford
2
^ Dan akhirnya saya menemukan alasannya, Mungkin layak untuk disebutkan, ketika Anda memiliki sel yang menempel di bagian bawah tampilan, Anda perlu mengatur prioritas batasan "ke bawah" menjadi kurang dari 1000 yang saya letakkan di 750 dan semuanya bekerja dengan sempurna. Saya kira itu menyusut tampilan.
Mathijs Segers
65

Di iOS 6, menggunakan autolayout, jika sisi (atau lebar) UILabel disematkan, ia akan secara otomatis tumbuh dan menyusut secara vertikal agar sesuai dengan isinya, tanpa kode sama sekali dan tanpa mengacaukan ketahanan kompresi atau apa pun. Ini sangat sederhana.

Dalam kasus yang lebih kompleks, cukup atur labelnya preferredMaxLayoutWidth.

Either way, hal yang benar terjadi secara otomatis.

matt
sumber
12
Anda juga perlu memastikan untuk mengatur numberOfLines ke 0 jika tidak, Anda tidak akan mendapatkan pembungkus.
devongovett
2
Ini tidak cukup bagi saya. Juga diperlukan poin 2 dari jawaban @ Mark.
Danyal Aytekin
mungkinkah membuat label tumbuh secara otomatis pada beberapa baris tanpa menulis kode?
Noor
Ini jauh lebih rumit daripada jawaban yang diterima. Saya membuat dua contoh (di sini dan di sini ) yang menggambarkan jawaban ini.
Suragch
@ Suragch Menggunakan batasan ini berfungsi. Tapi itu tidak berfungsi untuk kode yang sama di Playground . Di taman bermain, Anda harus memilikilabel.sizeToFit()
Sayang
42

Meskipun pertanyaan menyatakan secara pemrograman, setelah mengalami masalah yang sama, dan lebih suka bekerja di Interface Builder, saya pikir mungkin berguna untuk menambahkan jawaban yang ada dengan solusi Interface Builder.

Hal pertama yang harus dilupakan sizeToFit. Tata Letak Otomatis akan menangani ini atas nama Anda berdasarkan pada ukuran konten intrinsik.

Masalahnya adalah, bagaimana cara mendapatkan label agar sesuai dengan kontennya dengan Tata Letak Otomatis? Khususnya - karena pertanyaan menyebutkannya - tinggi. Perhatikan bahwa prinsip yang sama berlaku untuk lebar.

Jadi mari kita mulai dengan contoh UILabel yang memiliki tinggi diatur ke tinggi 41px:

masukkan deskripsi gambar di sini

Seperti yang dapat Anda lihat di layar ambil di atas, "This is my text"memiliki bantalan di atas dan di bawah. Itu adalah padding di antara ketinggian UILabel, dan isinya, teks.

Jika kami menjalankan aplikasi di simulator, tentu saja, kami melihat hal yang sama:

masukkan deskripsi gambar di sini

Sekarang, mari kita pilih label UIL di Interface Builder, dan lihat pengaturan default di inspektur Ukuran:

masukkan deskripsi gambar di sini

Perhatikan kendala yang disorot di atas. Itulah Prioritas Memeluk Konten . Seperti yang dijelaskan Erica Sadun dalam iOS Auto Layout Demystified , ini adalah:

cara tampilan lebih suka untuk menghindari bantalan tambahan di sekitar konten inti itu

Bagi kami, dengan UILabel, konten intinya adalah teks.

Di sini kita sampai pada inti dari skenario dasar ini. Kami telah memberi dua batasan pada label teks kami. Mereka saling bertentangan. Seseorang mengatakan "tingginya harus sama dengan tinggi 41 piksel" . Yang lain mengatakan "memeluk tampilan untuk konten itu sehingga kami tidak memiliki bantalan tambahan" . Dalam kasus kami, peluk tampilan ke teks itu sehingga kami tidak memiliki bantalan tambahan.

Sekarang, dengan Tata Letak Otomatis, dengan dua instruksi berbeda yang mengatakan melakukan hal yang berbeda, runtime harus memilih satu atau yang lain. Tidak bisa melakukan keduanya. UILabel tidak boleh memiliki tinggi 41 piksel, dan tidak memiliki bantalan.

Cara ini diselesaikan, adalah dengan menentukan prioritas. Satu instruksi harus memiliki prioritas yang lebih tinggi daripada instruksi lainnya. Jika kedua instruksi mengatakan hal yang berbeda, dan memiliki prioritas yang sama, pengecualian akan terjadi.

Jadi mari kita mencobanya. Batasan tinggi badan saya memiliki prioritas 1000 , yang diperlukan . Tinggi konten memeluk adalah 250 , yang lemah . Apa yang terjadi jika kita mengurangi prioritas batasan ketinggian menjadi 249 ?

masukkan deskripsi gambar di sini

Sekarang kita bisa melihat keajaiban mulai terjadi. Mari kita coba di sim:

masukkan deskripsi gambar di sini

Luar biasa! Pelukan konten tercapai. Hanya karena prioritas tinggi 249 kurang dari prioritas konten yang dipeluk 250 . Pada dasarnya, saya katakan "ketinggian yang saya tentukan di sini kurang penting daripada apa yang saya tentukan untuk memeluk konten" . Jadi, konten yang dipeluk menang.

Intinya, mendapatkan label agar sesuai dengan teks dapat sesederhana menetapkan batasan tinggi - atau lebar, dan memperbaiki pengaturan yang diprioritaskan terkait dengan kendala prioritas memeluk konten sumbu.

Akan meninggalkan melakukan yang setara dengan lebar sebagai latihan untuk pembaca!

Max MacLeod
sumber
3
Terima kasih banyak atas penjelasan Anda! Akhirnya saya memahami Prioritas Memeluk Konten.
kernix
Bisakah Anda jelaskan juga apa itu Prioritas Perlawanan Kompresi Konten? Terima kasih!
kernix
2
Excelente, jawab Max! Apakah ada cara untuk membatasi jumlah garis yang bekerja seperti itu?
rafaeljuzo
7

Tercatat dalam ukuran iOS7 iOSToFit tidak bekerja juga - mungkin solusinya dapat membantu Anda juga

[textView sizeToFit];
[textView layoutIfNeeded];
Nick Wood
sumber
1
@Rinoino Ini bekerja untuk saya. Tambahkan layoutIfNeededketika Anda membutuhkannya setNeedsUpdateConstraints. Tetapi situasinya mungkin sedikit berbeda.
Gon
Ini bekerja paling baik untuk saya ketika saya melakukan popover. Saya perlu melakukan layoutIfNeeded pertama, meskipun
Jason
4

Opsi lain untuk memastikanMaxLayoutWidth label yang disukai tetap sinkron dengan lebar label:

#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
Monte Hurd
sumber
4

Saya merasa harus berkontribusi karena butuh beberapa saat untuk menemukan solusi yang tepat:

  • Tujuannya adalah untuk membiarkan Tata Letak Otomatis melakukan tugasnya tanpa pernah memanggil sizeToFit (), kami akan melakukan ini dengan menentukan batasan yang tepat:
  • Tentukan batasan ruang atas, bawah, dan depan / belakang pada UILabel Anda
  • Tetapkan jumlah properti baris ke 0
  • Tambahkan Prioritas Memeluk Konten ke 1000
  • Turunkan Prioritas Ketahanan Kompresi Konten ke 500
  • Pada batasan wadah bawah Anda, turunkan prioritas ke 500

Pada dasarnya, yang terjadi adalah Anda memberi tahu UILabel bahwa meskipun memiliki batasan ketinggian yang tetap, itu dapat membuat mematahkan batasan untuk menjadikan dirinya lebih kecil untuk memeluk konten (jika Anda memiliki satu baris misalnya), tetapi tidak dapat mematahkan batasan untuk membuatnya lebih besar.

ArturT
sumber
3

Dalam kasus saya, saya membuat subkelas UIView yang berisi UILabel (dengan panjang yang tidak diketahui). Di iOS7 kodenya mudah: atur batasannya, jangan khawatir tentang konten yang dipeluk atau resistensi kompresi, dan semuanya berfungsi seperti yang diharapkan.

Namun di iOS6, UILabel selalu terpotong menjadi satu baris. Tidak ada jawaban di atas yang berfungsi untuk saya. Pengaturan pelukan konten dan resistensi kompresi diabaikan. Satu-satunya solusi yang mencegah kliping adalah dengan memasukkanMaxLayoutWidth yang disukai pada label. Tapi saya tidak tahu harus mengatur lebar yang disukai, karena ukuran tampilan induknya tidak diketahui (memang, itu akan ditentukan oleh konten).

Saya akhirnya menemukan solusinya di sini . Karena saya sedang mengerjakan tampilan kustom, saya bisa menambahkan metode berikut untuk mengatur lebar tata letak yang disukai setelah kendala dihitung sekali, dan kemudian menghitung ulang mereka:

- (void)layoutSubviews
{
    // Autolayout hack required for iOS6
    [super layoutSubviews];
    self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
    [super layoutSubviews];
}
Sumizome
sumber
Ini adalah skenario persis yang saya miliki ... kadang-kadang senang mengetahui Anda tidak sendirian.
daveMac
Adam, aku berjuang dengan apa yang kamu katakan bekerja dengan sempurna di ios7. Saya memiliki sel khusus dengan uiview dan uilabel. Saya bisa mendapatkan label agar sesuai dengan konten tetapi tampilan tidak, tidak peduli kendala apa pun yang saya pasang. Bagaimana Anda membuatnya bekerja? Saya bersumpah saya mencoba segalanya tetapi tidak butuh ketinggian label
denikov
3

saya tambahkan UILabel pemrograman dan dalam kasus saya itu sudah cukup:

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

Saya telah menyelesaikan dengan xCode6 menempatkan "Lebar yang Diinginkan" ke Otomatis dan pin atas label, memimpin dan mengikuti masukkan deskripsi gambar di sini

Kazzar
sumber
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));
Swati
sumber
0

Saya mengalami masalah ini juga dengan subkelas UIView yang berisi UILabel sebagai salah satu jika elemen internalnya. Bahkan dengan pembayaran otomatis dan mencoba semua solusi yang disarankan, label tidak akan memperketat ketinggiannya ke teks. Dalam kasus saya, saya hanya ingin label menjadi satu baris.

Bagaimanapun, apa yang berhasil bagi saya adalah menambahkan batasan ketinggian yang diperlukan untuk UILabel dan mengaturnya secara manual ke ketinggian yang benar ketika intrinsicContentSize dipanggil. Jika Anda tidak memiliki UILabel yang terkandung dalam UIView lain, Anda dapat mencoba subclassing UILabel dan memberikan implementasi yang serupa dengan terlebih dahulu menetapkan batasan ketinggian dan kemudian mengembalikan
[super instrinsicContentSize]; bukannya [self.containerview intrinsiceContentSize]; seperti yang saya lakukan di bawah ini yang khusus untuk subkelas UIView saya.

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

}

Sekarang berfungsi dengan baik di iOS 7 dan iOS 8.

n8tr
sumber
Perlu dicatat bahwa jika Anda telah mengatur tampilan Anda dengan benar (yaitu kendala Anda benar dan Anda telah mengatur semuanya selama tahap yang benar dalam siklus hidup tampilan), Anda seharusnya tidak perlu menimpa intrinsicContentSize. Runtime harus dapat menghitung ini berdasarkan batasan pengaturan Anda dengan benar.
John Rogers
Secara teori itu harus benar, tetapi kami tidak memiliki akses ke semua API dan implementasi pribadi Apple dan mereka tidak sempurna dan memiliki bug dalam kodenya.
n8tr
... tapi [super intrinsicContentSize] harus membereskannya, itulah yang saya katakan. Jika Anda telah menerapkan tata letak otomatis Anda dengan benar.
John Rogers
0

Solusi yang berhasil untuk saya; Jika UILabel Anda memiliki lebar tetap, ubah kendala dari constant =menjadi constant <=di file antarmuka Anda

masukkan deskripsi gambar di sini

Alex Brown
sumber
-1

Dalam kasus saya ketika menggunakan label dalam UITableViewCell, label at akan mengubah ukuran tetapi tingginya akan menyerbu ketinggian sel tabel. Inilah yang bekerja untuk saya. Saya lakukan sesuai dengan Max MacLeod, dan kemudian memastikan tinggi sel diatur ke UITableViewAutomaticDimension.

Anda dapat menambahkan ini di init Anda atau awakeFromNib,

self.tableView.rowHeight = UITableViewAutomaticDimension.  

Di storyboard, pilih sel, buka inspektur Ukuran, dan pastikan tinggi baris diatur ke "Default" dengan mencentang kotak centang 'Kustom'.

Ada masalah di 8.0 yang juga mengharuskannya diatur dalam kode.

Maria
sumber