Подтвердить что ты не робот

UICollectionView, ячейки полной ширины, позволяют автонастраивать динамическую высоту?

В (скажем так) вертикали UICollectionView,

Можно ли иметь ячейки полной ширины, но разрешить динамическую высоту для автоматического определения высоты?

(Если вы новичок в iOS, "динамическая высота" означает, что каждая ячейка может быть любой высоты, например, из-за текста или изображений.)

Мне кажется, что это, пожалуй, "самый важный вопрос в iOS без действительно хорошего ответа".


Это, безусловно,

самая сложная проблема во всех iOS :-(

Середина 2019 года... Я добавил еще одну награду - по-прежнему нет действительно хорошего решения этой основной проблемы.

WTH, Apple?


Важно - это академический вопрос - просто используйте табличное представление!

Около 50 000 человек изучили эту страницу! Как указывают xxtesaxx и другие,

... вы можете сделать это мгновенно, без усилий, с помощью таблицы.

В эти дни (2019 г.) вы ничего не делаете - вообще ничего - для представления таблицы, чтобы оно идеально работало с элементами динамической высоты в ячейках.

(Предполагая, что вы знаете, как правильно "связать" ячейку вверх и вниз по ограничениям, что является обязательным.)

Поэтому единственная причина сделать то, что просят на этой странице, - это пытка.

Опять же: имейте в виду, что в эти дни вы можете сделать это без особых усилий с обычным представлением таблицы!

4b9b3361

Ответ 1

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

Ожидаемое отображение:

enter image description here

Ответ 2

проблема

Вы ищете автоматическую высоту, а также хотите иметь полную ширину. Невозможно получить и то и другое при использовании UICollectionViewFlowLayoutAutomaticSize.

Вы хотите использовать UICollectionView поэтому ниже приведено решение для вас.

Решение

Шаг I: Рассчитать ожидаемую высоту ячейки

1. Если у вас есть только UILabel в CollectionViewCell чем установлено значение numberOfLines=0 и которое рассчитало ожидаемую высоту UIlable, передайте все три параметра

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. Если он содержит оба, то рассчитывается их высота и складывается их вместе.

ШАГ 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)
}

Я надеюсь, что это поможет вам.

Ответ 3

Есть несколько способов решить эту проблему.

Один из способов - вы можете дать макету представления представления коллекции приблизительный размер и вычислить размер ячейки.

Примечание. Как уже упоминалось в комментариях ниже, с 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)
}

Пока это не идеальные примеры готовой продукции, они должны начать работать в правильном направлении. Я не могу сказать, что это лучшая практика, но это работает для меня даже с довольно сложными ячейками, содержащими несколько ярлыков, которые могут или не могут быть перенесены на несколько строк.

Ответ 4

Я нашел довольно простое решение для этой проблемы: внутри моего 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

Результатом является ячейка с полной шириной и динамической высотой.

enter image description here

Ответ 5

Вы должны добавить ограничение ширины в CollectionViewCell

class SelfSizingCell: UICollectionViewCell {

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

Ответ 6

Лично я нашел лучшие способы иметь UICollectionView, где AutoLayout определяет размер, в то время как каждая ячейка может иметь различный размер, - это реализовать функцию UICollectionViewDelegateFlowLayout sizeForItemAtIndexPath, используя фактическую ячейку для измерения размера.

Я говорил об этом в одном из моих постов в блоге

Надеюсь, этот поможет вам достичь того, чего вы хотите. Я не уверен на 100%, но я верю, что в отличие от UITableView, где вы можете иметь полностью автоматическую высоту ячеек, используя AutoLayout в сочетании с

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44

UICollectionView не имеет такого способа позволить AutoLayout определять размер, потому что UICollectionViewCell не обязательно заполняет всю ширину экрана.

Но вот вопрос для вас: если вам нужны ячейки полной ширины экрана, зачем вам вообще использовать UICollectionView поверх старого доброго UITableView, который поставляется с ячейками авторазмера?

Ответ 7

Согласно моему комментарию к ответу Эрика, мое решение очень похоже на его, но я должен был добавить ограничение в предпочитаемый размер для... для ограничения фиксированного размера.

    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
    }

В этом вопросе есть несколько дубликатов, я подробно ответил на них здесь и предоставил пример рабочего приложения.

Ответ 8

  1. Задайте estimatedItemSize размер вашего макета потока:

    collectionViewLayout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
    
  2. Определите ограничение ширины в ячейке и установите его равным ширине суперпредставления:

    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 width.
            widthConstraint?.constant = superview?.bounds.width ?? 0
            widthConstraint?.isActive = true
            super.updateConstraints()
        }
    
        ...
    }
    

Полный пример

Протестировано на iOS 11.

Ответ 9

Не уверен, что это квалифицируется как "действительно хороший ответ", но это то, что я использую для достижения этого. Моя структура потока горизонтальна, и я пытаюсь настроить ширину с автозапуском, поэтому она похожа на вашу ситуацию.

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 снова, используя новый вид коллекции. В вашем случае он должен иметь возможность обновлять данные, учитывая новые ограничения, доступные в макете.

Ответ 10

Начиная с iOS 10, для этого у нас появился новый API для макета потока.

Все, что вам нужно сделать, это установить свой flowLayout.estimatedItemSize в новую константу, UICollectionViewFlowLayoutAutomaticSize.

Источник

Ответ 11

На вашем 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
    }

Вояла, вы, теперь, будут автоматически увеличивать высоту!

Ответ 12

ЗА РАБОТОЙ!!! Протестировано на IOS: 12.1

У меня есть очень простое решение, которое просто работает без нарушения ограничений.

enter image description here

Мой 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))
    }

}

Основной ингредиент функция systemLayoutSizeFitting в Customcell. А также мы должны установить ширину представления внутри ячейки с ограничениями.

Ответ 13

Ни один из решений не работал у меня, так как мне нужна динамическая ширина для адаптации между шириной 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()
        }
    }

Ответ 14

Вам необходимо наследовать класс UICollectionViewDelegateFlowLayout на вашем коллекционном контролере. Затем добавьте функцию:

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

Используя это, вы имеете размер ширины ширины экрана.

И теперь у вас есть collectionViewController со строками как tableViewController.

Если вы хотите, чтобы размер высоты каждой ячейки был динамически, возможно, вам нужно создать пользовательские ячейки для каждой ячейки, в которой вы нуждаетесь.