자동 레이아웃이있는 UICollectionView 자체 크기 조정 셀


207

UICollectionViewCells자동 레이아웃을 사용하여 자체 크기 조정을 시도하고 있지만 셀 크기를 내용에 맞게 조정할 수는 없습니다. 셀의 contentView 안에있는 내용에서 셀 크기가 어떻게 업데이트되는지 이해하는 데 어려움이 있습니다.

내가 시도한 설정은 다음과 같습니다.

  • 사용자 UICollectionViewCellA를 UITextView그있는 contentView한다.
  • 에 대한 스크롤 UITextView이 비활성화되었습니다.
  • contentView의 가로 제약 조건은 "H : | [_textView (320)]"입니다. 즉, UITextView명시 적 너비가 320 인 셀의 왼쪽에 고정됩니다.
  • contentView의 수직 제약은 "V : | -0-[_ textView]"입니다. 즉 UITextView, 셀 상단에 고정됩니다.
  • UITextView상수로 설정 높이 제약이 UITextView보고서는 텍스트에 맞게됩니다.

셀 배경이 빨간색으로 UITextView설정되고 배경이 파란색으로 설정된 모습은 다음과 같습니다 . 셀 배경 빨간색, UITextView 배경 파란색

나는 GitHub 에서 놀고있는 프로젝트를 여기 에 넣었다 .


sizeForItemAtIndexPath 메소드를 구현하고 있습니까?
Daniel Galasko

@DanielGalasko 아니에요. 내 이해는 iOS 8의 새로운 자체 크기 셀 기능은 더 이상 그렇게 할 필요가 없다는 것입니다.
rawbee

정보를 어디서 얻었는지 확실하지 않지만 여전히 필요한 정보는 WWDC 발췌문 asciiwwdc.com/2014/sessions/226을 참조하십시오
Daniel Galasko

그러나 다시 한 번 유스 케이스에 따라 추정 된 항목 크기를 구현해야합니다.
Daniel Galasko

답변:


309

스위프트 5 업데이트

preferredLayoutAttributesFittingAttributes이름 변경 preferredLayoutAttributesFitting및 자동 크기 조정 사용


스위프트 4 업데이트

systemLayoutSizeFittingSize 로 개명 systemLayoutSizeFitting


iOS 9 용으로 업데이트

iOS 9에서 GitHub 솔루션이 중단 된 것을보고 마침내 문제를 완전히 조사 할 시간을 얻었습니다. 자체 크기 조정 셀에 대한 서로 다른 구성의 몇 가지 예를 포함하도록 저장소를 업데이트했습니다. 내 결론은 자기 사이징 셀은 이론적으로는 훌륭하지만 실제로는 지저분하다는 것입니다. 자체 크기 조정 셀을 진행할 때주의 할 사항.

TL; DR

GitHub 프로젝트를 확인하십시오


자체 크기 조정 셀은 플로우 레이아웃에서만 지원되므로 사용중인 것이 맞는지 확인하십시오.

자체 크기 조정 셀이 작동하려면 설정해야 할 두 가지가 있습니다.

1. 설정 estimatedItemSizeUICollectionViewFlowLayout

estimatedItemSize속성 을 설정하면 흐름 레이아웃이 실제로 동적이 됩니다.

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

2. 셀 서브 클래스 크기 조정 지원 추가

이것은 2 가지 맛으로 나옵니다. 의 자동 레이아웃 또는 맞춤 재정의 preferredLayoutAttributesFittingAttributes.

자동 레이아웃으로 셀 생성 및 구성

셀의 제약 조건을 구성하는 것에 대한 훌륭한 SO 게시물 이 있기 때문에 이에 대해 자세히 설명하지 않습니다 . 것을 그냥 조심 엑스 코드 (6) 파산 당신이 아이폰 OS 7 지원하는 경우 아이폰 OS 7 그래서 함께 잔뜩, 당신은 autoresizingMask 셀의 경계 때와 같이 설정되어있는 contentView의 경계 셀의있는 contentView에와 있다는 설정되어 있는지 확인 같은 물건을 수행해야합니다 셀이로드됩니다 (예 :) awakeFromNib.

