CollectionView content height size doesn't properly update inside tableView cell - ios

I have a collectionView inside a tableView cell. I'm using a custom flowLayout to customize collectionView cells (all have to be custom size and aligned to left) and update the collectionView height and fit all elements inside it.
The tableView cell is auto growing. So the collectionView have to adjust to the content, and the tableViewCell have to adjust to collectionView.
Here is my tableView cell class, with collectionView configuration inside:
import UIKit
class SearchStationsTableViewCell: UITableViewCell {
var delegate : SearchStationsTableViewCellProtocol?
static let heightForRow = 234
var servicos = [String]()
var serviceList = ServicesList()
var station : Station?
#IBOutlet weak var background: UIView!
#IBOutlet weak var nameLabel : UILabel!
#IBOutlet weak var seeOnMapButton : UIButton!
#IBOutlet weak var favoriteButton: UIButton!
#IBOutlet weak var addressLabel : UILabel!
#IBOutlet weak var distanceLabel : UILabel!
#IBOutlet weak var collectionView : UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.background.shadowWith(color: UIColor.black.cgColor, opacity: 0.1, size: CGSize(width: 0.0, height: 3.0), radius: 5.0)
self.collectionView.delegate = self
self.collectionView.dataSource = self
registerCells()
}
func registerCells() {
collectionView?.register(UINib(nibName: "StationFiltersCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "StationFiltersCollectionViewCell")
}
/// Configura células da tableView
///
/// - Parameter station:
func configure(station: Station){
// constraintCollectionHeight?.constant = 0.0
let columnLayout = FlowLayout(
minimumInteritemSpacing: 5,
minimumLineSpacing: 5,
sectionInset: UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
)
collectionView?.collectionViewLayout = columnLayout
collectionView.isScrollEnabled = false
self.station = station
self.nameLabel.text = station.nomeFantasia
self.addressLabel.text = station.endereco
if(station.isFavorite)!{
self.favoriteButton.setImage(UIImage(named: "icFavoriteMarked"), for: .normal)
self.favoriteButton.isUserInteractionEnabled = false
}else{
self.favoriteButton.setImage(UIImage(named:"icFavorite"), for: .normal)
self.favoriteButton.isUserInteractionEnabled = true
}
if let _servicos = station.servicos, _servicos.count > 0{
self.servicos = _servicos
self.collectionView?.reloadData()
let heightSize = self.collectionView.collectionViewLayout.collectionViewContentSize.height
self.collectionView?.heightAnchor.constraint(equalToConstant:heightSize).isActive = true
}
}
#IBAction func favoriteButton_action(sender: Any){
self.delegate?.favoriteButtonPressed(sender:self)
}
#IBAction func callButon_action(sender: Any){
self.delegate?.callButtonPressed(station: station!)
}
#IBAction func routeButton_action(sender: Any){
self.delegate?.routeButtonPressed(station: station!)
}
#IBAction func seeOnMap_action(sender: Any){
self.delegate?.seeOnMapButtonPressed(sender: sender,station: station!)
}
}
extension SearchStationsTableViewCell : UICollectionViewDelegate, UICollectionViewDataSource{
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return servicos.count
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "StationFiltersCollectionViewCell", for: indexPath) as! StationFiltersCollectionViewCell
if(ServicesList.haveImage(serviceName: serviceList.serviceName[indexPath.row])){
cell.filterImage?.image = UIImage(named: serviceList.getImage(serviceName: serviceList.serviceName[indexPath.row]))
cell.filterName?.text = ""
}else{
let title = serviceList.getFriendlyName(serviceName: serviceList.serviceName[indexPath.row])
cell.filterName?.text = title
cell.filterImage?.image = UIImage()
}
return cell
}
}
class FlowLayout: UICollectionViewFlowLayout {
required init(minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
super.init()
estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
//sectionInsetReference = .fromSafeArea
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
guard scrollDirection == .vertical else { return layoutAttributes
}
// Filter attributes to compute only cell attributes
let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })
// Group cell attributes by row (cells with same vertical center) and loop on those groups
for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
// Set the initial left inset
var leftInset = sectionInset.left
// Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
for attribute in attributes {
attribute.frame.origin.x = leftInset
leftInset = attribute.frame.maxX + minimumInteritemSpacing
}
}
return layoutAttributes
}
}

