Pertanyaan Menggunakan Tata Letak Otomatis di UITableView untuk tata letak sel dinamis & ketinggian baris variabel


Bagaimana Anda menggunakan Tata Letak Otomatis dalam UITableViewCells dalam tampilan tabel untuk membiarkan setiap konten dan subview sel menentukan tinggi baris (secara otomatis / otomatis), sambil mempertahankan kinerja pengguliran yang mulus?


1350
2017-09-11 16:48


asal


Jawaban:


TL; DR: Tidak suka membaca? Langsung menuju ke proyek sampel di GitHub:

Deskripsi Konseptual

Dua langkah pertama di bawah ini berlaku terlepas dari versi iOS mana yang Anda kembangkan.

1. Set Up & Tambahkan Batasan

Di dalam Anda UITableViewCell subclass, tambahkan kendala sehingga subview sel memiliki ujungnya disematkan ke tepi sel contentView (paling penting ke tepi atas DAN bawah). CATATAN: jangan pin subview ke sel itu sendiri; hanya ke sel contentView! Biarkan ukuran konten intrinsik dari subview ini mendorong ketinggian tampilan konten sel tampilan tabel dengan memastikan resistensi kompresi konten dan konten memeluk kendala dalam dimensi vertikal untuk setiap subview tidak ditimpa oleh batasan prioritas tinggi yang telah Anda tambahkan. (Hah? Klik disini.)

Ingat, idenya adalah memiliki subview sel yang terhubung secara vertikal ke tampilan konten sel sehingga mereka dapat "memberikan tekanan" dan membuat tampilan konten meluas agar sesuai dengan mereka. Menggunakan sel contoh dengan beberapa sublihat, berikut ini adalah ilustrasi visual tentang apa beberapa  (tidak semua!) batasan Anda akan perlu terlihat seperti:

Example illustration of constraints on a table view cell.

Anda dapat membayangkan bahwa semakin banyak teks ditambahkan ke label badan multi-baris dalam contoh sel di atas, maka perlu untuk tumbuh secara vertikal agar sesuai dengan teks, yang secara efektif akan memaksa sel untuk tumbuh tinggi. (Tentu saja, Anda perlu mendapatkan batasan yang tepat agar ini berfungsi dengan benar!)

Mendapatkan batasan Anda benar adalah pasti bagian tersulit dan terpenting untuk mendapatkan tinggi sel dinamis bekerja dengan Tata Letak Otomatis. Jika Anda membuat kesalahan di sini, itu bisa mencegah yang lainnya bekerja - jadi ambillah waktu Anda! Saya sarankan Anda mengatur batasan dalam kode karena Anda tahu persis kendala mana yang ditambahkan di mana, dan itu jauh lebih mudah untuk melakukan debug ketika ada yang salah. Menambahkan kendala dalam kode dapat semudah dan secara signifikan lebih kuat daripada Interface Builder menggunakan jangkar tata letak, atau salah satu dari API open source fantastis yang tersedia di GitHub.

  • Jika Anda menambahkan batasan dalam kode, Anda harus melakukan ini satu kali dari dalam updateConstraints metode subclass UITableViewCell Anda. Perhatikan itu updateConstraints dapat dipanggil lebih dari satu kali, jadi untuk menghindari penambahan kendala yang sama lebih dari sekali, pastikan untuk membungkus kode pembatas kendala Anda dalam updateConstraints di cek untuk properti boolean seperti didSetupConstraints (yang Anda tetapkan ke YES setelah Anda menjalankan kode pembatas-kendala Anda sekali). Di sisi lain, jika Anda memiliki kode yang memperbarui kendala yang ada (seperti menyesuaikan constantproperti pada beberapa kendala), tempatkan ini updateConstraints tetapi di luar pemeriksaan didSetupConstraints sehingga dapat dijalankan setiap kali metode ini dipanggil.

2. Tentukan Unik Tabel View Cell Reuse Identifiers