알아야 할 사항은 셀이 테이블 뷰 셀보다 더 심각하게 제한되어야한다는 것입니다. 예를 들어 너비를 동적으로 만들려면 셀에 높이 제한이 필요합니다. 마찬가지로, 높이를 동적으로하려면 셀에 대한 너비 제한이 필요합니다.

preferredLayoutAttributesFittingAttributes커스텀 셀에서 구현

이 기능이 호출되면 뷰가 이미 컨텐츠로 구성되었습니다 (예 : cellForItem호출). 제약 조건이 적절하게 설정되었다고 가정하면 다음과 같이 구현할 수 있습니다.

//forces the system to do one layout pass
var isHeightCalculated: Bool = false

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    //Exhibit A - We need to cache our calculation to prevent a crash.
    if !isHeightCalculated {
        setNeedsLayout()
        layoutIfNeeded()
        let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
        var newFrame = layoutAttributes.frame
        newFrame.size.width = CGFloat(ceilf(Float(size.width)))
        layoutAttributes.frame = newFrame
        isHeightCalculated = true
    }
    return layoutAttributes
}

참고 iOS 9에서는주의하지 않으면 구현에서 충돌이 발생할 수있는 동작이 약간 변경되었습니다 (자세한 내용은 여기 참조 ). 구현할 때 preferredLayoutAttributesFittingAttributes레이아웃 속성의 프레임 만 한 번만 변경해야합니다. 이 작업을 수행하지 않으면 레이아웃이 구현을 무기한 호출하고 결국 충돌합니다. 한 가지 해결책은 셀에서 계산 된 크기를 캐시하고 셀을 재사용하거나 isHeightCalculated속성으로 수행 한 내용을 변경할 때마다 무효화하는 것입니다 .

레이아웃 경험

이 시점에서 collectionView에 '작동하는'동적 셀이 있어야합니다. 아직 테스트 중에 충분한 기본 솔루션을 찾지 못 했으므로 언제든지 의견을 말하십시오. 여전히 UITableView역동적 인 사이징 IMHO와의 싸움에서이기는 것 같습니다 .

경고

프로토 타입 셀을 사용하여 추정 된 항목 크기 를 계산하는 경우 XIB가 크기 클래스를 사용 하면 중단됩니다 . 그 이유는 XIB에서 셀을로드 할 때 크기 클래스가로 구성되기 때문입니다 Undefined. iOS 7에서는 크기 클래스가 장치 (iPad = Regular-Any, iPhone = Compact-Any)를 기반으로로드되기 때문에 iOS 8 이상에서만 손상됩니다. XIB를로드하지 않고 추정 된 항목 크기를 설정하거나 XIB에서 셀을로드하고 collectionView에 추가하고 (traitCollection을 설정 함) 레이아웃을 수행 한 다음 슈퍼 뷰에서 제거 할 수 있습니다. 또는 셀이 traitCollection게터를 재정의 하고 적절한 특성을 반환 하도록 할 수도 있습니다 . 그것은 당신에게 달려 있습니다.

내가 놓친 것이 있으면 알려주세요. 도움이 되길 바랍니다. 행운을 빌어 요.



2
흐름 레이아웃으로 동일한 것을 구현하려고하지만 업데이트 된 크기로 newFrame을 반환하면 레이아웃이 여전히 y 위치를 다시 계산하지 않는 것 같습니다. 여전히 추정 된 크기의 높이를 기반으로합니다. 무엇이 빠졌습니까?
anna

@anna 레이아웃에서 invalidateLayout을 호출하고 있습니까?
Daniel Galasko

1
아아, 그 차이를 발견했다면, 당신은 추정 높이를 꽤 높게 설정했지만 (400) 내가 최소 (44)를하고있었습니다. 도움이되었습니다. 그러나 contentSize는 여전히 스크롤 할 때까지 올바르게 설정되지 않은 것 같습니다. Btw, UITextView를 UILabel로 변경했습니다. 동일한 동작입니다.
anna