Related

How to get all UICollectionViewCells to share the same height?

I have a UICollectionView where the Cell's do not fill their vertical space. What adjustment should I make to ensure each cell fills up the entire cell area? Or at least all share the same height for the row they are on?
Here is the Storyboard
UICollectionViewFlowLayout
class AddServiceFlowLayout: UICollectionViewFlowLayout {
let cellsPerRow: Int
init(cellsPerRow: Int, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
self.cellsPerRow = cellsPerRow
super.init()
self.minimumInteritemSpacing = minimumInteritemSpacing
self.minimumLineSpacing = minimumLineSpacing
self.sectionInset = sectionInset
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepare() {
super.prepare()
guard let collectionView = collectionView else { return }
let marginsAndInsets = sectionInset.left + sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
itemSize = CGSize(width: itemWidth, height: itemWidth)
}
override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
return context
}
}
UICollectionViewCell
class AddServiceViewCell: UICollectionViewCell {
#IBOutlet weak var labelStackView: UIStackView!
#IBOutlet weak var tvServiceName: UILabel!
#IBOutlet weak var tvQuantityNeeded: UILabel!
public var onCellTapped: (() -> ())?
override func awakeFromNib() {
super.awakeFromNib()
self.layer.cornerRadius = 10.0
}
override func prepareForReuse() {
super.prepareForReuse()
self.tvServiceName.text = nil
self.tvQuantityNeeded.show()
}
public static func nib() -> UINib {
return UINib.init(nibName: identifier, bundle: Bundle(for: ServiceLineItemCell.self))
}
public func configure(with service: TowService){
self.tvServiceName.text = service.description
if(service.calculated){
self.tvQuantityNeeded.hide()
}
}
public func eventTriggered()
{
onCellTapped?()
}
}
UIViewController
class AddServiceViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
private let columnLayout = AddServiceFlowLayout(
cellsPerRow: 3,
minimumInteritemSpacing: 10,
minimumLineSpacing: 10,
sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
)
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Add Service"
// Register cell classes
self.collectionView!.delegate = self
self.collectionView!.dataSource = self
self.collectionView!.collectionViewLayout = columnLayout
self.collectionView!.contentInsetAdjustmentBehavior = .always
self.collectionView!.register(AddServiceViewCell.nib().self, forCellWithReuseIdentifier: AddServiceViewCell.identifier)
}
// removed for brevity ....
// MARK: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
extension AddServiceViewController : UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return allServices.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AddServiceViewCell.identifier, for: indexPath) as! AddServiceViewCell
cell.configure(with: allServices[indexPath.row])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.onConfirmAdd(allServices[indexPath.row])
}
}
Here is a road map
1- You need to implement sizeForItemAt
2- Consider you have 3 columns per row , create a function that that accepts 3 Items ( introduced with the current indexPath.item ) from your model and manually calculate maximum height for each string in that model
3- Return the maximum height and by this you will set that height for all cells of same row

Align the collectionviewcells center

I current have a collectionview attached to a array of strings. I want to align these cells center regardless of the number of things in the array. I am not sure how to do it, and where to do it.
import UIKit
class MyButtonCell: UICollectionViewCell{
#IBOutlet weak var buttonOne: UIButton!
#IBOutlet weak var targetButton: UIButton!
var callback: (() -> ())?
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.black.cgColor
}
#IBAction func buttonTapped(_ sender: UIButton) {
callback?()
}
}
class StevenViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
let buttonTitles: [String] = [
"4", "6", "7", "8"
]
var targetButtonTitles: [String] = [
"", "", "", ""
]
var current:String = ""
#IBOutlet var collectionView: UICollectionView!
#IBOutlet var targetCollection: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
targetCollection.delegate = self
targetCollection.dataSource = self
collectionView.delegate = self
collectionView.dataSource = self
collectionView.tag = 1
targetCollection.tag = 2
}
func centerItemsInCollectionView(cellWidth: Double, numberOfItems: Double, spaceBetweenCell: Double, collectionView: UICollectionView) -> UIEdgeInsets {
let totalWidth = cellWidth * numberOfItems
let totalSpacingWidth = spaceBetweenCell * (numberOfItems - 1)
let leftInset = (collectionView.frame.width - CGFloat(totalWidth + totalSpacingWidth)) / 2
let rightInset = leftInset
return UIEdgeInsets(top: 0, left: leftInset, bottom: 0, right: rightInset)
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView.tag == 1 {
return buttonTitles.count
} else {
return targetButtonTitles.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// set the button title (and any other properties)
if collectionView.tag == 1 {
// Setup here your cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCellID", for: indexPath) as! MyButtonCell
cell.callback = {
print("Button was tapped at \(indexPath)")
self.targetButtonTitles[indexPath.item] = self.buttonTitles[indexPath.item]
//print(self.targetButtonTitles)
self.current = self.buttonTitles[indexPath.item]
print(self.current)
//collectionView.reloadData()
// do what you want when the button is tapped
}
cell.buttonOne.setTitle(buttonTitles[indexPath.item], for: [])
return cell
} else {
// Setup here your targetCell
let targetCell = targetCollection.dequeueReusableCell(withReuseIdentifier: "myCellID", for: indexPath) as! MyButtonCell
targetCell.callback = {
if self.current != ""{
self.targetButtonTitles[indexPath.item] = self.current
print(self.targetButtonTitles)
targetCell.targetButton.setTitle(self.targetButtonTitles[indexPath.item], for: [])
self.current = ""
}else{
self.targetButtonTitles[indexPath.item] = ""
targetCell.targetButton.setTitle(self.targetButtonTitles[indexPath.item], for: [])
}
}
return targetCell
}
}
}
As you can see, right now they are just starting from the left. So, How should I align them in the middle.

cellForItemAt called only once in Swift collectionView

If I use flow layout with collectionView, then all my cells are visible with the data. If I use a custom layout, then cellForItemAt is only accessed for index (0,0), and correspondingly only a single cell is displayed.
I'm baffled why - please help!
Minimal example below:
ViewController:
import UIKit
private let reuseIdentifier = "customCell"
class customCollectionViewController: UICollectionViewController {
#IBOutlet var customCollectionView: UICollectionView!
let dwarfArray = ["dopey", "sneezy", "bashful", "grumpy", "doc", "happy", "sleepy"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dwarfArray.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! customCollectionViewCell
let cellContentsIndex = indexPath.row
if cellContentsIndex <= dwarfArray.count
{
cell.displayContent(name: dwarfArray[cellContentsIndex])
}
return cell
}
}
Custom Cell
import UIKit
class customCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var nameLabel: UILabel!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect){
super.init(frame: frame)
}
public func displayContent(name: String){
nameLabel.text = name
}
func setup(){
self.layer.borderWidth = 1.0
self.layer.borderColor = UIColor.black.cgColor
}}
Custom Layout
If this is not here - I can see all the cells I expect (albeit without my preferred layout). When I use this, I only see one cell.
import UIKit
class customCollectionViewLayout: UICollectionViewLayout {
let CELL_SIZE = 100.0
var cellAttrsDictionary = Dictionary<NSIndexPath, UICollectionViewLayoutAttributes>()
//define the size of the area the user can move around in within the collection view
var contentSize = CGSize.zero
var dataSourceDidUpdate = true
func collectionViewContentSize() -> CGSize{
return self.contentSize
}
override func prepare() {
if (collectionView?.numberOfItems(inSection: 0))! > 0 {
/// cycle through each item of the section
for item in 0...(collectionView?.numberOfItems(inSection: 0))!-1{
/// build the collection attributes
let cellIndex = NSIndexPath(item: item, section: 0)
let xPos = Double(item)*CELL_SIZE
let yPos = 40.0
let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex as IndexPath)
cellAttributes.frame = CGRect(x: xPos, y:yPos, width: CELL_SIZE, height: CELL_SIZE)
// cellAttributes.frame = CGRect(x: xPos, y:yPos, width: CELL_WIDTH + 2*CELL_SPACING, height: CELL_HEIGHT)
cellAttributes.zIndex = 1
//save
cellAttrsDictionary[cellIndex] = cellAttributes
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
/// create array to hold all the elements in our current view
var attributesInRTect = [UICollectionViewLayoutAttributes]()
/// check each element to see if they should be returned
for cellAttributes in cellAttrsDictionary.values {
if rect.intersects(cellAttributes.frame)
{
attributesInRTect.append(cellAttributes)
}
}
return attributesInRTect
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return cellAttrsDictionary[indexPath as NSIndexPath]!
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}}
Output
The problem is with contentSize value
func collectionViewContentSize() -> CGSize{
return self.contentSize
}
Just replace func collectionViewContentSize()... by something like this:
func lastLayoutAttributes() -> UICollectionViewLayoutAttributes? {
return cellAttrsDictionary.values.map { $0 }.sorted(by: { $0.frame.maxX < $1.frame.maxX }).last
}
override var collectionViewContentSize: CGSize {
guard let collectionView = collectionView else { return .zero }
guard collectionView.frame != .zero else { return .zero }
let width: CGFloat
let height: CGFloat = collectionView.frame.height
if let lastLayoutAttributes = lastLayoutAttributes() {
width = lastLayoutAttributes.frame.maxX
} else {
width = 0
}
return CGSize(width: width, height: height)
}
And you will see more than one cell.

Collectionview dynamic width not worked Swift

I have one collectionView inside Table view, it works perfectly but the issue is that collection view cell width not worked perfectly on initial stage but worked once scrolls it.
You can check here that in the first section, it shows full name but in other section, it truncates tail and that works after scrolls.
Here is the code that matters
class SubjectsViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributesForElementsInRect = super.layoutAttributesForElements(in: rect)
var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()
// use a value to keep track of left margin
var leftMargin: CGFloat = 0.0
for attributes in attributesForElementsInRect! {
let refAttributes = attributes
// assign value if next row
if (refAttributes.frame.origin.x == self.sectionInset.left) {
leftMargin = self.sectionInset.left
} else {
// set x position of attributes to current margin
var newLeftAlignedFrame = refAttributes.frame
newLeftAlignedFrame.origin.x = leftMargin
if newLeftAlignedFrame.origin.x + newLeftAlignedFrame.size.width > (self.collectionView?.bounds.size.width)! {
leftMargin = 0.0
newLeftAlignedFrame.origin.x = 0.0
if (newAttributesForElementsInRect.last?.frame.origin.y == newLeftAlignedFrame.origin.y){
newLeftAlignedFrame.origin.y = newLeftAlignedFrame.origin.y + newLeftAlignedFrame.height + minimumLineSpacing
}
}
refAttributes.frame = newLeftAlignedFrame
}
// calculate new value for current margin
leftMargin += refAttributes.frame.size.width + 10
newAttributesForElementsInRect.append(refAttributes)
}
return newAttributesForElementsInRect
}
}
class DynamicCollectionView: UICollectionView {
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
if self.superview?.superview?.superview is UITableView {
(self.superview?.superview?.superview as! UITableView).beginUpdates()
(self.superview?.superview?.superview as! UITableView).endUpdates()
}
}
}
override var intrinsicContentSize: CGSize {
return collectionViewLayout.collectionViewContentSize
}
}
class TagTableCell: UITableViewCell {
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var flowLayout: SubjectsViewFlowLayout!
override func awakeFromNib() {
super.awakeFromNib()
collectionView.register(UINib(nibName: "TagCollectionCell", bundle: nil), forCellWithReuseIdentifier: "TagCollectionCell")
}
func setupCell() {
let flowLayout = collectionView.collectionViewLayout as? SubjectsViewFlowLayout
flowLayout?.estimatedItemSize = .init(width: 100, height: 45)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.collectionView.reloadData()
}
}
func setCollectionViewDataSourceDelegate<D: UICollectionViewDataSource & UICollectionViewDelegate>(_ dataSourceDelegate: D, forRow row: Int) {
setupCell()
collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row + 1
collectionView.layoutIfNeeded()
}
}
You just need to call datasource and datadelegate in async method while adding the UICollectionView in tableview cell
DispatchQueue.main.async {
self.collectionView.delegate = dataSourceDelegate
self.collectionView.dataSource = dataSourceDelegate
}
You can modify your cell size just by adding this function, change your desired width and height for each cell.
Here is an example to make the cell's width half of the collection view's.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellWidth = collectionView.layer.bounds.width / 2
let cellHeight : CGFloat = 150
return CGSize(width: cellWidth, height: cellHeight)
}
You should implement this Protocol to your class:
UICollectionViewDelegateFlowLayout

Changing UICollectionview cell XY position - swift 3

I'm using a UICollectionview with XX cells. How can i change the XY position on every single cell programmatically? I can change the size of every cell with:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
Is there another function like above that changes the XY positioning?
I hope this example will help you. Let me explain this code blocks. You can think standard UICollectionView and Cell design. There is no detailView. This code implementation will create parallax effect HeaderView.
FIRST AND FOREMOST
import UIKit
class DIYLayoutAttributes: UICollectionViewLayoutAttributes {
var deltaY: CGFloat = 0
override func copy(with zone: NSZone?) -> Any {
let copy = super.copy(with: zone) as! DIYLayoutAttributes
copy.deltaY = deltaY
return copy
}
override func isEqual(_ object: Any?) -> Bool {
if let attributes = object as? DIYLayoutAttributes {
if attributes.deltaY == deltaY {
return super.isEqual(object)
}
}
return false
}
}
class DIYLayout: UICollectionViewFlowLayout {
var maximumStretchHeight: CGFloat = 0
override class var layoutAttributesClass : AnyClass {
return DIYLayoutAttributes.self
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Create layout attributes
let layoutAttributes = super.layoutAttributesForElements(in: rect) as! [DIYLayoutAttributes]
// How far user scrollview.
let insets = collectionView!.contentInset
let offset = collectionView!.contentOffset
let minY = -insets.top
// we need the calculate delta.
if (offset.y < minY) {
let deltaY = fabs(offset.y - minY)
// we can loop through all attributes
for attributes in layoutAttributes {
//Manipulate attributes
if let elementKind = attributes.representedElementKind {
if elementKind == UICollectionElementKindSectionHeader {
// create frame.
var frame = attributes.frame
frame.size.height = max(minY, headerReferenceSize.height + deltaY)
//We need the change origin for scrolling.
frame.origin.y = frame.minY - deltaY
attributes.frame = frame
attributes.deltaY = deltaY
}
}
}
}
return layoutAttributes
}
// Layout will scroll.
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
SECOND
You need custom cell design.
import UIKit
class ScheduleHeaderView: UICollectionReusableView {
#IBOutlet fileprivate weak var backgroundImageView: UIView!
#IBOutlet fileprivate weak var backgroundImageViewHeightLayoutConstraint: NSLayoutConstraint!
#IBOutlet fileprivate weak var foregroundImageView: UIView!
#IBOutlet fileprivate weak var foregroundImageViewHeightLayoutConstraint: NSLayoutConstraint!
fileprivate var backgroundImageViewHeight: CGFloat = 0
fileprivate var foregroundImageViewHeight: CGFloat = 0
fileprivate var previousHeight: CGFloat = 0
override func awakeFromNib() {
super.awakeFromNib()
backgroundImageViewHeight = backgroundImageView.bounds.height
foregroundImageViewHeight = foregroundImageView.bounds.height
}
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
let attributes = layoutAttributes as! DIYLayoutAttributes
let height = attributes.frame.height
if previousHeight != height {
backgroundImageViewHeightLayoutConstraint.constant = backgroundImageViewHeight - attributes.deltaY
foregroundImageViewHeightLayoutConstraint.constant = foregroundImageViewHeight + attributes.deltaY
previousHeight = height
}
}
}
THIRD
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "ScheduleHeader", for: indexPath) as! ScheduleHeaderView
return header
}
Of course we need to from viewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
//Add layout.
let width = collectionView!.bounds.width
let layout = collectionViewLayout as! DIYLayout
layout.headerReferenceSize = CGSize(width: width, height: 180)
layout.itemSize = CGSize(width: width, height: 62)
layout.maximumStretchHeight = width
}

Resources