Untuk setiap set batasan unik dalam sel, gunakan pengidentifikasi penggunaan ulang sel yang unik. Dengan kata lain, jika sel Anda memiliki lebih dari satu tata letak unik, setiap tata letak unik harus menerima pengenal penggunaan ulangnya sendiri. (Petunjuk yang baik bahwa Anda perlu menggunakan pengenal reuse baru adalah ketika varian sel Anda memiliki jumlah sub-tampilan yang berbeda, atau sublihat disusun dengan cara yang berbeda.)

Misalnya, jika Anda menampilkan pesan email di setiap sel, Anda mungkin memiliki 4 tata letak unik: pesan hanya dengan subjek, pesan dengan subjek dan tubuh, pesan dengan subjek dan lampiran foto, serta pesan dengan subjek, tubuh, dan lampiran foto. Setiap tata letak memiliki kendala yang sangat berbeda yang diperlukan untuk mencapainya, sehingga setelah sel diinisialisasi dan kendala ditambahkan untuk salah satu dari jenis sel ini, sel harus mendapatkan pengidentifikasi penggunaan kembali unik khusus untuk jenis sel tersebut. Ini berarti ketika Anda melakukan dequeue sel untuk digunakan kembali, kendala telah ditambahkan dan siap untuk pergi untuk jenis sel itu.

Perhatikan bahwa karena perbedaan ukuran konten intrinsik, sel dengan batasan yang sama (tipe) mungkin masih memiliki ketinggian yang bervariasi! Jangan mengacaukan tata letak yang berbeda secara mendasar (batasan yang berbeda) dengan bingkai tampilan terhitung yang berbeda (diselesaikan dari kendala yang sama) karena ukuran konten yang berbeda.

  • Jangan menambahkan sel dengan kumpulan kendala yang sama sekali berbeda ke kolam reuse yang sama (yaitu menggunakan pengenal reuse yang sama) dan kemudian mencoba untuk menghapus kendala lama dan mengatur batasan baru dari awal setelah setiap dequeue. Mesin Tata Letak Internal otomatis tidak dirancang untuk menangani perubahan skala besar dalam batasan, dan Anda akan melihat masalah kinerja besar.

Untuk iOS 8 - Ukuran Sel Sendiri

3. Aktifkan Estimasi Tinggi Row

Untuk mengaktifkan sel tampilan tabel ukuran sendiri, Anda harus menetapkan tampilan tabel   properti rowHeight ke UITableViewAutomaticDimension. Anda juga harus   berikan nilai ke properti estimatedRowHeight. Begitu keduanya   properti ini diatur, sistem menggunakan Tata Letak Otomatis untuk menghitung   tinggi baris sebenarnya

Apel: Bekerja dengan Self-Sizing Table View Cells

Dengan iOS 8, Apple telah menginternalisasi sebagian besar pekerjaan yang sebelumnya harus dilaksanakan oleh Anda sebelum iOS 8. Untuk memungkinkan mekanisme sel sizing diri bekerja, Anda harus terlebih dahulu mengatur rowHeight properti pada tampilan tabel ke konstanta UITableViewAutomaticDimension. Kemudian, Anda hanya perlu mengaktifkan estimasi ketinggian baris dengan mengatur tampilan tabel estimatedRowHeight properti ke nilai bukan nol, misalnya:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is

Apa yang dilakukan adalah menyediakan tampilan tabel dengan perkiraan sementara / placeholder untuk ketinggian baris sel yang belum ada di layar. Kemudian, ketika sel-sel ini akan bergulir di layar, tinggi baris yang sebenarnya akan dihitung. Untuk menentukan tinggi aktual untuk setiap baris, tampilan tabel secara otomatis menanyakan setiap sel berapa tinggi contentViewharus didasarkan pada lebar tetap yang diketahui dari tampilan konten (yang didasarkan pada lebar tampilan tabel, dikurangi hal-hal tambahan seperti indeks bagian atau tampilan aksesori) dan kendala tata letak otomatis yang telah Anda tambahkan ke tampilan konten sel dan ikhtisar . Setelah ketinggian sel yang sebenarnya ini telah ditentukan, perkiraan ketinggian lama untuk baris diperbarui dengan tinggi aktual yang baru (dan penyesuaian apa pun untuk konten tampilan tabelUkuran / contentOffset dibuat sesuai kebutuhan untuk Anda).