17
이것은 iOS 9 GM에서 근본적으로 고장난 것으로 보입니다. 설정 estimatedItemSize하면 충돌이 발생합니다. 자동 레이아웃이 UICollectionViewCell을 처리하는 방법에 큰 버그가있는 것 같습니다.
Wes Campaigne

3
비슷한 문제가 발생하여 해결 방법을 찾은 사람이 있습니까?
Tony

47

iOS10에는 UICollectionViewFlowLayout.automaticSize(이전 UICollectionViewFlowLayoutAutomaticSize) 이라는 새로운 상수가 있으므로 대신 :

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

이것을 사용할 수 있습니다 :

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

컬렉션 뷰의 셀에 일정한 wid가있는 경우 특히 성능이 향상됩니다.

흐름 레이아웃 액세스 :

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
   }
}

스위프트 5 업데이트 :

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    }
}

8
이것을 어디에 설정합니까? viewdidload? flowlayout을 어떻게 처리합니까?
UKDataGeek

1
나는 이것을 구현할 수 있었지만 단일 셀이있는 경우 셀을 중심으로 만드는 이상한 동작이 있습니다. 여기에 대한 스택 오버 플로우 문제는 - 내가 난처한 상황에 빠진 : stackoverflow.com/questions/43266924/...
UKDataGeek

1
collectionViews를 통해 플로우 레이아웃에 액세스 할 수 있으며 플로우 레이아웃 collectionViewLayout이 유형인지 확인하십시오.UICollectionViewFlowLayout
d4Rk

4
그것은 나의 세포를 매우 작고 쓸모 없게 만든다
user924

44

Daniel Galasko의 답변에 대한 몇 가지 주요 변경 사항 모든 문제가 해결되었습니다. 불행히도, 나는 직접적으로 (아직도) 언급할만한 명성을 얻지 못했습니다.

1 단계에서 자동 레이아웃을 사용할 때 셀에 단일 상위 UIView를 추가하기 만하면됩니다. 셀 내부의 모든 것은 부모의 하위 뷰 여야합니다. 그것은 내 모든 문제에 답했다. Xcode는 UITableViewCells에 이것을 자동으로 추가하지만 UICollectionViewCells에는 그렇지 않습니다. 문서 에 따르면 :

셀 모양을 구성하려면 dataView 속성의보기에 데이터 항목의 내용을 하위보기로 표시하는 데 필요한보기를 추가하십시오. 셀 자체에 하위 뷰를 직접 추가하지 마십시오.

그런 다음 3 단계를 완전히 건너 뜁니다. 필요하지 않습니다.


1
흥미있는; 이것은 Xibs를위한 것이지만 그럼에도 불구하고 감사합니다 .Github 프로젝트를 시도하고 재현 할 수 있는지 확인하십시오. 어쩌면 프로그램 대 XIB에 답을 분할
다니엘 Galasko

매우 도움이됩니다. 감사!
ProblemSlover

2
iOS 9, Xcode 7. 셀이 스토리 보드에서 프로토 타입 화되고 사용자 정의 서브 클래스가 설정됩니다. 라는 속성을 만들려고 시도 contentView했지만 Xcode는 기존 속성과 충돌한다고 불평합니다. 하위보기를 추가 self.contentView하고 제약 조건을 설정 하려고하면 응용 프로그램이 중단됩니다.
Nicolas Miari

프로그래밍 방식 으로이 작업을 수행하는 방법을 모르겠습니다. 내 솔루션은 실제로 XIB에 모든 것이 들어가는 단일 uiview를 추가하는 것입니다. 속성이나 아무것도 만들 필요가 없습니다.
Matt Koala

@NicolasMiari와 같은 문제에 직면하고 있습니다. 나는 / 엑스 코드 7 아이폰 OS 9에서 자동 레이아웃 제약과 스토리 보드에서 프로토 타입 세포의 estimatedContentSize을 설정하면 응용 프로그램이 잘못된 액세스 예외없이 도움이 스택 트레이스와 충돌
와사비

34

iOS 10 이상에서는 매우 간단한 2 단계 프로세스입니다.

  1. 모든 셀 내용이 단일 UIView (또는 UIStackView와 같은 UIView의 하위 항목) 내에 배치되어 자동 레이아웃을 많이 단순화해야합니다. UITableViewCells의 크기를 동적으로 조정하는 것처럼 전체 뷰 계층에는 가장 바깥 쪽 컨테이너에서 가장 안쪽 뷰까지 제약 조건이 구성되어 있어야합니다. UICollectionViewCell과 즉각적인 childview 사이의 제약 조건이 포함됩니다.

  2. UICollectionView의 flowlayout에 자동 크기 조정을 지시하십시오.

    yourFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

궁금합니다. UIView에서 셀 내용을 래핑하면 자동 레이아웃의 성능이 어떻게 향상됩니까? 더 얕은 계층 구조로 더 잘 수행 할 것이라고 생각 했습니까?
James Heald

1
나는 성능에 대해서는 언급하지 않고 단순성 만 언급했다. 자체 크기 조정 셀은 왼쪽과 오른쪽 사이, 위쪽과 아래쪽 사이에 제약 조건이있는 경우에만 작동합니다. 모든 뷰가 단일 컨테이너에 싸여있을 때 가장 쉽습니다. 해당 컨테이너가 UIStackView 인 경우 다른 UIView의 하위 항목 인 경우 더 쉽습니다.
Marmoy

UICollectionViewFlowLayoutAutomaticSize에 대해 높이를 일정하게 설정하는 방법 (설정 높이 25와 같이 너비가 자동으로 변경됨)을 알려주십시오.
Svetoslav Atanasov

Swift 4.1을 사용하면 Xcode는 UICollectionViewFlowLayout.automaticSize이름이로 바뀌 었습니다 UICollectionViewFlowLayoutAutomaticSize.
LinusGeffarth

14
  1. viewDidLoad ()에 flowLayout 추가

    override func viewDidLoad() {
        super.viewDidLoad() 
        if let flowLayout = infoCollection.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(width: 1, height:1)
        }
    }
  2. 또한 UIView를 셀의 mainContainer로 설정하고 셀에 필요한 모든보기를 추가하십시오.

  3. 자세한 내용은이 멋진 튜토리얼을 참조하십시오 .iOS 9 및 10에서 자동 레이아웃을 사용하여 자동 크기 조정 셀이있는 UICollectionView


11

11/19/19 편집 : iOS 13의 경우 UICollectionViewCompositionalLayout을 예상 높이와 함께 사용하십시오. 이 깨진 API를 다루는 데 시간을 낭비하지 마십시오.

일정 시간 동안이 문제를 해결 한 후 스크롤을 비활성화하지 않으면 UITextViews에서 크기 조정이 작동하지 않는 것으로 나타났습니다.

let textView = UITextView()
textView.scrollEnabled = false

스크롤하려고하면 컬렉션 뷰 셀이 부드럽 지 않습니다. 이것에 대한 해결책이 있습니까?
Sathish Kumar Gurunathan

6

contentView 앵커 미스터리 :

하나의 기괴한 경우

    contentView.translatesAutoresizingMaskIntoConstraints = false

작동하지 않습니다. contentView에 네 개의 명시 적 앵커를 추가하고 작동했습니다.

class AnnoyingCell: UICollectionViewCell {
    
    @IBOutlet var word: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame); common() }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder); common() }
    
    private func common() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

평소와 같이

    estimatedItemSize = UICollectionViewFlowLayout.automaticSize

YourLayout: UICollectionViewFlowLayout

누가 알아? 누군가를 도울 수 있습니다.

신용

https://www.vadimbulavin.com/collection-view-cells-self-sizing/

