program story

UICollectionView, 전체 너비 셀, 자동 레이아웃 동적 높이 허용?

inputbox 2020. 10. 10. 10:01
반응형

UICollectionView, 전체 너비 셀, 자동 레이아웃 동적 높이 허용?


다음은 현재 iOS 개발에 대한 매우 기본적인 질문입니다. 이에 대한 좋은 답을 찾지 못했습니다.


(말) 수직에서 UICollectionView,

전체 너비 셀 을 가질 수 있지만 자동 레이아웃으로 동적 높이를 제어 할 수 있습니까?

(iOS "동적 높이"를 처음 사용하는 경우, 셀에 높이 또는 높이가 다를 수있는 이미지가 될 수있는 텍스트 뷰가 몇 개 있으므로 궁극적으로 각 셀의 높이가 완전히 다릅니다.)

그렇다면 어떻게?

이것은 아마도 "정말 좋은 대답이없는 iOS에서 가장 중요한 질문"이라고 생각합니다.


이것은 확실히

모든 iOS에서 가장 어려운 문제 :(

2019 년 중반 ... 또 다른 현상금을 추가했습니다.이 초 기본 문제에 대한 좋은 해결책은 아직 없습니다. WTH, 애플? "컬렉션보기 항목을 전체 너비로 만드십시오."


중요-이것은 학문적 질문입니다-테이블보기를 사용하십시오!

약 50,000 명이이 페이지를 공부했습니다! xxtesaxx 및 다른 사람들이 지적했듯이,

... 테이블보기를 사용하여 아무런 노력없이 즉시이 작업을 수행 할 수 있습니다.

요즘 (2019) 당신은 할 수 없습니다 아무것도 - 전혀 아무것도 - 특별한 테이블보기로는 세포 내부의 동적 높이 항목 (임의의 수)와 완벽하게 작동하도록.

(필수 사항 인 제약 조건에서 셀을 위아래로 올바르게 "체인"하는 방법을 알고 있다고 가정합니다.)

따라서이 페이지에서 요구하는 것을 문자 그대로해야하는 유일한 이유 는 일종의 고문 학문적 운동 이라는 점을 지적하는 것이 공정합니다 .

일반적인 테이블 뷰를 사용하여 즉시 (완전히 내장되어 있으며, 추가 코드 한 줄이 아니라)이를 수행 할 수 있습니다.


#1. iOS 13 용 솔루션

Swift 5.1 및 iOS 13에서는 문제 해결을 위해 Compositional Layout 개체사용할 수 있습니다 .

다음 전체 샘플 코드는 UILabel전체 너비 안에 여러 줄 을 표시하는 방법을 보여줍니다 UICollectionViewCell.

CollectionViewController.swift

import UIKit

class CollectionViewController: UICollectionViewController {

    let array = [
        "Lorem ipsum.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.",
        "Lorem ipsum dolor sit amet.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        let size = NSCollectionLayoutSize(
            widthDimension: NSCollectionLayoutDimension.fractionalWidth(1),
            heightDimension: NSCollectionLayoutDimension.estimated(44)
        )

        let item = NSCollectionLayoutItem(layoutSize: size)

        let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])

        let section = NSCollectionLayoutSection(group: group)
        section.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
        section.interGroupSpacing = 5

        let layout = UICollectionViewCompositionalLayout(section: section)
        collectionView.collectionViewLayout = layout

        collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: CollectionViewCell.cellIdentifier)
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return array.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.cellIdentifier, for: indexPath) as! CollectionViewCell
        cell.label.text = array[indexPath.row]
        return cell
    }

}

CollectionViewCell.swift

import UIKit

class CollectionViewCell: UICollectionViewCell {

    static let cellIdentifier = "CellIdentifier"
    let label = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)

        label.numberOfLines = 0
        backgroundColor = .orange
        contentView.addSubview(label)

        label.translatesAutoresizingMaskIntoConstraints = false
        label.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

# 2. iOS 12 용 솔루션

Swift 5 및 iOS 12를 사용하면 하위 클래스를 UICollectionViewFlowLayout만들고 estimatedItemSize속성을로 설정할 수 있습니다 UICollectionViewFlowLayoutAutomaticSize(이는 자동 크기 조정을 처리 할 시스템을 알려줍니다 UICollectionViewCell). 그런 다음 재정의 layoutAttributesForElements(in:)하고 layoutAttributesForItem(at:)셀 너비를 설정해야합니다. 마지막으로 셀 preferredLayoutAttributesFitting(_:)방법 을 재정의 하고 압축 된 피팅 높이를 계산해야합니다.

다음 전체 코드는 UILabel전체 너비 내부에 여러 줄 을 표시하는 방법을 보여줍니다 UIcollectionViewCell( UICollectionView의 안전 영역 및의 삽입에 의해 제한됨 UICollectionViewFlowLayout).

CollectionViewController.swift

import UIKit

class CollectionViewController: UICollectionViewController {

    let items = [
        "Lorem ipsum.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.",
        "Lorem ipsum dolor sit amet.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam."
    ]
    let columnLayout = FlowLayout()

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.alwaysBounceVertical = true
        collectionView.collectionViewLayout = columnLayout
        collectionView.contentInsetAdjustmentBehavior = .always
        collectionView.register(Cell.self, forCellWithReuseIdentifier: "Cell")
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
        cell.label.text = items[indexPath.row]
        return cell
    }

}

FlowLayout.swift

import UIKit

class FlowLayout: UICollectionViewFlowLayout {

    override init() {
        super.init()

        self.minimumInteritemSpacing = 10
        self.minimumLineSpacing = 10
        self.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
        estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath) else { return nil }
        guard let collectionView = collectionView else { return nil }
        layoutAttributes.bounds.size.width = collectionView.safeAreaLayoutGuide.layoutFrame.width - sectionInset.left - sectionInset.right
        return layoutAttributes
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let superLayoutAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
        guard scrollDirection == .vertical else { return superLayoutAttributes }

        let computedAttributes = superLayoutAttributes.compactMap { layoutAttribute in
            return layoutAttribute.representedElementCategory == .cell ? layoutAttributesForItem(at: layoutAttribute.indexPath) : layoutAttribute
        }
        return computedAttributes
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }

}

Cell.swift

import UIKit

class Cell: UICollectionViewCell {

    let label = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)

        label.numberOfLines = 0
        backgroundColor = .orange
        contentView.addSubview(label)

        label.translatesAutoresizingMaskIntoConstraints = false
        label.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
        label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        layoutIfNeeded()
        let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
        layoutAttributes.bounds.size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
        return layoutAttributes
    }

}

다음은에 대한 몇 가지 대체 구현입니다 preferredLayoutAttributesFitting(_:).

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    layoutIfNeeded()
    label.preferredMaxLayoutWidth = label.bounds.size.width
    layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
    return layoutAttributes
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    label.preferredMaxLayoutWidth = layoutAttributes.size.width - contentView.layoutMargins.left - contentView.layoutMargins.left
    layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
    return layoutAttributes
}

예상 디스플레이 :

여기에 이미지 설명 입력


문제

자동 높이를 찾고 있고 너비가 가득 차기를 원합니다 UICollectionViewFlowLayoutAutomaticSize. 둘 다 사용할 수는 없습니다 .

UICollectionView아래를 사용하여 원하는 솔루션이 있습니다.

해결책

Step-I : 예상되는 Cell 높이 계산

1.를 설정하고 의 예상 높이를 계산 한 것 이상 UILabel있는 CollectionViewCell경우 세 개의 매개 변수를 모두 전달합니다.numberOfLines=0UIlable

func heightForLable(text:String, font:UIFont, width:CGFloat) -> CGFloat {
    // pass string, font, LableWidth  
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
     label.numberOfLines = 0
     label.lineBreakMode = NSLineBreakMode.byWordWrapping
     label.font = font
     label.text = text
     label.sizeToFit()

     return label.frame.height
}

2. 귀하의 경우 CollectionViewCell에만 포함 UIImageView하고 고도의 동적 있어야하는데의 경우보다 당신의 높이를 얻을 필요가 UIImage (당신 UIImageView이 있어야합니다 AspectRatio제약)

// this will give you the height of your Image
let heightInPoints = image.size.height
let heightInPixels = heightInPoints * image.scale

3. 계산 된 높이보다 둘 다 포함되어 있으면 함께 더합니다.

STEP-II : 크기 반환CollectionViewCell

1.UICollectionViewDelegateFlowLayout viewController에 델리게이트 추가

2. 델리게이트 메서드 구현

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

    // This is just for example, for the scenario Step-I -> 1 
    let yourWidthOfLable=self.view.size.width
    let font = UIFont(name: "Helvetica", size: 20.0)

    var expectedHeight = heightForLable(array[indePath.row], font: font, width:yourWidthOfLable)


    return CGSize(width: view.frame.width, height: expectedHeight)
}

도움이 되었기를 바랍니다.


이 문제를 해결할 수있는 몇 가지 방법이 있습니다.

한 가지 방법은 컬렉션보기 흐름 레이아웃에 예상 크기를 제공하고 셀 크기를 계산하는 것입니다.

참고 : 아래 주석에서 언급했듯이 iOS 10부터는 셀 호출을 트리거하기 위해 더 이상 예상 크기를 제공 할 필요가 없습니다 func preferredLayoutAttributesFitting(_ layoutAttributes:). 이전 (iOS 9)에서는 prefferedLayoutAttributes 셀을 쿼리하려는 경우 예상 크기를 제공해야했습니다.

(스토리 보드를 사용 중이고 컬렉션 뷰가 IB를 통해 연결되어 있다고 가정)

override func viewDidLoad() {
    super.viewDidLoad()
    let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
    layout?.estimatedItemSize = CGSize(width: 375, height: 200) // your average cell size
}

일반적으로 충분할 단순한 세포의 경우. 여전히 크기가 잘못된 경우 컬렉션보기 셀에서을 재정의 할 수 있습니다 func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes. 이렇게하면 셀 크기를보다 세밀하게 제어 할 수 있습니다 . 참고 : 여전히 흐름 레이아웃에 예상 크기를 제공해야합니다 .

그런 다음 재정 func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes의하여 올바른 크기를 반환합니다.

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
    let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
    let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityDefaultLow)
    let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize)
    autoLayoutAttributes.frame = autoLayoutFrame
    return autoLayoutAttributes
}

또는 대신 크기 조정 셀을 사용하여 UICollectionViewDelegateFlowLayout.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = collectionView.frame.width
    let size = CGSize(width: width, height: 0)
    // assuming your collection view cell is a nib
    // you may also instantiate a instance of our cell from a storyboard
    let sizingCell = UINib(nibName: "yourNibName", bundle: nil).instantiate(withOwner: nil, options: nil).first as! YourCollectionViewCell
    sizingCell.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    sizingCell.frame.size = size
    sizingCell.configure(with: object[indexPath.row]) // what ever method configures your cell
    return sizingCell.contentView.systemLayoutSizeFitting(size, withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityDefaultLow)
}

이것이 완벽한 프로덕션 준비 예제는 아니지만 올바른 방향으로 시작해야합니다. 이것이 최선의 방법이라고 말할 수는 없지만 여러 레이블을 포함하는 상당히 복잡한 셀에서도 여러 줄로 줄 바꿈되거나 줄 바꿈되지 않을 수도 있습니다.


이 문제에 대한 매우 쉬운 해결책을 찾았습니다. CollectionViewCell 내부에 실제로 배경 인 UIView ()가 있습니다. 전체 너비를 얻으려면 다음 앵커를 설정하십시오.

bgView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width - 30).isActive = true // 30 is my added up left and right Inset
bgView.topAnchor.constraint(equalTo: topAnchor).isActive = true
bgView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
bgView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
bgView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true

"마법"은 첫 번째 줄에서 발생합니다. widthAnchor를 화면 너비에 동적으로 설정했습니다. 또한 CollectionView의 삽입을 빼는 것도 중요합니다. 그렇지 않으면 셀이 표시되지 않습니다. 이러한 배경보기를 원하지 않으면 보이지 않게 만드십시오.

FlowLayout은 다음 설정을 사용합니다.

layout.itemSize = UICollectionViewFlowLayoutAutomaticSize
layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize

결과는 동적 높이가있는 전체 너비 크기의 셀입니다.

여기에 이미지 설명 입력


CollectionViewCell에 너비 제약을 추가해야합니다.

class SelfSizingCell: UICollectionViewCell {

  override func awakeFromNib() {
      super.awakeFromNib()
      contentView.translatesAutoresizingMaskIntoConstraints = false
      contentView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width).isActive = true
  }
}

개인적으로 AutoLayout이 크기를 결정하는 UICollectionView를 갖는 가장 좋은 방법은 각 Cell이 다른 크기를 가질 수 있지만 실제 Cell을 사용하여 크기를 측정하는 동안 UICollectionViewDelegateFlowLayout sizeForItemAtIndexPath 함수를 구현하는 것입니다.