Secara umum, perkiraan yang Anda berikan tidak harus sangat akurat - itu hanya digunakan untuk mengukur ukuran indikator gulir dalam tampilan tabel, dan tampilan tabel melakukan pekerjaan yang baik untuk menyesuaikan indikator gulir untuk perkiraan yang salah saat Anda gulir sel di layar. Anda harus mengatur estimatedRowHeight properti pada tampilan tabel (dalam viewDidLoad atau serupa) ke nilai konstan yaitu tinggi baris "rata-rata". Hanya jika ketinggian baris Anda memiliki variabilitas ekstrim (misalnya berbeda dengan urutan besarnya) dan Anda melihat indikator gulir "melompat" saat Anda menggulir sebaiknya Anda mengimplementasikan tableView:estimatedHeightForRowAtIndexPath: untuk melakukan perhitungan minimal yang diperlukan untuk mengembalikan perkiraan yang lebih akurat untuk setiap baris.

Untuk dukungan iOS 7 (menerapkan ukuran sel otomatis sendiri)

3. Lakukan Layout Pass & Dapatkan Cell Height

Pertama, instantiate instance offscreen dari sel tampilan tabel, satu contoh untuk setiap pengenal reuse, yang digunakan secara ketat untuk perhitungan tinggi. (Layar eksternal berarti referensi sel disimpan di properti / ivar pada pengontrol tampilan dan tidak pernah kembali tableView:cellForRowAtIndexPath: untuk tampilan tabel untuk benar-benar merender di layar.) Selanjutnya, sel harus dikonfigurasikan dengan konten yang tepat (misalnya teks, gambar, dll) yang akan dipegangnya jika ingin ditampilkan dalam tampilan tabel.

Kemudian, paksa sel untuk segera menata subeksinya, lalu gunakan systemLayoutSizeFittingSize: metode pada UITableViewCell's contentView untuk mengetahui berapa tinggi sel yang dibutuhkan. Menggunakan UILayoutFittingCompressedSize untuk mendapatkan ukuran terkecil yang dibutuhkan agar sesuai dengan semua isi sel. Ketinggian kemudian dapat dikembalikan dari tableView:heightForRowAtIndexPath: metode delegasi.

4. Gunakan Taksiran Deret Tinggi

Jika tampilan tabel Anda memiliki lebih dari beberapa lusin baris di dalamnya, Anda akan menemukan bahwa melakukan penyelesaian kendala Layout Otomatis dapat dengan cepat menurunkan utas utama saat pertama kali memuat tampilan tabel, seperti tableView:heightForRowAtIndexPath: dipanggil pada setiap baris pada beban pertama (untuk menghitung ukuran indikator gulir).

Pada iOS 7, Anda dapat (dan tentu saja) menggunakan estimatedRowHeight properti pada tampilan tabel. Apa yang dilakukan adalah menyediakan tampilan tabel dengan perkiraan sementara / placeholder untuk ketinggian baris sel yang belum ada di layar. Kemudian, ketika sel-sel ini akan bergulir di layar, tinggi baris yang sebenarnya akan dihitung (dengan menelepon tableView:heightForRowAtIndexPath:), dan perkiraan tinggi yang diperbarui dengan yang sebenarnya.

