FlowLayout으로 UICollectionView의 높이를 결정하는 방법


118

나는있어 UICollectionView과를 UICollectionViewFlowLayout, 그리고 난 (에 반환 컨텐츠 크기를 계산하려면 intrinsicContentSize자동 레이아웃을 통해 높이를 조정 필요).

문제는 다음과 같습니다. 모든 셀에 대해 높이가 고정되어 있어도 .NET 파일에 "행"/ 줄이 몇 개 있는지 알 수 없습니다 UICollectionView. 또한 데이터 항목을 나타내는 셀의 너비가 다르기 때문에 데이터 소스의 항목 수로 개수를 결정할 수 없으며 결과적으로 UICollectionView.

공식 문서에서이 주제에 대한 힌트를 찾을 수없고 인터넷 검색이 더 이상 나에게 가져 오지 않았기 때문에 어떤 도움과 아이디어라도 대단히 감사하겠습니다.

답변:


254

우와! 어떤 이유에서인지 몇 시간 동안 조사한 끝에 내 질문에 대한 매우 쉬운 대답을 찾았습니다. 잘못된 곳에서 완전히 검색하고 찾을 수있는 모든 문서를 파헤 치고있었습니다 UICollectionView.

간단하고 쉬운 해결책은 기본 레이아웃 collectionViewContentSize에 있습니다. myCollectionView.collectionViewLayout속성을 호출 하기 만하면 콘텐츠의 높이와 너비를 CGSize. 그것만큼 쉽습니다.


1
우와, 나는 UITableView가 비슷한 것을 가지고 있기를 바랍니다
Chris Chen

1
Ignatus, @jbandi, Chris 외, 이것에 대해 확장 할 수 있습니까? 항목 간 간격이 큰 가로 스크롤 방향으로 테스트했을 때 (항목이 가로로 배치되도록) 컬렉션 뷰는 유효하지 않은 내부 콘텐츠 크기 (-1, -1)를 반환했고 collectionViewContentSize는 효과적인 항목을 반환했습니다. 컬렉션 뷰의 경계-공간으로 둘러싸인 행이 하나뿐임을 고려하지 않았습니다. 요컨대, 그다지 내재적이지 않았습니다. 감사!
Chris Conover 2014 년

8
스크롤 뷰의 collectionViewContentSize와 contentSize의 차이점은 무엇입니까?
rounak

viewDidLoad에서 컬렉션 뷰의 contentSize를 호출하면 컬렉션 뷰의 프레임을 반환하고 collectionViewContentSize는 정확한 콘텐츠 크기를 반환합니다.
Fury

1
누구에게 도움이 될 수 있는지 :이 작업을 수행하기 전에 "layoutIfNeeded ()"를 수행해야했습니다.
asanli

37

자동 레이아웃을 사용하는 경우 다음 하위 클래스를 만들 수 있습니다.UICollectionView

아래 코드를 사용하면 컬렉션 뷰의 내용에 따라 달라 지므로 컬렉션 뷰에 대한 높이 제약 조건을 지정할 필요가 없습니다.

다음은 구현입니다.