내 블로그 게시물 중 하나에서 이에 대해 이야기했습니다.

바라건대 이것은 당신이 원하는 것을 달성하는 데 도움이 될 것입니다. 100 % 확실하지는 않지만 AutoLayout을 사용하여 실제로 완전 자동 셀 높이를 가질 수있는 UITableView와는 달리

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44

UICollectionView는 UICollectionViewCell이 화면의 전체 너비를 반드시 채우는 것은 아니기 때문에 AutoLayout이 크기를 결정하도록하는 방법이 없습니다.

그러나 여기에 질문이 있습니다 : 전체 화면 너비 셀이 필요한 경우 자동 크기 조정 셀과 함께 제공되는 좋은 오래된 UITableView 대신 UICollectionView를 사용하는 이유는 무엇입니까?


Eric의 답변에 대한 내 의견에 따르면 내 솔루션은 그의 솔루션과 매우 유사하지만 고정 차원으로 제한하기 위해 preferredSizeFor ...에 제약 조건을 추가해야했습니다.

    override func systemLayoutSizeFitting(
        _ targetSize: CGSize, withHorizontalFittingPriority
        horizontalFittingPriority: UILayoutPriority,
        verticalFittingPriority: UILayoutPriority) -> CGSize {

        width.constant = targetSize.width

        let size = contentView.systemLayoutSizeFitting(
            CGSize(width: targetSize.width, height: 1),
            withHorizontalFittingPriority: .required,
            verticalFittingPriority: verticalFittingPriority)

        print("\(#function) \(#line) \(targetSize) -> \(size)")
        return size
    }

이 질문에는 많은 중복이 있으며 여기에서 자세히 답변하고 여기 에서 작동하는 샘플 앱을 제공했습니다 .


  1. estimatedItemSize흐름 레이아웃 세트 :

    collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
    
  2. 셀에 너비 제한을 정의하고 superview의 너비와 같도록 설정합니다.

    class CollectionViewCell: UICollectionViewCell {
        private var widthConstraint: NSLayoutConstraint?
    
        ...
    
        override init(frame: CGRect) {
            ...
            // Create width constraint to set it later.
            widthConstraint = contentView.widthAnchor.constraint(equalToConstant: 0)
        }
    
        override func updateConstraints() {
            // Set width constraint to superview's width.
            widthConstraint?.constant = superview?.bounds.width ?? 0
            widthConstraint?.isActive = true
            super.updateConstraints()
        }
    
        ...
    }
    

전체 예

iOS 11에서 테스트되었습니다.


이것이 "정말 좋은 대답"인지 확실하지 않지만, 이것을 달성하기 위해 사용하고 있습니다. 내 흐름 레이아웃은 수평이고 자동 레이아웃으로 너비를 조정하려고하므로 상황과 비슷합니다.

extension PhotoAlbumVC: UICollectionViewDelegateFlowLayout {
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // My height is static, but it could use the screen size if you wanted
    return CGSize(width: collectionView.frame.width - sectionInsets.left - sectionInsets.right, height: 60) 
  }
}

그런 다음 자동 레이아웃 제약 조건이 수정되는 뷰 컨트롤러에서 NSNotification을 실행합니다.

NotificationCenter.default.post(name: NSNotification.Name("constraintMoved"), object: self, userInfo: nil)

내 UICollectionView 하위 클래스에서 해당 알림을 수신합니다.

// viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(handleConstraintNotification(notification:)), name: NSNotification.Name("constraintMoved"), object: nil)

레이아웃을 무효화합니다.

func handleConstraintNotification(notification: Notification) {
    self.collectionView?.collectionViewLayout.invalidateLayout()
}

이로 인해 sizeForItemAt컬렉션 뷰의 새 크기를 사용하여 다시 호출됩니다. 귀하의 경우 레이아웃에서 사용 가능한 새로운 제약 조건을 고려하여 업데이트 할 수 있어야합니다.


iOS 10부터는이를위한 흐름 레이아웃에 대한 새로운 API가 있습니다.

당신이해야 할 일은 flowLayout.estimatedItemSize새로운 상수 인 UICollectionViewFlowLayoutAutomaticSize.

출처


viewDidLayoutSubviews의 설정 estimatedItemSize전체 폭 (레이아웃이 UICollectionViewFlowLayout 객체를 참조)에 :

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    return CGSize(width: collectionView.bounds.size.width, height: 120)
}