Secara umum, perkiraan yang Anda berikan tidak harus sangat akurat - itu hanya digunakan untuk mengukur ukuran indikator gulir dalam tampilan tabel, dan tampilan tabel melakukan pekerjaan yang baik untuk menyesuaikan indikator gulir untuk perkiraan yang salah saat Anda gulir sel di layar. Anda harus mengatur estimatedRowHeight properti pada tampilan tabel (dalam viewDidLoad atau serupa) ke nilai konstan yaitu tinggi baris "rata-rata". Hanya jika ketinggian baris Anda memiliki variabilitas ekstrim (misalnya berbeda dengan urutan besarnya) dan Anda melihat indikator gulir "melompat" saat Anda menggulir sebaiknya Anda mengimplementasikan tableView:estimatedHeightForRowAtIndexPath: untuk melakukan perhitungan minimal yang diperlukan untuk mengembalikan perkiraan yang lebih akurat untuk setiap baris.

5. (Jika Dibutuhkan) Tambahkan Row Height Caching

Jika Anda telah melakukan semua hal di atas dan masih menemukan bahwa kinerja sangat lambat ketika melakukan penyelesaian kendala tableView:heightForRowAtIndexPath:Sayangnya, Anda perlu menerapkan beberapa cache untuk ketinggian sel. (Ini adalah pendekatan yang disarankan oleh para insinyur Apple.) Gagasan umum adalah membiarkan mesin Tata Letak Auto memecahkan kendala pertama kali, kemudian cache tinggi yang dihitung untuk sel itu dan menggunakan nilai cache untuk semua permintaan masa depan untuk ketinggian sel itu. Triknya tentu saja adalah untuk memastikan Anda membersihkan ketinggian cache untuk sel ketika sesuatu terjadi yang dapat menyebabkan tinggi sel berubah - terutama, ini akan terjadi ketika sel konten berubah atau ketika peristiwa penting lainnya terjadi (seperti pengguna menyesuaikan penggeser ukuran teks Jenis Dinamis).

Kode Contoh Generik iOS 7 (dengan banyak komentar yang menarik)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path, depending on the particular layout required (you may have
    // just one, or may have many).
    NSString *reuseIdentifier = ...;

    // Dequeue a cell for the reuse identifier.
    // Note that this method will init and return a new cell if there isn't
    // one available in the reuse pool, so either way after this line of 
    // code you will have a cell with the correct constraints ready to go.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // If you are using multi-line UILabels, don't forget that the 
    // preferredMaxLayoutWidth needs to be set correctly. Do it at this 
    // point if you are NOT doing it within the UITableViewCell subclass 
    // -[layoutSubviews] method. For example: 
    // cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Determine which reuse identifier should be used for the cell at this 
    // index path.
    NSString *reuseIdentifier = ...;

    // Use a dictionary of offscreen cells to get a cell for the reuse 
    // identifier, creating a cell and storing it in the dictionary if one 
    // hasn't already been added for the reuse identifier. WARNING: Don't 
    // call the table view's dequeueReusableCellWithIdentifier: method here 
    // because this will result in a memory leak as the cell is created but 
    // never returned from the tableView:cellForRowAtIndexPath: method!
    UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
    if (!cell) {
        cell = [[YourTableViewCellClass alloc] init];
        [self.offscreenCells setObject:cell forKey:reuseIdentifier];
    }

    // Configure the cell with content for the given indexPath, for example:
    // cell.textLabel.text = someTextForThisCell;
    // ...

    // Make sure the constraints have been set up for this cell, since it 
    // may have just been created from scratch. Use the following lines, 
    // assuming you are setting up constraints from within the cell's 
    // updateConstraints method:
    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];

    // Set the width of the cell to match the width of the table view. This
    // is important so that we'll get the correct cell height for different
    // table view widths if the cell's height depends on its width (due to 
    // multi-line UILabels word wrapping, etc). We don't need to do this 
    // above in -[tableView:cellForRowAtIndexPath] because it happens 
    // automatically when the cell is used in the table view. Also note, 
    // the final width of the cell may not be the width of the table view in
    // some cases, for example when a section index is displayed along 
    // the right side of the table view. You must account for the reduced 
    // cell width.
    cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));

    // Do the layout pass on the cell, which will calculate the frames for 
    // all the views based on the constraints. (Note that you must set the 
    // preferredMaxLayoutWidth on multi-line UILabels inside the 
    // -[layoutSubviews] method of the UITableViewCell subclass, or do it 
    // manually at this point before the below 2 lines!)
    [cell setNeedsLayout];
    [cell layoutIfNeeded];

    // Get the actual height required for the cell's contentView
    CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    // Add an extra point to the height to account for the cell separator, 
    // which is added between the bottom of the cell's contentView and the 
    // bottom of the table view cell.
    height += 1.0;

    return height;
}