@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
나를 위해 작동하지 않았다 ... 나는 UITableViewCell 내부에서 내 CollectionView를 사용하고 있으며 iOS8 UITableViewAutomaticDimension을 사용하여 컬렉션 뷰의 높이가 증가함에 따라 tableview의 높이가 커지기를 원하지만 내 collectionview는 작게 유지됩니다 :(
Georg

대박. UIScrollView 내부의 UICollectionView에 대한 짧고 달콤한 솔루션!
dotToString

2
한 가지 중요한 것은 컬렉션 뷰에서 스크롤과 스크롤바를 비활성화하는 것입니다
Georg

1
Georg와 같은 문제입니다. 400 이상이어야 할 때 높이가 약 50px입니다
xtrinch

3
예, collectionview를 "not scrollabe, no scollbars"로 설정 한 다음 다시로드하면 tableview의 내용이 설정됩니다. 또한 collectionview는 콘텐츠 크기를 intrinsicContentSize로 반환하는 하위 클래스입니다. 도움이 되었기를 바랍니다.
Georg

25

에서 viewDidAppear당신은 그것을으로 얻을 수 있습니다 :

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

데이터를 다시로드 한 다음 새 데이터로 새 높이를 계산해야 할 때 다음 방법으로 가져올 수 있습니다. 다음 위치 CollectionView에서 데이터를 다시로드 할 때 수신 할 관찰자를 추가합니다 viewdidload.

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

그런 다음 벨로우즈 기능을 추가하여 새로운 높이를 얻거나 collectionview가 다시로드 된 후 무엇이든 수행하십시오.

- (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;    
}

관찰자를 제거하는 것을 잊지 마십시오.

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

11

스위프트의 user1046037 답변 ...

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

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

완벽하게 작동하지만 UILabel위와 같은 내용 collectionView이 있으면 위의 다른 요소 를 전달 하는 콘텐츠 가 다른 솔루션이 있습니까?
KolaCaine

11

user1046037 답변에 대한 Swift 3 코드

import UIKit

class DynamicCollectionView: UICollectionView {

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

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}

나는 더 신선하고 내 ViewController에서 이것을 사용하는 방법과 위치에 대해 잘 모르겠습니다. 답변으로 편집하십시오 .....?
Abirami Bala

1
단지 스토리 보드에 collectionview에이 클래스를 설정
모하메드 사 디크 샤이 크

7

스위프트 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
나는 그것의 아이폰 6/7 플러스 XR, XS-MAX에서 작동하지 않는 것을 발견
라훌 K 라잔

7

스위프트 4.2

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

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

4

이 질문에 답하기에는 너무 늦었지만 최근에이 문제를 겪었습니다.
@lee의 위 답변 은 내 답변을 얻는 데 많은 도움이됩니다. 그러나 그 대답은 Objective-c로 제한되어 있으며 저는 swift 에서 작업했습니다 .
@lee 귀하의 답변을 참조하여 신속한 사용자 가이 문제를 쉽게 해결할 수 있도록하겠습니다. 이를 위해 아래 단계를 따르십시오.

선언 CGFloat섹션에서 변수를 선언하십시오.

var height : CGFloat!

에서 viewDidAppear당신은 그것을으로 얻을 수 있습니다 :

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

당신이 때 아마 reload: 데이터를 다음 새 데이터로 새로운 높이를 계산해야 당신은으로 그것을 얻을 수 있습니다 addObserver때 듣고 CollectionView완성 된 데이터를 다시로드를시 viewWillAppear:

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

그런 다음 벨로우즈 기능을 추가하여 새 높이를 얻거나 새로 collectionview고침 을 마친 후 무엇이든하십시오 .

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
    }

관찰자를 제거하는 것을 잊지 마십시오.

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

이 정보가 문제를 신속하게 해결하는 데 도움이되기를 바랍니다.


@ShikhaSharma : "ViewDidDisappear"메소드 내에 removeObserver 코드를 추가 할 수 있습니다.이 코드는 뷰가 사라진 것을 확인한 후에 만 ​​관찰자를 제거합니다. 업데이트 된 답변을 확인하십시오.
어. Vihar

이 오류가 발생합니다. -observeValueForKeyPath : ofObject : change : context : 메시지가 수신되었지만 처리되지 않았습니다.
Shikha Sharma 2017

@ShikhaSharma : 죄송합니다, 방법을 지정하는 것은 제 실수입니다. viewWillDisappear에 removeobserver 코드를 넣으십시오.
어. Vihar

1

이 답변은 @Er을 기반으로합니다. Vihar의 대답. RxSwift를 사용하여 솔루션을 쉽게 구현할 수 있습니다.

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

0

@ user1046037의 Swift 버전 :

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 :

어떤 이유로 collectionView.contentSize.height내 컬렉션 뷰의 해결 된 높이보다 작게 나타납니다. 첫째, 수퍼 뷰 높이의 1/2에 상대적인 자동 레이아웃 제한을 사용했습니다. 이 문제를 해결하기 위해 제약 조건을 뷰의 "안전 영역"에 상대적으로 변경했습니다.

이를 통해 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)
}

전에

superview에 상대적인 높이 제약 잘못된 시뮬레이터 결과

안전 영역에 상대적인 높이 제약 좋은 시뮬레이터 결과


0

가정 당신의 collectionView는 다음과 같이 이름 collectionView 다음과 같이 높이를 취할 수 있습니다.

let height = collectionView.collectionViewLayout.collectionViewContentSize.height

0

또한 reloadData높이를 얻기 전에 전화하는 것이 좋습니다 .

theCollectionView.collectionViewLayout.collectionViewContentSize;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.