그 팁을 우연히 발견했습니다-이것에 관한 1000 년대 기사의 다른 곳에서는 그것을 보지 못했습니다.


3

컬렉션 뷰의 동적 셀 높이를 수행했습니다. 여기 자식 허브 저장소가 있습니다 있습니다.

그리고 preferredLayoutAttributesFittingAttributes가 두 번 이상 호출 된 이유를 찾아보십시오. 실제로는 3 회 이상 호출됩니다.

콘솔 로그 사진 : 여기에 이미지 설명을 입력하십시오

첫 번째 preferredLayoutAttributesFittingAttributes :

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c290e0> index path: (<NSIndexPath:    0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 57.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

layoutAttributes.frame.size.height는 현재 상태 57.5입니다. 입니다.

두 번째 preferredLayoutAttributesFittingAttributes :

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c16370> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

예상대로 셀 프레임 높이가 534.5 로 변경되었습니다 . 그러나 컬렉션 뷰의 높이는 여전히 0입니다.

3 번째 preferredLayoutAttributesFittingAttributes :

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa403d516a0> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 477);

컬렉션 뷰 높이가 0에서 477로 변경된 것을 볼 수 있습니다 .

동작은 스크롤 처리와 비슷합니다.

1. Before self-sizing cell

2. Validated self-sizing cell again after other cells recalculated.

3. Did changed self-sizing cell

처음에는이 방법이 한 번만 호출된다고 생각했습니다. 그래서 나는 다음과 같이 코딩했다 :

CGRect frame = layoutAttributes.frame;
frame.size.height = frame.size.height + self.collectionView.contentSize.height;
UICollectionViewLayoutAttributes* newAttributes = [layoutAttributes copy];
newAttributes.frame = frame;
return newAttributes;

이 줄 :

frame.size.height = frame.size.height + self.collectionView.contentSize.height;

시스템 호출 무한 루프 및 앱 충돌이 발생합니다.

크기가 변경되면 모든 셀의 위치 (예 : 프레임)가 더 이상 변경되지 않을 때까지 모든 셀의 preferredLayoutAttributesFittingAttributes를 반복해서 확인합니다.


2

위의 답변 외에도

그냥 확인하면 설정 estimatedItemSize의 재산 UICollectionViewFlowLayout을 어떤 크기로와 하지 않는 구현 sizeForItem을 : atIndexPath 위임 방법을.

그게 다야.


1

UICollectionViewDelegateFlowLayout 메소드를 구현하는 경우 :

- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath

를 호출 collectionview performBatchUpdates:completion:하면 크기 높이 sizeForItemAtIndexPath대신에 preferredLayoutAttributesFittingAttributes .

의 렌더링 프로세스 performBatchUpdates:completion는 메소드를 거치지 preferredLayoutAttributesFittingAttributes만 변경 사항은 무시합니다.


이 오작동을 보았지만 예상 크기가 0 일 때만 가능합니다. 예상 크기를 설정 하시겠습니까?
dgatwood 2012

1

누구에게나 도움이 될 수 있다면

내가 estimatedItemSize설정 되면 그 심한 충돌이 발생했습니다 . 에서 0을 반환하더라도 numberOfItemsInSection. 따라서 셀 자체와 자동 레이아웃은 충돌의 원인이 아니 었습니다. collectionView는 비어있을 때에도 충돌했기 때문에estimatedItemSize 었습니다. 자체 크기 조정이 설정되어 .

필자의 경우 collectionView를 포함하는 컨트롤러에서 collectionViewController로 프로젝트를 재구성했으며 효과가있었습니다.

그림을 이동.


1
호출 시도 collectionView.collectionViewLayout.invalidateLayout()collectionView.reloadData().
chengsam