// NOTE: Set the table view's estimatedRowHeight property instead of 
// implementing the below method, UNLESS you have extreme variability in 
// your row heights and you notice the scroll indicator "jumping" 
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Do the minimal calculations required to be able to return an 
    // estimated row height that's within an order of magnitude of the 
    // actual height. For example:
    if ([self isTallCellAtIndexPath:indexPath]) {
        return 350.0;
    } else {
        return 40.0;
    }
}

Proyek Contoh

Proyek ini sepenuhnya berfungsi sebagai contoh tampilan tabel dengan ketinggian baris variabel karena sel tampilan tabel berisi konten dinamis dalam UILabel.

Xamarin (C # /. NET)

Jika Anda menggunakan Xamarin, periksa ini proyek sampel disatukan oleh @KentBoogaart.


2265
2018-06-19 08:21



Untuk IOS8 itu sangat sederhana:

override func viewDidLoad() {  
    super.viewDidLoad()

    self.tableView.estimatedRowHeight = 80
    self.tableView.rowHeight = UITableViewAutomaticDimension
}

ATAU

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

Tetapi untuk IOS7, kuncinya adalah menghitung tinggi setelah autolayout,

func calculateHeightForConfiguredSizingCell(cell: GSTableViewCell) -> CGFloat {
    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingExpandedSize).height + 1.0
    return height
}

Penting 

  • Jika beberapa label garis, jangan lupa mengatur numberOfLines untuk 0.

  • Jangan lupa label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)

Kode contoh lengkapnya adalah sini.


122
2018-04-05 09:23



Contoh cepat dari variabel tinggi UITableViewCell

Diperbarui untuk Swift 3

Jawaban Swift William Hu baik, tetapi membantu saya untuk memiliki beberapa langkah sederhana namun terperinci ketika belajar melakukan sesuatu untuk pertama kalinya. Contoh di bawah ini adalah proyek uji saya sambil belajar membuat UITableView dengan ketinggian sel variabel. Saya mendasarkannya contoh UITableView dasar ini untuk Swift.

Proyek yang sudah selesai akan terlihat seperti ini:

enter image description here

Buat proyek baru

Ini bisa jadi hanya Aplikasi Tampilan Tunggal.

Tambahkan kode

Tambahkan file Swift baru ke proyek Anda. Namai itu MyCustomCell. Kelas ini akan menahan outlet untuk tampilan yang Anda tambahkan ke sel Anda di storyboard. Dalam contoh dasar ini kita hanya akan memiliki satu label di setiap sel.

import UIKit
class MyCustomCell: UITableViewCell {
    @IBOutlet weak var myCellLabel: UILabel!
}

Kami akan menghubungkan outlet ini nanti.

Buka ViewController.swift dan pastikan Anda memiliki konten berikut:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    // These strings will be the data for the table view cells
    let animals: [String] = [
        "Ten horses:  horse horse horse horse horse horse horse horse horse horse ",
        "Three cows:  cow, cow, cow",
        "One camel:  camel",
        "Ninety-nine sheep:  sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
        "Thirty goats:  goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]

    // Don't forget to enter this in IB also
    let cellReuseIdentifier = "cell"

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // delegate and data source
        tableView.delegate = self
        tableView.dataSource = self

        // Along with auto layout, these are the keys for enabling variable cell height
        tableView.estimatedRowHeight = 44.0
        tableView.rowHeight = UITableViewAutomaticDimension
    }

    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }

    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
        cell.myCellLabel.text = self.animals[indexPath.row]
        return cell
    }

    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }
}

