FlowLayout ile UICollectionView yüksekliği nasıl belirlenir


118

Bir UICollectionViewile bir var UICollectionViewFlowLayoutve içerik boyutunu hesaplamak istiyorum ( intrinsicContentSizeyüksekliğini AutoLayout aracılığıyla ayarlamak için gereken geri dönüş için).

Problemler şudur: Tüm hücreler için sabit ve eşit yüksekliğe sahip olsam bile, içinde kaç tane "satır" / satırım olduğunu bilmiyorum UICollectionView. Veri kaynağımdaki öğelerin sayısına göre de bu sayımı belirleyemiyorum, çünkü veri öğelerini temsil eden hücrelerin genişliği değişiklik gösterdiğinden, sonuç olarak UICollectionView.

Resmi belgelerde bu konuyla ilgili herhangi bir ipucu bulamadığım ve Google'da arama beni daha ileri götürmediğinden, herhangi bir yardım ve fikir çok takdir edilecektir.

Yanıtlar:


254

Oha! Nedense, saatler süren araştırmalardan sonra soruma oldukça kolay bir yanıt buldum: Bulabildiğim tüm belgeleri karıştırarak tamamen yanlış yerde arıyordum UICollectionView.

Altta yatan düzeni basit ve kolay çözüm yalan: Sadece çağrı collectionViewContentSizesizin üzerinde myCollectionView.collectionViewLayoutmülkiyet ve siz içeriğin yüksekliğini ve genişliğini almak CGSize. Bu kadar kolay.


1
Dur, UITableView benzer bir şey vardır dilek
Chris Chen

1
Ignatus, @jbandi, Chris ve diğerleri, bunu biraz daha açabilir misiniz? Öğeler arası geniş aralıklı yatay kaydırma yönüyle test ettiğimde (öğelerin yatay olarak yerleştirilmesi için), koleksiyon görünümü geçersiz bir iç içerik boyutu (-1, -1) döndürdü ve collectionViewContentSize, etkili olanı döndürdü Koleksiyon görünümünün sınırları - boşlukla çevrili tek bir satır olduğu dikkate alınmadan. Kısacası, pek içkin değildi. Teşekkürler!
Chris Conover

8
CollectionViewContentSize ile kaydırma görünümünün contentSize arasındaki fark nedir?
rounak

ViewDidLoad'da koleksiyon görünümünün contentSize öğesini çağırdığınızda, koleksiyon görünümünün çerçevesini döndürür, collectionViewContentSize doğru içerik boyutunu döndürür.
Fury

1
Kimin için yardımcı olabilir: Çalışmasını sağlamak için bundan önce bir "layoutIfNeeded ()" yapmam gerekiyordu.
asanli

37

Otomatik düzen kullanıyorsanız , bir alt sınıf oluşturabilirsiniz.UICollectionView

Aşağıdaki kodu kullanırsanız, koleksiyon görünümünün içeriğine bağlı olarak değişeceğinden, koleksiyon görünümü için herhangi bir yükseklik kısıtlaması belirtmeniz gerekmez.

Uygulama aşağıda verilmiştir:

@interface DynamicCollectionView : UICollectionView

@end

@implementation DynamicCollectionView

- (void) layoutSubviews
{
    [super layoutSubviews];

    if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize]))
    {
        [self invalidateIntrinsicContentSize];
    }
}

- (CGSize)intrinsicContentSize
{
    CGSize intrinsicContentSize = self.contentSize;

    return intrinsicContentSize;
}

@end

12
benim için işe yaramadı ... CollectionView'ımı bir UITableViewCell içinde kullanıyorum ve iOS8 UITableViewAutomaticDimension kullanarak koleksiyon görünümü büyüdükçe tablo görünümünün de yükselmesini istiyorum Ama koleksiyon görünümüm küçük kalıyor :(
Georg

Muhteşem. Bir UIScrollView içindeki UICollectionView için kısa ve tatlı bir çözüm!
dotToString

2
Koleksiyon görünümünde kaydırmayı ve kaydırma çubuklarını devre dışı bırakmak önemli bir şey
Georg

1
Georg ile aynı sorun, 400'den fazla olması gerektiğinde yaklaşık 50
piksel

3
Evet, koleksiyon görünümünü "kaydırılabilir değil, küçük çubuk yok" olarak ayarladım ve sonra yeniden yükledikten sonra tablo görünümünün içeriği ayarlandı. Ayrıca koleksiyon görünümü, içerik boyutunu intrinsicContentSize olarak döndüren bir alt sınıftır. Umarım yardımcı olur!
Georg

25

Şuradan viewDidAppearalabilirsiniz:

float height = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;

Belki verileri yeniden yüklediğinizde, daha sonra yeni verilerle yeni bir yükseklik hesaplamanız gerektiğinde, bunu elde edebilirsiniz: CollectionViewbitmiş yeniden yükleme verilerinizi dinlemek için şu adresten gözlemci ekleyin viewdidload:

[self.myCollectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL];

Ardından yeni yükseklik elde etmek veya koleksiyon görünümü yeniden yüklemeyi tamamladıktan sonra herhangi bir şey yapmak için aşağıdaki işlevi ekleyin:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
{
    //Whatever you do here when the reloadData finished
    float newHeight = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;    
}

Ve gözlemciyi kaldırmayı unutmayın:

[self.myCollectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];

11

Swift ile user1046037 yanıtı ...

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize() {
            invalidateIntrinsicContentSize()
        }
    }

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}

Mükemmel çalışıyor ama UILabelyukarıdaki gibi bir şeye sahipseniz, yukarıdaki collectionViewdiğer öğelerin üzerinden geçen içerik, başka bir çözümünüz var mı?
KolaCaine

