Sepertinya yang Anda minta adalah cara menggunakan UICollectionView untuk menghasilkan layout seperti UITableView. Jika itu yang Anda inginkan, cara yang tepat untuk melakukannya adalah dengan subkelas UICollectionViewLayout kustom (mungkin sesuatu seperti SBTableLayout ).
Di sisi lain, jika Anda benar-benar bertanya apakah ada cara yang bersih untuk melakukan ini dengan UICollectionViewFlowLayout default, maka saya yakin tidak mungkin. Bahkan dengan sel self-sizing iOS8, itu tidak langsung. Masalah mendasar, seperti yang Anda katakan, adalah bahwa mesin tata letak aliran tidak menyediakan cara untuk memperbaiki satu dimensi dan membiarkan dimensi lain merespons. (Selain itu, meskipun Anda bisa, akan ada kerumitan tambahan seputar perlunya dua penerusan tata letak untuk mengukur label multi-baris. Ini mungkin tidak sesuai dengan cara sel yang mengukur sendiri ingin menghitung semua ukuran melalui satu panggilan ke systemLayoutSizeFittingSize.)
Namun, jika Anda masih ingin membuat tata letak seperti tampilan tabel dengan tata letak aliran, dengan sel yang menentukan ukurannya sendiri, dan merespons secara alami lebar tampilan koleksi, tentu saja hal itu dimungkinkan. Masih ada cara yang berantakan. Saya telah melakukannya dengan "sel ukuran", yaitu UICollectionViewCell yang tidak ditampilkan yang disimpan oleh pengontrol hanya untuk menghitung ukuran sel.
Ada dua bagian dalam pendekatan ini. Bagian pertama adalah untuk delegasi tampilan koleksi untuk menghitung ukuran sel yang benar, dengan mengambil lebar tampilan koleksi dan menggunakan sel ukuran untuk menghitung tinggi sel.
Di UICollectionViewDelegateFlowLayout Anda, Anda menerapkan metode seperti ini:
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
// NOTE: here is where we say we want cells to use the width of the collection view
let requiredWidth = collectionView.bounds.size.width
// NOTE: here is where we ask our sizing cell to compute what height it needs
let targetSize = CGSize(width: requiredWidth, height: 0)
/// NOTE: populate the sizing cell's contents so it can compute accurately
self.sizingCell.label.text = items[indexPath.row]
let adequateSize = self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
return adequateSize
}
Ini akan menyebabkan tampilan koleksi menyetel lebar sel berdasarkan tampilan koleksi terlampir, tetapi kemudian meminta sel ukuran untuk menghitung tinggi.
Bagian kedua adalah mendapatkan sel ukuran untuk menggunakan batasan AL-nya sendiri untuk menghitung ketinggian. Ini bisa lebih sulit dari yang seharusnya, karena cara UILabel multi-baris secara efektif membutuhkan proses tata letak dua tahap. Pekerjaan dilakukan dengan metode preferredLayoutSizeFittingSize
, yang seperti ini:
/*
Computes the size the cell will need to be to fit within targetSize.
targetSize should be used to pass in a width.
the returned size will have the same width, and the height which is
calculated by Auto Layout so that the contents of the cell (i.e., text in the label)
can fit within that width.
*/
func preferredLayoutSizeFittingSize(targetSize:CGSize) -> CGSize {
// save original frame and preferredMaxLayoutWidth
let originalFrame = self.frame
let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
// assert: targetSize.width has the required width of the cell
// step1: set the cell.frame to use that width
var frame = self.frame
frame.size = targetSize
self.frame = frame
// step2: layout the cell
self.setNeedsLayout()
self.layoutIfNeeded()
self.label.preferredMaxLayoutWidth = self.label.bounds.size.width
// assert: the label's bounds and preferredMaxLayoutWidth are set to the width required by the cell's width
// step3: compute how tall the cell needs to be
// this causes the cell to compute the height it needs, which it does by asking the
// label what height it needs to wrap within its current bounds (which we just set).
let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
// assert: computedSize has the needed height for the cell
// Apple: "Only consider the height for cells, because the contentView isn't anchored correctly sometimes."
let newSize = CGSize(width:targetSize.width,height:computedSize.height)
// restore old frame and preferredMaxLayoutWidth
self.frame = originalFrame
self.label.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
return newSize
}
(Kode ini diadaptasi dari kode contoh Apple dari kode contoh sesi WWDC2014 pada "Tampilan Koleksi Lanjutan".)
Beberapa hal yang perlu diperhatikan. Ini menggunakan layoutIfNeeded () untuk memaksa tata letak seluruh sel, untuk menghitung dan menyetel lebar label. Tapi itu belum cukup. Saya yakin Anda juga perlu mengatur preferredMaxLayoutWidth
agar label akan menggunakan lebar itu dengan Tata Letak Otomatis. Dan hanya dengan begitu Anda dapat menggunakansystemLayoutSizeFittingSize
agar sel menghitung tingginya sambil mempertimbangkan label.
Apakah saya menyukai pendekatan ini? Tidak!! Rasanya terlalu rumit, dan tata letak dua kali. Namun selama kinerja tidak menjadi masalah, saya lebih suka melakukan tata letak dua kali pada waktu proses daripada harus mendefinisikannya dua kali dalam kode, yang tampaknya menjadi satu-satunya alternatif lain.
Harapan saya adalah bahwa pada akhirnya sel yang mengukur sendiri akan bekerja secara berbeda dan ini semua akan menjadi jauh lebih sederhana.
Contoh proyek yang menunjukkannya di tempat kerja.
Tapi mengapa tidak menggunakan sel yang mengukur sendiri?
Secara teori, fasilitas baru iOS8 untuk "sel yang mengukur sendiri" seharusnya membuat hal ini tidak perlu. Jika Anda telah menentukan sel dengan Tata Letak Otomatis (AL), tampilan koleksi harus cukup pintar untuk membiarkannya mengukur sendiri dan menata dirinya sendiri dengan benar. Dalam praktiknya, saya belum melihat contoh apa pun yang membuat ini berfungsi dengan label multi-garis. Saya pikir ini sebagian karena mekanisme sel yang mengukur sendiri masih bermasalah.
Tapi saya berani bertaruh itu sebagian besar karena kerumitan Tata Letak Otomatis dan label yang biasa, yaitu UILabel pada dasarnya memerlukan proses tata letak dua langkah. Tidak jelas bagi saya bagaimana Anda dapat melakukan kedua langkah dengan sel yang mengukur sendiri.
Dan seperti yang saya katakan, ini benar-benar pekerjaan untuk tata letak yang berbeda. Ini adalah bagian dari esensi tata letak aliran yang memposisikan benda-benda yang memiliki ukuran, daripada memperbaiki lebar dan membiarkan mereka memilih tingginya.
Dan bagaimana dengan preferLayoutAttributesFittingAttributes:?
The preferredLayoutAttributesFittingAttributes:
Metode adalah ikan merah, saya pikir. Itu hanya untuk digunakan dengan mekanisme sel ukuran sendiri yang baru. Jadi ini bukan jawabannya selama mekanismenya tidak bisa diandalkan.
Dan ada apa dengan systemlayoutSizeFittingSize :?
Anda benar, dokumen membingungkan.
Dokumen di systemLayoutSizeFittingSize:
dan systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
keduanya menyarankan agar Anda hanya meneruskan UILayoutFittingCompressedSize
dan UILayoutFittingExpandedSize
sebagai filetargetSize
. Namun, tanda tangan metode itu sendiri, komentar header, dan perilaku fungsi menunjukkan bahwa mereka merespons nilai targetSize
parameter yang tepat.
Faktanya, jika Anda menyetel UICollectionViewFlowLayoutDelegate.estimatedItemSize
, untuk mengaktifkan mekanisme sel self-sizing baru, nilai tersebut tampaknya diteruskan sebagai targetSize. Dan UILabel.systemLayoutSizeFittingSize
tampaknya mengembalikan nilai yang sama persis seperti UILabel.sizeThatFits
. Hal ini mencurigakan, mengingat dalil ke systemLayoutSizeFittingSize
seharusnya menjadi sasaran kasar dan dalil untuksizeThatFits:
seharusnya menjadi ukuran yang membatasi maksimum.
Sumber Daya Lainnya
Meskipun menyedihkan untuk berpikir bahwa persyaratan rutin seperti itu harus membutuhkan "sumber daya penelitian", saya rasa memang demikian. Contoh dan diskusi yang bagus adalah:
Ada cara yang lebih bersih untuk melakukan ini daripada beberapa jawaban lain di sini, dan itu bekerja dengan baik. Ini harus berkinerja baik (tampilan koleksi dimuat dengan cepat, tidak ada tata letak otomatis yang tidak perlu, dll.), Dan tidak memiliki 'angka ajaib' seperti lebar tampilan koleksi yang tetap. Mengubah ukuran tampilan koleksi, misalnya pada rotasi, dan kemudian membatalkan tata letak juga akan bekerja dengan baik.
1. Buat subclass tata letak aliran berikut
2. Daftarkan tampilan koleksi Anda untuk penentuan ukuran otomatis
3. Gunakan lebar yang telah ditentukan + tinggi khusus di subkelas sel Anda
sumber
Cara sederhana untuk melakukannya di iOS 9 dalam beberapa baris kode - contoh cara horizontal (memperbaiki ketinggiannya ke tinggi Tampilan Koleksi):
Masukkan Tata Letak Alur Tampilan Koleksi Anda dengan
estimatedItemSize
untuk mengaktifkan sel swa-ukuran:Menerapkan Delegasi Tata Letak Tampilan Koleksi (di Pengontrol Tampilan Anda sebagian besar waktu)
collectionView:layout:sizeForItemAtIndexPath:
,. Sasarannya di sini adalah untuk menyetel tinggi (atau lebar) tetap ke dimensi Tampilan Koleksi. Nilai 10 bisa apa saja, tetapi Anda harus menyetelnya ke nilai yang tidak melanggar batasan:Timpa
preferredLayoutAttributesFittingAttributes:
metode sel kustom Anda , bagian ini sebenarnya menghitung lebar sel dinamis Anda berdasarkan batasan Tata Letak Otomatis dan tinggi yang baru saja Anda tetapkan:sumber
UILabel
, yang pemutusan garisnya memengaruhi tinggi intrinsik sel? Ini adalah kasus umum yang biasanya membutuhkan semua backflip yang saya jelaskan. Saya bertanya-tanya apakah iOS telah memperbaikinya sejak jawaban saya.Coba perbaiki lebar Anda di atribut tata letak pilihan:
Tentu Anda juga ingin memastikan bahwa Anda mengatur tata letak Anda dengan benar untuk:
Inilah sesuatu yang saya pakai di Github yang menggunakan sel lebar konstan dan mendukung tipe dinamis sehingga ketinggian sel diperbarui saat ukuran font sistem berubah.
sumber
YA itu dapat dilakukan dengan menggunakan tata letak otomatis secara terprogram dan dengan menetapkan batasan di storyboard atau xib. Anda perlu menambahkan batasan untuk ukuran lebar agar tetap konstan dan menyetel tinggi lebih dari atau sama dengan.
http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/
http://www.cocoanetics.com/2013/08/variable-sized-items-in-uicollectionview/
Semoga ini akan terjadi membantu dan menyelesaikan masalah Anda.
sumber
cellForItemAtIndexPath
memperbarui kendala:cell.constraintItemWidth.constant = UIScreen.main.bounds.width
. Aplikasi saya hanya untuk potret. Anda mungkin inginreloadData
setelah perubahan orientasi untuk memperbarui batasan.