Catatan penting:

  • Ini adalah dua baris kode berikut (bersama dengan tata-letak otomatis) yang membuat tinggi sel variabel menjadi mungkin:

    tableView.estimatedRowHeight = 44.0
    tableView.rowHeight = UITableViewAutomaticDimension
    

Siapkan storyboard

Tambahkan Tampilan Tabel ke pengontrol tampilan dan gunakan tata-letak otomatis untuk menyematkannya ke empat sisi. Lalu seret Sel Tampilan Tabel ke Tampilan Tabel. Dan ke sel Prototipe, seret Label. Gunakan tata letak otomatis untuk menyematkan label ke empat sisi tampilan konten pada Sel Tampilan Tabel.

enter image description here

Catatan penting:

  • Tata letak otomatis bekerja bersama dengan dua baris kode penting yang saya sebutkan di atas. Jika Anda tidak menggunakan tata letak otomatis, itu tidak akan berhasil.

Pengaturan IB lainnya

Nama dan Identifier kelas khusus

Pilih Sel Tampilan Tabel dan setel kelas khusus MyCustomCell (nama kelas di file Swift yang kami tambahkan). Juga mengatur Identifier yang akan cell(string yang sama yang kami gunakan untuk cellReuseIdentifier dalam kode di atas.

enter image description here

Garis Nol untuk Label

Setel jumlah baris ke 0 di Label Anda. Ini berarti multi-baris dan memungkinkan label untuk mengubah ukuran sendiri berdasarkan kontennya.

enter image description here 

Menghubungkan Outlet

  • Kontrol seret dari Tampilan Tabel di papan cerita ke tableView variabel dalam ViewController kode.
  • Lakukan hal yang sama untuk Label di sel Prototipe Anda ke myCellLabel variabel dalam MyCustomCell kelas.

Jadi

Anda harus dapat menjalankan proyek Anda sekarang dan mendapatkan sel dengan ketinggian variabel.

Catatan

  • Contoh ini hanya berfungsi untuk iOS 8 dan sesudahnya. Jika Anda masih perlu mendukung iOS 7 maka ini tidak akan berfungsi untuk Anda.
  • Sel kustom Anda sendiri dalam proyek Anda di masa depan mungkin memiliki lebih dari satu label. Pastikan Anda mendapatkan semua yang disematkan dengan benar sehingga tata letak otomatis dapat menentukan ketinggian yang tepat untuk digunakan. Anda mungkin juga harus menggunakan ketahanan kompresi vertikal dan pelukan. Lihat artikel ini untuk lebih lanjut tentang itu.
  • Jika Anda tidak menjepit tepi leading dan trailing (kiri dan kanan), Anda mungkin juga perlu mengatur labelnya preferredMaxLayoutWidth sehingga ia tahu kapan harus membungkus garis. Misalnya, jika Anda telah menambahkan Batasan Horizontal Pusat ke label di proyek di atas daripada menyematkan tepi depan dan belakang, maka Anda perlu menambahkan baris ini ke tableView:cellForRowAtIndexPath metode:

     cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
    

Lihat juga


71
2018-06-11 15:18



Saya membungkus solusi iOS7 @ smileyborg dalam kategori

Saya memutuskan untuk membungkus solusi pintar ini dengan @smileyborg menjadi UICollectionViewCell+AutoLayoutDynamicHeightCalculation kategori.

Kategori ini juga memperbaiki masalah yang diuraikan dalam jawaban @ wildmonkey (memuat sel dari nib dan systemLayoutSizeFittingSize: kembali CGRectZero)

Ini tidak memperhitungkan cache apa pun tetapi sesuai dengan kebutuhan saya sekarang. Jangan ragu untuk menyalin, menempel, dan meretasnya.

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.h

#import <UIKit/UIKit.h>

typedef void (^UICollectionViewCellAutoLayoutRenderBlock)(void);

/**
 *  A category on UICollectionViewCell to aid calculating dynamic heights based on AutoLayout contraints.
 *
 *  Many thanks to @smileyborg and @wildmonkey
 *
 *  @see stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
 */
@interface UICollectionViewCell (AutoLayoutDynamicHeightCalculation)

/**
 *  Grab an instance of the receiving type to use in order to calculate AutoLayout contraint driven dynamic height. The method pulls the cell from a nib file and moves any Interface Builder defined contrainsts to the content view.
 *
 *  @param name Name of the nib file.
 *
 *  @return collection view cell for using to calculate content based height
 */
+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name;

/**
 *  Returns the height of the receiver after rendering with your model data and applying an AutoLayout pass
 *
 *  @param block Render the model data to your UI elements in this block
 *
 *  @return Calculated constraint derived height
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width;

/**
 *  Directly calls `heightAfterAutoLayoutPassAndRenderingWithBlock:collectionViewWidth` assuming a collection view width spanning the [UIScreen mainScreen] bounds
 */
- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block;

@end

UICollectionViewCell + AutoLayoutDynamicHeightCalculation.m

#import "UICollectionViewCell+AutoLayout.h"

@implementation UICollectionViewCell (AutoLayout)

#pragma mark Dummy Cell Generator

+ (instancetype)heightCalculationCellFromNibWithName:(NSString *)name
{
    UICollectionViewCell *heightCalculationCell = [[[NSBundle mainBundle] loadNibNamed:name owner:self options:nil] lastObject];
    [heightCalculationCell moveInterfaceBuilderLayoutConstraintsToContentView];
    return heightCalculationCell;
}

#pragma mark Moving Constraints

- (void)moveInterfaceBuilderLayoutConstraintsToContentView
{
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        [self removeConstraint:constraint];
        id firstItem = constraint.firstItem == self ? self.contentView : constraint.firstItem;
        id secondItem = constraint.secondItem == self ? self.contentView : constraint.secondItem;
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:firstItem
                                                                     attribute:constraint.firstAttribute
                                                                     relatedBy:constraint.relation
                                                                        toItem:secondItem
                                                                     attribute:constraint.secondAttribute
                                                                    multiplier:constraint.multiplier
                                                                      constant:constraint.constant]];
    }];
}

#pragma mark Height

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block
{
    return [self heightAfterAutoLayoutPassAndRenderingWithBlock:block
                                            collectionViewWidth:CGRectGetWidth([[UIScreen mainScreen] bounds])];
}

- (CGFloat)heightAfterAutoLayoutPassAndRenderingWithBlock:(UICollectionViewCellAutoLayoutRenderBlock)block collectionViewWidth:(CGFloat)width
{
    NSParameterAssert(block);

    block();

    [self setNeedsUpdateConstraints];
    [self updateConstraintsIfNeeded];

    self.bounds = CGRectMake(0.0f, 0.0f, width, CGRectGetHeight(self.bounds));

    [self setNeedsLayout];
    [self layoutIfNeeded];

    CGSize calculatedSize = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

    return calculatedSize.height;

}

@end

Contoh penggunaan:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MYSweetCell *cell = [MYSweetCell heightCalculationCellFromNibWithName:NSStringFromClass([MYSweetCell class])];
    CGFloat height = [cell heightAfterAutoLayoutPassAndRenderingWithBlock:^{
        [(id<MYSweetCellRenderProtocol>)cell renderWithModel:someModel];
    }];
    return CGSizeMake(CGRectGetWidth(self.collectionView.bounds), height);
}

Syukurlah kita tidak harus melakukan jazz ini di iOS8, tetapi sekarang ada!


60
2018-02-10 19:41



Inilah solusi saya. Anda harus memberi tahu TableView perkiraanTinggi sebelum memuat tampilan. Kalau tidak, tidak akan bisa berperilaku seperti yang diharapkan.

- (void)viewWillAppear:(BOOL)animated {
    _messageField.delegate = self;
    _tableView.estimatedRowHeight = 65.0;
    _tableView.rowHeight = UITableViewAutomaticDimension;
}

43
2017-11-11 16:52



Solusi yang diajukan oleh @smileyborg hampir sempurna. Jika Anda memiliki sel khusus dan Anda menginginkan satu atau lebih UILabel dengan ketinggian dinamis maka systemLayoutSizeFittingSize metode yang dikombinasikan dengan AutoLayout diaktifkan mengembalikan a CGSizeZero kecuali Anda memindahkan semua batasan sel Anda dari sel ke contentView-nya (seperti yang disarankan oleh @TomSwift di sini Bagaimana cara mengubah ukuran superview agar sesuai dengan semua subview dengan autolayout?).

Untuk melakukannya Anda perlu memasukkan kode berikut dalam implementasi UITableViewCell khusus Anda (terima kasih kepada @Adrian).

- (void)awakeFromNib{
    [super awakeFromNib];
    for (NSLayoutConstraint *cellConstraint in self.constraints) {
        [self removeConstraint:cellConstraint];
        id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
        id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
        NSLayoutConstraint *contentViewConstraint =
        [NSLayoutConstraint constraintWithItem:firstItem
                                 attribute:cellConstraint.firstAttribute
                                 relatedBy:cellConstraint.relation
                                    toItem:seccondItem
                                 attribute:cellConstraint.secondAttribute
                                multiplier:cellConstraint.multiplier
                                  constant:cellConstraint.constant];
        [self.contentView addConstraint:contentViewConstraint];
    }
}

Mencampur jawaban @smileyborg dengan ini seharusnya berhasil.


42
2017-12-03 02:23



Sebuah gotcha yang cukup penting saya hanya berlari ke posting sebagai jawaban.

Jawaban @ smileyborg sebagian besar benar. Namun, jika Anda memiliki kode apa pun di layoutSubviews metode kelas sel kustom Anda, misalnya pengaturan preferredMaxLayoutWidth, maka tidak akan dijalankan dengan kode ini:

[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];

Ini membingungkan saya untuk sementara. Lalu saya menyadari itu karena mereka hanya memicu layoutSubview di contentView, bukan sel itu sendiri.

Kode kerja saya terlihat seperti ini:

TCAnswerDetailAppSummaryCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailAppSummaryCell"];
[cell configureWithThirdPartyObject:self.app];
[cell layoutIfNeeded];
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;

Perhatikan bahwa jika Anda membuat sel baru, saya cukup yakin Anda tidak perlu menelepon setNeedsLayout sebagaimana seharusnya sudah diatur. Dalam kasus di mana Anda menyimpan referensi ke sel, Anda mungkin harus menyebutnya. Either way seharusnya tidak menyakiti apa pun.

Tip lain jika Anda menggunakan subclass sel di mana Anda mengatur hal-hal seperti preferredMaxLayoutWidth. Seperti @smileyborg menyebutkan, "sel tampilan tabel Anda belum memiliki lebar tetap ke lebar tampilan tabel". Ini benar, dan masalah jika Anda melakukan pekerjaan Anda di subkelas Anda dan bukan di pengontrol tampilan. Namun Anda cukup mengatur frame sel pada titik ini menggunakan lebar tabel:

Misalnya dalam perhitungan tinggi:

self.summaryCell = [self.tableView dequeueReusableCellWithIdentifier:@"TCAnswerDetailDefaultSummaryCell"];
CGRect oldFrame = self.summaryCell.frame;
self.summaryCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, self.tableView.frame.size.width, oldFrame.size.height);

(Saya kebetulan cache sel khusus ini untuk digunakan kembali, tapi itu tidak relevan).


22
2018-03-05 15:53