ios10 및 아래) (이 코드`재정의 FUNC의 viewWillLayoutSubviews를 추가 {#available 경우) (super.viewWillLayoutSubviews (아이폰 OS 11.0, *) {} 다른 {mainCollectionView.collectionViewLayout.invalidateLayout ()}}`
Marosdee 우마

1

운없이 모든 것을 시도한 사람에게는 이것이 나를 위해 일한 유일한 것입니다. 셀 내부의 여러 줄 레이블의 경우 다음 마법 줄을 추가하십시오.

label.preferredMaxLayoutWidth = 200

더 많은 정보: here

건배!


0

위의 예제 방법은 컴파일되지 않습니다. 다음은 수정 된 버전입니다 (하지만 작동 여부에 대해서는 테스트되지 않았습니다).

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
{
    let attr: UICollectionViewLayoutAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes

    var newFrame = attr.frame
    self.frame = newFrame

    self.setNeedsLayout()
    self.layoutIfNeeded()

    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    newFrame.size.height = desiredHeight
    attr.frame = newFrame
    return attr
}

0

추가 정보 업데이트 :

  • 을 사용 flowLayout.estimatedItemSize하는 경우 iOS8.3 이후 버전 사용 을 제안하십시오. iOS8.3 이전에는 다운됩니다 [super layoutAttributesForElementsInRect:rect];. 오류 메시지는

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

  • 둘째, iOS8.x 버전에서는 flowLayout.estimatedItemSize다른 섹션 삽입 설정이 작동하지 않습니다. 즉 기능 : (UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:.


0

이 솔루션은 4 가지 중요한 단계로 구성됩니다.

  1. 동적 셀 크기 조정 사용

flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

  1. 자동 레이아웃을 활성화하지 않은 경우 자동 크기 조정을 방지 contentView하므로 자동 레이아웃을 명시 적으로 활성화 하고 셀 가장자리에 고정 합니다 contentView.
class MultiLineCell: UICollectionViewCell{

    private var textViewHeightContraint: NSLayoutConstraint!

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .cyan
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentViewWidthAnchor = contentView.widthAnchor.constraint(equalToConstant: 0)

        NSLayoutConstraint.activate([
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
    } 
    ...
}
  1. collectionView(:cellForItemAt:)contentView의 너비를 collectionView의 너비로 제한 하려면 contentView.widthAnchor.constraint를 로 설정하십시오 .

다음과 같이 발생합니다. 셀의 maxWidth변수를 CollectionView의 너비로 설정 collectionView(:cellForItemAt:)하고 maxWidthdidSet메소드 에서 widthAnchor.constant를 maxWidth로 설정합니다.

class ViewController: UIViewController, UICollectionViewDataSource {
    ...

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! MultiLineCell
        cell.textView.text = dummyTextMessages[indexPath.row]
        cell.maxWidth = collectionView.frame.width
        return cell
    }

    ...
}
class MultiLineCell: UICollectionViewCell{
    ....

    var maxWidth: CGFloat? {
        didSet {
            guard let maxWidth = maxWidth else {
                return
            }
            contentViewWidthAnchor.constant = maxWidth
            contentViewWidthAnchor.isActive = true
        }
    }

    ....
}

UITextView의 자체 크기 조정을 사용하려면 추가 단계가 있습니다.

4. UITextView의 heightAnchor.constant를 계산하고 설정하십시오.

contentView의 너비가 설정 될 때마다 UITextView의 높이 didSetmaxWidth .

UICollectionViewCell 내부 :

var maxWidth: CGFloat? {
    didSet {
        guard let maxWidth = maxWidth else {
            return
        }
        contentViewWidthAnchor.constant = maxWidth
        contentViewWidthAnchor.isActive = true

        let sizeToFitIn = CGSize(width: maxWidth, height: CGFloat(MAXFLOAT))
        let newSize = self.textView.sizeThatFits(sizeToFitIn)
        self.textViewHeightContraint.constant = newSize.height
    }
}

이 단계는 원하는 결과를 얻습니다. 여기에 완전한 런너 블이 있습니다 요점입니다.

명확하고 밝은 블로그 게시물- 컬렉션 뷰 셀 자체 크기 조정 : 단계별 자습서에 대한 Vadim Bulavin 에게 감사합니다


-2

나는 사용하려고 시도 estimatedItemSize했지만 estimatedItemSize셀의 높이와 정확히 일치하지 않으면 셀을 삽입하고 삭제할 때 많은 버그가있었습니다 . 나는 설정을 멈췄다estimatedItemSize프로토 타입 셀을 사용하여 하고 동적 셀을 구현했습니다. 그 방법은 다음과 같습니다.

이 프로토콜을 작성하십시오.

protocol SizeableCollectionViewCell {
    func fittedSize(forConstrainedSize size: CGSize)->CGSize
}

이 프로토콜을 사용자 정의에 구현하십시오 UICollectionViewCell.

class YourCustomCollectionViewCell: UICollectionViewCell, SizeableCollectionViewCell {

    @IBOutlet private var mTitle: UILabel!
    @IBOutlet private var mDescription: UILabel!
    @IBOutlet private var mContentView: UIView!
    @IBOutlet private var mTitleTopConstraint: NSLayoutConstraint!
    @IBOutlet private var mDesciptionBottomConstraint: NSLayoutConstraint!

    func fittedSize(forConstrainedSize size: CGSize)->CGSize {

        let fittedSize: CGSize!

        //if height is greatest value, then it's dynamic, so it must be calculated
        if size.height == CGFLoat.greatestFiniteMagnitude {

            var height: CGFloat = 0

            /*now here's where you want to add all the heights up of your views.
              apple provides a method called sizeThatFits(size:), but it's not 
              implemented by default; except for some concrete subclasses such 
              as UILabel, UIButton, etc. search to see if the classes you use implement 
              it. here's how it would be used:
            */
            height += mTitle.sizeThatFits(size).height
            height += mDescription.sizeThatFits(size).height
            height += mCustomView.sizeThatFits(size).height    //you'll have to implement this in your custom view

            //anything that takes up height in the cell has to be included, including top/bottom margin constraints
            height += mTitleTopConstraint.constant
            height += mDescriptionBottomConstraint.constant

            fittedSize = CGSize(width: size.width, height: height)
        }
        //else width is greatest value, if not, you did something wrong
        else {
            //do the same thing that's done for height but with width, remember to include leading/trailing margins in calculations
        }

        return fittedSize
    }
}

이제 컨트롤러가에 따르도록 UICollectionViewDelegateFlowLayout하고, 다음 필드를 갖습니다.

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout {
    private var mCustomCellPrototype = UINib(nibName: <name of the nib file for your custom collectionviewcell>, bundle: nil).instantiate(withOwner: nil, options: nil).first as! SizeableCollectionViewCell
}

데이터를 바인딩 한 다음 해당 데이터가 동적 측정하려는 차원에 어떤 영향을 미치는지 결정하기위한 프로토 타입 셀로 사용됩니다.

마지막 UICollectionViewDelegateFlowLayout's collectionView(:layout:sizeForItemAt:)으로 구현해야합니다.

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    private var mDataSource: [CustomModel]

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)->CGSize {

        //bind the prototype cell with the data that corresponds to this index path
        mCustomCellPrototype.bind(model: mDataSource[indexPath.row])    //this is the same method you would use to reconfigure the cells that you dequeue in collectionView(:cellForItemAt:). i'm calling it bind

        //define the dimension you want constrained
        let width = UIScreen.main.bounds.size.width - 20    //the width you want your cells to be
        let height = CGFloat.greatestFiniteMagnitude    //height has the greatest finite magnitude, so in this code, that means it will be dynamic
        let constrainedSize = CGSize(width: width, height: height)

        //determine the size the cell will be given this data and return it
        return mCustomCellPrototype.fittedSize(forConstrainedSize: constrainedSize)
    }
}

그리고 그게 다야. collectionView(:layout:sizeForItemAt:)이런 식으로 셀 크기를 반환하면 사용하지 않아도되며 estimatedItemSize셀 삽입 및 삭제가 완벽하게 작동합니다.

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