所以我一直在尝试使用自定义FlowLayout创建一个集合视图,以根据它包含的UICollectionViewCells调整自己的高度。我一直在网上搜索很多,我找到的所有解决方案导致在运行时引发不同的异常。
要知道的是,我的collectionview位于tableview单元格内。当tableview单元格的内容需要更多/更少的空间时,它们的高度会相应变化,但是它们包含的集合视图不会....
大多数情况下,有问题的集合视图有一两个项目,它们的宽度使它们适合单行。但有时,物品太宽,因此需要相互包裹。
当发生这种情况时,collectionview的高度应该展开以显示两行项目,但这不会发生。
一些信息:
class BaseOnboardingTableViewCell: UITableViewCell {
@IBOutlet weak var watsonIndicator: UIView!
@IBOutlet weak var cellTextLabel: UILabel!
@IBOutlet weak var customContentViewContainer: UIView!
@IBOutlet weak var actionButtonsCollection: UICollectionView!
@IBOutlet var customContentViewContainerHeightConstraint: NSLayoutConstraint!
@IBOutlet var actionButtonsCollectionHeightConstraint: NSLayoutConstraint!
@IBOutlet var customContentDistanceFromCellTextLabelConstraint: NSLayoutConstraint!
@IBOutlet var actionButtonsCollectionDistanceFromCustomContentConstraint: NSLayoutConstraint!
/// Keeps track wether the cell has animated for its intro or not
var hasAnimated: Bool = false
/// Receiver for interaction with chat actions
var chatActionDelegate: OnboardingChatActionDelegate?
/// Dictates what the cell should contain and display
var onboardingStep: OnboardingStep! {
didSet {
if onboardingStep.text() != nil {
cellTextLabel.text = onboardingStep.text()!
} else {
hideText()
}
if onboardingStep.customContent != nil {
loadCustomContent()
} else {
hideCustomContent()
}
if onboardingStep.chatActions == nil {
hideActionButtonsCollection()
} else {
actionButtonsCollection.collectionViewLayout.invalidateLayout()
actionButtonsCollection.reloadData()
actionButtonsCollectionHeightConstraint.constant = actionButtonsCollection.contentSize.height
}
}
}
/// Prepares the cell before setting its OnboardingStep
override func awakeFromNib() {
super.awakeFromNib()
self.translatesAutoresizingMaskIntoConstraints = true
let collectionController = OnboardingChatActionCollectionViewController()
collectionController.attach(toCollection: actionButtonsCollection)
collectionController.onboardingCellDelegate = self
actionButtonsCollection.sizeToFit()
watsonIndicator.layer.cornerRadius = watsonIndicator.frame.width/2
makeAllTransparent()
}
class OnboardingChatActionCollectionViewController: UICollectionViewFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate {
var onboardingCellDelegate: BaseOnboardingTableViewCell?
/// This will link a UICollectionView to this controller
func attach(toCollection collection: UICollectionView) {
collection.delegate = self
collection.dataSource = self
collection.collectionViewLayout = self
collection.contentInset.left = 0
collection.contentInset.right = 0
collection.backgroundColor = UIColor.clear
self.estimatedItemSize = CGSize(width: 50, height: 29)
collection.register(UINib(nibName: "ChatActionButton", bundle: nil), forCellWithReuseIdentifier: "ChatActionButtonCell")
}
/// Renders buttons inactive and greys them out (based on user selection) after an action was tapped
fileprivate func greyOutButtons(selectedCell: OnboardingChatActionCollectionViewCell) {
DispatchQueue.main.async {
let cells = self.collectionView!.visibleCells as! [OnboardingChatActionCollectionViewCell]
for cell in cells {
if cell == selectedCell {
cell.background.backgroundColor = UIColor(colorLiteralRed: 52/255, green: 51/255, blue: 52/255, alpha: 1)
} else {
cell.background.backgroundColor = UIColor.aqua10
cell.actionButton.setTitleColor(UIColor.gunmetal, for: .normal)
}
cell.actionButton.isEnabled = false
}
}
}
/// Wrapping buttons on a new row if they're too large
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
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return onboardingCellDelegate?.onboardingStep.chatActions?.count ?? 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let chatActionCell = self.collectionView!.dequeueReusableCell(withReuseIdentifier: "ChatActionButtonCell", for: indexPath) as! OnboardingChatActionCollectionViewCell
chatActionCell.chatAction = onboardingCellDelegate!.onboardingStep.chatActions![indexPath.row]
chatActionCell.background.layer.cornerRadius = chatActionCell.background.frame.height / 2
chatActionCell.sizeToFit()
chatActionCell.collectionControllerDelegate = self
return chatActionCell
}