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

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

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

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

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

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



답변

스위프트 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게터를 재정의 하고 적절한 특성을 반환 하도록 할 수도 있습니다 . 그것은 당신에게 달려 있습니다.

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



답변

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


답변

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

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

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

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


답변

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

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

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

    yourFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

답변

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

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

let textView = UITextView()
textView.scrollEnabled = false


답변

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 년대 기사의 다른 곳에서는 그것을 보지 못했습니다.