11

User1046037 cevabı için Swift 3 kodu

import UIKit

class DynamicCollectionView: UICollectionView {

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
            self.invalidateIntrinsicContentSize()
        }

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}

Ben daha yeniyim ve bunu ViewController'imde nerede ve nasıl kullanacağımdan emin değilim, Lütfen cevabım ile bir düzenleme yapabilir misiniz .....?
Abirami Bala

1
bu sınıfı Storyboard'daki koleksiyon görünümünüze ayarlayın
Mohammad Sadiq Shaikh

7

Swift 4

class DynamicCollectionView: UICollectionView {
  override func layoutSubviews() {
    super.layoutSubviews()
    if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
      self.invalidateIntrinsicContentSize()
    }
  }

  override var intrinsicContentSize: CGSize {
    return collectionViewLayout.collectionViewContentSize
  }
}

1
İPhone 6/7 plus ve XR, XS-MAX
Rahul K Rajan'da

7

Swift 4.2

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        return self.contentSize
    }
}

4

Bu soruyu cevaplamak için çok geç ama son zamanlarda bu konuyu ele aldım.
@ lee'in yukarıdaki cevabı cevabımı almam için bana çok yardımcı oluyor. Ama bu cevap objektif-c ile sınırlı ve ben hızlı çalışıyordum .
@lee Cevabınıza referansla, hızlı kullanıcının bu sorunu kolayca çözmesine izin vermeme izin verin. Bunun için lütfen aşağıdaki adımları izleyin:

CGFloatBildirim bölümünde bir değişken bildirin:

var height : CGFloat!

Şuradan viewDidAppearalabilirsiniz:

height = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

Eğer Belki reload: veriler daha sonra yeni verilerle yeni bir yükseklik hesaplamak gerekir o zaman bunu elde edebilirsiniz addObserverzaman sizi dinlemesini CollectionViewbitmiş yeniden verilerini de viewWillAppear:

override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)
        ....
        ....
        self.shapeCollectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.Old, context: nil)
    }

Ardından, yeni yükseklik elde etmek veya collectionviewyeniden yüklemeyi bitirdikten sonra herhangi bir şey yapmak için aşağıdaki işlevi ekleyin :

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        let newHeight : CGFloat = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

        var frame : CGRect! = self.myCollectionView.frame
        frame.size.height = newHeight

        self.myCollectionView.frame = frame
    }

Ve gözlemciyi kaldırmayı unutmayın:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)
    ....
    ....
    self.myCollectionView.removeObserver(self, forKeyPath: "contentSize")
}

Umarım bu, sorununuzu hızlı bir şekilde çözmenize yardımcı olur.


@ShikhaSharma: "ViewDidDisappear" yöntemine removeObserver kodunu ekleyebilirsiniz; bu, yalnızca görünümün kaybolduğunu onayladıktan sonra gözlemciyi kaldırır. Lütfen güncellenmiş yanıtı kontrol edin.
Er. Vihar

Bu hatayı alıyorum: Bir -observeValueForKeyPath: ofObject: değişiklik: bağlam: ileti alındı, ancak işlenmedi.
Shikha Sharma

@ShikhaSharma: Üzgünüm, yöntemi belirtmek benim hatam. Removeobserver kodunu viewWillDisappear'a koymayı deneyin.
Er. Vihar

1

Bu cevap @Er'e dayanmaktadır. Vihar'ın cevabı. Çözümü RxSwift kullanarak kolayca uygulayabilirsiniz

     collectionView.rx.observe(CGSize.self , "contentSize").subscribe(onNext: { (size) in
        print("sizer \(size)")
    }).disposed(by: rx.disposeBag)

0

@ User1046037'nin hızlı sürümü:

public class DynamicCollectionView: UICollectionView {

    override public func layoutSubviews() {
        super.layoutSubviews()
        if !bounds.size.equalTo(intrinsicContentSize) {
            invalidateIntrinsicContentSize()
        }
    }

    override public var intrinsicContentSize: CGSize {
        let intrinsicContentSize: CGSize = contentSize
        return intrinsicContentSize
    }
}

0

Swift 4.2, Xcode 10.1, iOS 12.1:

Bazı nedenlerden dolayı, collectionView.contentSize.heightkoleksiyon görünümümün çözümlenmiş yüksekliğinden daha küçük görünüyordu. İlk olarak, süpervizörün yüksekliğinin 1 / 2'sine göre bir otomatik düzen kısıtlaması kullanıyordum. Bunu düzeltmek için, kısıtlamayı görünümün "güvenli alanı" na göre değiştirdim.

Bu, hücre yüksekliğini şunu kullanarak koleksiyon görünümümü dolduracak şekilde ayarlamama izin verdi collectionView.contentSize.height:

private func setCellSize() {
    let height: CGFloat = (collectionView.contentSize.height) / CGFloat(numberOfRows)
    let width: CGFloat = view.frame.width - CGFloat(horizontalCellMargin * 2)

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: width, height: height)
}

Önce

süpervizyona göre yükseklik kısıtlaması kötü simülatör sonucu

Sonra

güvenli alana göre yükseklik kısıtlaması iyi simülatör sonucu


0

varsayarsak CollectionView'ın olarak adlandırılır CollectionView'ın aşağıdaki gibi yüksekliğini alabilir.

let height = collectionView.collectionViewLayout.collectionViewContentSize.height

0

Ek olarak ,reloadDatayüksekliği almadan önce aramanız daha iyi olur :

theCollectionView.collectionViewLayout.collectionViewContentSize;
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.