셀에서 제약 조건이 셀의 맨 위와 맨 아래 모두에 닿아 야합니다 (다음 코드는지도 ​​제작을 사용하여 제약 조건 설정을 단순화하지만 원하는 경우 NSLayoutConstraint 또는 IB를 사용하여 수행 할 수 있음).

constrain(self, nameLabel, valueLabel) { view, name, value in
        name.top == view.top + 10
        name.left == view.left
        name.bottom == view.bottom - 10
        value.right == view.right
        value.centerY == view.centerY
    }

짜잔, 당신의 세포는 이제 높이가 자동으로 증가합니다!


일!!! IOS : 12.1 Swift 4.1에서 테스트 됨

제약 조건 위반없이 작동하는 매우 간단한 솔루션이 있습니다.

여기에 이미지 설명 입력

내 ViewControllerClass

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    let cellId = "CustomCell"

    var source = ["nomu", "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. ", "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,","nomu", "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. ", "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,","nomu", "when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. ", "t is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by", "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia,"]

    override func viewDidLoad() {
        super.viewDidLoad()

        self.collectionView.delegate = self
        self.collectionView.dataSource = self
        self.collectionView.register(UINib.init(nibName: cellId, bundle: nil), forCellWithReuseIdentifier: cellId)

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

    }

}


extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.source.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as? CustomCell else { return UICollectionViewCell() }
        cell.setData(data: source[indexPath.item])
        return cell
    }


}

CustomCell 클래스 :

class CustomCell: UICollectionViewCell {

    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var widthConstraint: NSLayoutConstraint!

    override func awakeFromNib() {
        super.awakeFromNib()
        self.widthConstraint.constant = UIScreen.main.bounds.width
    }

    func setData(data: String) {
        self.label.text = data
    }

    override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
        return contentView.systemLayoutSizeFitting(CGSize(width: self.bounds.size.width, height: 1))
    }

}

주성분은 CustomcellsystemLayoutSizeFitting 함수입니다. 또한 제약 조건으로 Cell 내부 뷰의 너비를 설정해야합니다.


iPhone 너비 사이에 적응하기 위해 동적 너비가 필요하기 때문에 솔루션 중 어느 것도 나를 위해 작동하지 않았습니다.

    class CustomLayoutFlow: UICollectionViewFlowLayout {
        override init() {
            super.init()
            minimumInteritemSpacing = 1 ; minimumLineSpacing = 1 ; scrollDirection = .horizontal
        }

        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            minimumInteritemSpacing = 1 ; minimumLineSpacing = 1 ; scrollDirection = .horizontal
        }

        override var itemSize: CGSize {
            set { }
            get {
                let width = (self.collectionView?.frame.width)!
                let height = (self.collectionView?.frame.height)!
                return CGSize(width: width, height: height)
            }
        }
    }

    class TextCollectionViewCell: UICollectionViewCell {
        @IBOutlet weak var textView: UITextView!

        override func prepareForReuse() {
            super.prepareForReuse()
        }
    }




    class IntroViewController: UIViewController, UITextViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate, UINavigationControllerDelegate {
        @IBOutlet weak var collectionViewTopDistanceConstraint: NSLayoutConstraint!
        @IBOutlet weak var collectionViewTopDistanceConstraint: NSLayoutConstraint!
        @IBOutlet weak var collectionView: UICollectionView!
        var collectionViewLayout: CustomLayoutFlow!

        override func viewDidLoad() {
            super.viewDidLoad()

            self.collectionViewLayout = CustomLayoutFlow()
            self.collectionView.collectionViewLayout = self.collectionViewLayout
        }

        override func viewWillLayoutSubviews() {
            self.collectionViewTopDistanceConstraint.constant = UIScreen.main.bounds.height > 736 ? 94 : 70

            self.view.layoutIfNeeded()
        }
    }

collectionViewController에서 UICollectionViewDelegateFlowLayout 클래스를 상속해야합니다. 그런 다음 함수를 추가하십시오.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: view.frame.width, height: 100)
}

그것을 사용하면 화면 너비의 너비 크기가 있습니다.

그리고 이제 tableViewController로 행이있는 collectionViewController가 있습니다.

각 셀의 높이 크기를 동적으로 지정하려면 필요한 각 셀에 대해 사용자 지정 셀을 만들어야합니다.

참고 URL : https://stackoverflow.com/questions/44187881/uicollectionview-full-width-cells-allow-autolayout-dynamic-height

반응형