İstediğiniz şey UITableView gibi bir düzen oluşturmak için UICollectionView kullanmanın bir yolu gibi görünüyor. Gerçekten istediğiniz buysa, bunu yapmanın doğru yolu, özel bir UICollectionViewLayout alt sınıfı (belki SBTableLayout gibi bir şey ) kullanmaktır.
Öte yandan, bunu varsayılan UICollectionViewFlowLayout ile yapmanın temiz bir yolu olup olmadığını gerçekten soruyorsanız, o zaman hiçbir yolu olmadığına inanıyorum. İOS8'in kendi boyutlarını belirleyen hücreleriyle bile, bu kolay değildir. Sizin de söylediğiniz gibi temel sorun, akış düzeninin mekanizmasının bir boyutu düzeltip diğerinin yanıt vermesine izin vermemesidir. (Buna ek olarak, yapabilseniz bile, çok satırlı etiketleri boyutlandırmak için iki düzen geçişine ihtiyaç duyma konusunda ek bir karmaşıklık olacaktır. Bu, kendi boyutlarını belirleyen hücrelerin systemLayoutSizeFittingSize'a yapılan tek bir çağrı aracılığıyla tüm boyutlandırmayı nasıl hesaplamak istediğine uymayabilir.)
Bununla birlikte, kendi boyutlarını belirleyen ve koleksiyon görünümünün genişliğine doğal olarak yanıt veren hücrelerle akış düzenine sahip bir tablo görünümüne benzer düzen oluşturmak istiyorsanız, elbette bu mümkündür. Hâlâ dağınık bir yol var. Bunu bir "boyutlandırma hücresi" ile yaptım, yani denetleyicinin yalnızca hücre boyutlarını hesaplamak için sakladığı görüntülenmeyen bir UICollectionViewCell.
Bu yaklaşımın iki bölümü var. İlk bölüm, koleksiyon görünümü temsilcisinin koleksiyon görünümünün genişliğini alarak ve hücrenin yüksekliğini hesaplamak için boyutlandırma hücresini kullanarak doğru hücre boyutunu hesaplaması içindir.
UICollectionViewDelegateFlowLayout'unuzda, aşağıdaki gibi bir yöntem uygularsınız:
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
}
Bu, koleksiyon görünümünün hücrenin genişliğini çevreleyen koleksiyon görünümüne göre ayarlamasına neden olur, ancak daha sonra boyutlandırma hücresinden yüksekliği hesaplamasını ister.
İkinci kısım, boyutlandırma hücresinin yüksekliği hesaplamak için kendi AL sınırlamalarını kullanmasını sağlamaktır. Bu, çok satırlı UILabel'in etkili bir şekilde iki aşamalı bir yerleşim süreci gerektirmesi nedeniyle olması gerekenden daha zor olabilir. İş şu yöntemle yapılır preferredLayoutSizeFittingSize
:
/*
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
}
(Bu kod, "Gelişmiş Koleksiyon Görünümü" ndeki WWDC2014 oturumunun örnek kodundan Apple örnek kodundan uyarlanmıştır.)
Dikkat edilmesi gereken birkaç nokta. Etiket genişliğini hesaplamak ve ayarlamak için tüm hücrenin düzenini zorlamak için layoutIfNeeded () kullanıyor. Ama bu yeterli değil. Ayrıca preferredMaxLayoutWidth
etiketin Otomatik Yerleşim ile bu genişliği kullanması için ayarlamanız gerektiğine inanıyorum . Ve ancak o zaman systemLayoutSizeFittingSize
hücrenin etiketi dikkate alırken yüksekliğini hesaplamasını sağlamak için kullanabilirsiniz .
Bu yaklaşımı beğendim mi? Hayır!! Çok karmaşık geliyor ve iki kez düzen yapıyor. Ancak performans bir sorun haline gelmediği sürece, mizanpajı kodda iki kez tanımlamak yerine çalışma zamanında iki kez gerçekleştirmeyi tercih ederim, bu da tek alternatif gibi görünüyor.
Umudum, sonunda kendi kendine boyutlandırılan hücrelerin farklı şekilde çalışacağı ve tüm bunların çok daha basit hale geleceği.
İş yerinde gösteren örnek proje .
Ama neden sadece kendi kendine boyutlandırma hücreleri kullanmıyorsunuz?
Teorik olarak, iOS8'in "kendi kendini boyutlandıran hücreler" için sunduğu yeni olanaklar bunu gereksiz hale getirmelidir. Otomatik Düzen (AL) ile bir hücre tanımladıysanız, koleksiyon görünümü, kendisini boyutlandırmasına ve doğru bir şekilde yerleştirmesine izin verecek kadar akıllı olmalıdır. Pratikte, bunu çok satırlı etiketlerle çalıştıran hiçbir örnek görmedim. Bence bu kendini boyutlandırma hücre mekanizması hala arabası olmasından kaynaklanır.
Ancak bahse girerim bunun büyük ölçüde Otomatik Yerleşim ve etiketlerin olağan yanıltıcılığından kaynaklanmaktadır, bu da UILabels'ın temelde iki aşamalı bir düzen süreci gerektirmesidir. Her iki adımı da kendi kendine boyutlandıran hücrelerle nasıl gerçekleştirebileceğiniz net değil.
Ve dediğim gibi, bu gerçekten farklı bir düzen için bir iş. Genişliği sabitlemek ve yüksekliğini seçmelerine izin vermek yerine boyutu olan şeyleri konumlandırması akış düzeninin özünün bir parçasıdır.
Peki ya preferLayoutAttributesFittingAttributes:?
preferredLayoutAttributesFittingAttributes:
Yöntem kırmızı ringa, sanırım. Bu, yalnızca yeni kendi kendine boyutlandırma hücre mekanizmasıyla kullanılacak. Yani mekanizma güvenilmez olduğu sürece cevap bu değil.
Ve systemlayoutSizeFittingSize ile neler oluyor :?
Haklısın, dokümanlar kafa karıştırıcı.
Üzerine dokümanlar systemLayoutSizeFittingSize:
ve systemLayoutSizeFittingSize:withHorizontalFittingPriority:verticalFittingPriority:
hem sadece geçirmesi gerektiğini düşündürmektedir UILayoutFittingCompressedSize
ve UILayoutFittingExpandedSize
olarak targetSize
. Ancak yöntem imzasının kendisi, üstbilgi yorumları ve işlevlerin davranışı, bunların targetSize
parametrenin tam değerine yanıt verdiklerini gösterir .
Aslında, UICollectionViewFlowLayoutDelegate.estimatedItemSize
yeni kendi kendine boyutlandırma hücre mekanizmasını etkinleştirmek için ayarlarsanız , bu değer targetSize olarak aktarılıyor gibi görünür. Ve ile UILabel.systemLayoutSizeFittingSize
aynı değerleri döndürüyor gibi görünüyor UILabel.sizeThatFits
. Argümanının systemLayoutSizeFittingSize
kaba bir hedef olduğu ve argümanının sizeThatFits:
maksimum sınırlayıcı boyut olduğu varsayıldığında , bu şüphelidir .
Daha fazla kaynak
Böyle rutin bir gereksinimin "araştırma kaynakları" gerektirmesi gerektiğini düşünmek üzücü olsa da, bence öyle. İyi örnekler ve tartışmalar: