У меня есть 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
выделено синим цветом. При первой загрузке он выглядит так:
Затем через долю секунды он изменяется до правильного размера: