Высота UICollectionView изменяется при первой загрузке - PullRequest
0 голосов
/ 30 мая 2020

У меня есть UITableViewCell (MyTableViewCell), который содержит стек. Первый элемент в stackview - это subview (MyHeaderView), который содержит несколько меток и UICollectionView. У меня проблема, когда tableview изначально загружается там, где высота UICollectionView больше, чем содержимое, а затем он убирается / перескакивает до правильного размера (начальная высота примерно в два раза больше, чем у содержимого). Это также происходит только тогда, когда элементы в UICollectionView занимают более одной строки.

class MyHeaderView: UIView {

    private var identifierLabel = UILabel()
    private var statementLabel = UILabel()
    private var referenceIdLabel = UILabel()

    private var tagsCollectionView: DynamicHeightCollectionView!

    private var tagsArray = [String]()
    private var tags = [String]()

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() {
        initTagsCollectionViewLayout()

        backgroundColor = .red
        self.addSubview(contentView)
        contentView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

        contentView.addSubview(identifierLabel)
        contentView.addSubview(tagsCollectionView)
        contentView.addSubview(statementLabel)
        contentView.addSubview(referenceIdLabel)

        identifierLabel.layer.borderWidth = 4
        identifierLabel.layer.borderColor = UIColor.white.cgColor
        identifierLabel.snp.makeConstraints { make in
            make.top.equalToSuperview()
            make.leading.equalToSuperview()
            make.width.lessThanOrEqualTo(30)
            make.height.equalTo(30)
        }

        tagsCollectionView.snp.makeConstraints { make in
            make.top.equalTo(identifierLabel.snp.bottom)
            make.leading.equalToSuperview()
            make.trailing.equalToSuperview()
        }

        referenceIdLabel.numberOfLines = 0
        referenceIdLabel.snp.makeConstraints { make in
            make.top.equalTo(tagsCollectionView.snp.bottom)
            make.leading.equalToSuperview()
            make.trailing.equalToSuperview()
        }

        statementLabel.numberOfLines = 0
        statementLabel.snp.makeConstraints { make in
            make.top.equalTo(referenceIdLabel.snp.bottom)
            make.leading.equalToSuperview()
            make.trailing.equalToSuperview()
            make.bottom.equalToSuperview()
        }
    }

    lazy var contentView: UIView = {
        let view = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 400))
        view.layer.borderWidth = 4
        view.layer.borderColor = UIColor.white.cgColor

        view.backgroundColor = UIColor.red
        return view
    }()

    func initTagsCollectionViewLayout() {
        let flowLayout = LeftAlignedCollectionViewFlowLayout()

        tagsCollectionView = DynamicHeightCollectionView(frame: .zero, collectionViewLayout: flowLayout)

        tagsCollectionView.delegate = self
        tagsCollectionView.dataSource = self
        tagsCollectionView.isScrollEnabled = false
        tagsCollectionView.backgroundColor = .white

        flowLayout.estimatedItemSize = CGSize(width: 50, height: 20)
        flowLayout.minimumInteritemSpacing = 4
        flowLayout.minimumLineSpacing = 8
        flowLayout.scrollDirection = .vertical

        self.tagsCollectionView.collectionViewLayout = flowLayout
        self.tagsCollectionView.layoutIfNeeded()
        self.tagsCollectionView.register(UINib.init(nibName: Constants.tagsCollectionCellNibName, bundle: nil), forCellWithReuseIdentifier: Constants.tagsCollectionCellReuseID)
    }

    // MARK: Set up view
    func setUpView(observation: Observation) {
        identifierLabel.text = observation.identifier
        statementLabel.text = observation.statement
        referenceIdLabel.text = observation.referenceID

        tagsArray = [String]()

        if let tags = observation.tags, tags.count > 0 {
            for tag in tags {
                tagsArray.append(tag)
            }
        }

        if tagsArray.count > 0 {
            tagsCollectionView.reloadData()
            //tagsCollectionView.setNeedsLayout()
            tagsCollectionView.layoutIfNeeded()
        }
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        if (self.superview != nil) {
            self.superview?.layoutIfNeeded()
        }

        return tagsCollectionView.contentSize
    }
}

extension MyHeaderView: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return tagsArray.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        var cell = UICollectionViewCell()

        switch collectionView {
        case tagsCollectionView:
            let tagsCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.tagsCollectionCellReuseID, for: indexPath) as! TagsCollectionViewCell

            tagsCollectionViewCell.initRequirementTag(tagText: self.tagsArray[indexPath.item])

            cell = tagsCollectionViewCell

        default:
            break
        }

        return cell
    }
}

// MARK: - Constants
private enum Constants {
    static let tagsCollectionCellReuseID = "TagsCollectionViewCell"
    static let tagsCollectionCellNibName = "TagsCollectionViewCell"
}








class MyTableViewCell: UITableViewCell {

    private var stackView = UIStackView()
    private var myHeaderView = MyHeaderView()
    private var observation: Observation?


    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit() {
        self.addSubview(stackView)

        stackView.axis = .vertical
        stackView.spacing = 4
        stackView.distribution = .fillProportionally
        stackView.alignment = .fill
        stackView.arrangedSubviews.forEach({ $0.removeFromSuperview() }) // clear stack view on each load

        stackView.addArrangedSubview(myHeaderView)

        stackView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            //make.width.equalToSuperview()
        }

        myHeaderView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

    }

    func setupView(observation: Observation) {
        self.observation = observation

        myHeaderView.setUpView(observation: observation)
    }

}





class DynamicHeightCollectionView: UICollectionView {

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

    override var intrinsicContentSize: CGSize {
        return contentSize
    }
}

class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)

        var leftMargin = sectionInset.left
        var maxY: CGFloat = -1.0
        attributes?.forEach { layoutAttribute in
            if layoutAttribute.frame.origin.y >= maxY {
                leftMargin = sectionInset.left
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
            maxY = max(layoutAttribute.frame.maxY , maxY)
        }

        return attributes
    }
}




// in tableview controller
var obs1 = Observation(identifier: "1.1", statement: "custom statement test", referenceID: "reference id test", tags: ["tag 1", "tag 2", "tag 3", "tag 4", "tag 5", "tag 6", "tag 7", "tag 8", "tag 9", "tag 10", "tag 11", "tag 12", "tag 13", "tag 14", "tag 15", "tag 16", "tag 17", "tag 18", "tag 19", "tag 20"])

    var obs2 = Observation(identifier: "1.2", statement: "custom statement test thats runs onto multiple lines, custom statement test thats runs onto multiple lines, custom statement test thats runs onto multiple lines, custom statement test thats runs onto multiple lines", referenceID: "reference id test that runs onto multiple lines, reference id test that runs onto multiple lines reference id test that runs onto multiple lines reference id test that runs onto multiple lines reference id test that runs onto multiple lines", tags: ["tag 1", "tag 2", "tag 3", "tag 4", "tag 5", "tag 6", "tag 7", "tag 8"])

    var obs3 = Observation(identifier: "1.3", statement: "custom statement test", referenceID: "reference id test", tags: [])

    var obs4 = Observation(identifier: "1.4", statement: "custom statement test", referenceID: "reference id test", tags: [])

var obs: [Observation]
obs.append(obs1)
obs.append(obs2)
obs.append(obs3)
obs.append(obs4)

struct Observation {
    var identifier: String
    var statement: String
    var referenceID: String
    var tags: [String]
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ObservationTableViewCellV2") as! ObservationTableViewCellV2

        let observation = obs[indexPath.row] as! Observation
        cell.setupView(observation: observation)

        return cell
    }

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return obs.count
    }

Я пытался изменить приблизительный размер элемента и UICollectionViewDelegateFlowLayout sizeForItemAt, но это не имеет никакого значения.

UICollectionView выделено синим цветом. При первой загрузке он выглядит так: enter image description here

Затем через долю секунды он изменяется до правильного размера: enter image description here

Ответы [ 4 ]

0 голосов
/ 08 июня 2020

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

У меня был аналогичный простая проблема, когда я поместил некоторые logi изменения размера c в viewDidAppear, а не в viewWillAppear. Внесение этого простого изменения имело все значение.

0 голосов
/ 03 июня 2020

Попытайтесь явно запустить layoutIfNeeded в основном потоке внутри initTagsCollectionViewLayout. Это могло быть проблемой здесь.

DispatchQueue.main.async {
   self.tagsCollectionView.layoutIfNeeded()
}
0 голосов
/ 04 июня 2020

Когда вы выводите из очереди ячейку, содержащую представление коллекции, проверьте ее ширину. Вероятно, он меньше ширины табличного представления (может быть, 320?). Установите для него что-то большее, например, ширину tableView в cellForRowAt indexPath:, как только вы удалите его из очереди, чтобы представление коллекции могло правильно вычислить его высоту. После того, как вы установите его ширину и добавлен collectionView, вызовите layoutIfNeeded()

0 голосов
/ 30 мая 2020

В раскадровке внутри раздела инспектора размеров сделайте Estimate Size в представлении коллекции от Automatic до None.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...