Collapse/Expand HeaderView With Tabs Part1

yusef naser
SwiftCairo
Published in
4 min readMay 17, 2020

when you want to create collapse/expand header view like Android, you can see many tutorials about it with one scrollView, In this tutorials, we create it but with two or more tabs.

1- Creating a design

we will create a project and call it “Expand-CollapseHeaderWithTabs” and open Main.storyboard
We will add UIView and collectionView.

1.1 Adding UIView

add UIView to storyboard “ViewController” and add Constraints [Top, Leading, Trailing, height] which height = 250 or any height you want.

1.2 Adding CollectionView:

adding collectionView at the bottom of UIView and add Constraints [Top, Leading, Trailing, Bottom] and make scroll Direction of collectionView is Horizontal and check on paging Enable.

Now open ViewController.swift and take objects from collectionView and height of UIView, then set delegate and dataSource of collection to self

import UIKit 
class ViewController: UIViewController {

@IBOutlet weak var heightView : NSLayoutConstraint!
@IBOutlet weak var collectionView: UICollectionView!

override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
}
}extension ViewController : UICollectionViewDelegate ,
UICollectionViewDataSource {

func collectionView(_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
return 2 // the number of tabs you want
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
}

After that, we need to create Cells for collectionView
these cells are represented as tabs. We will create two Cells.

2- Creating Cells

we will be creating two cells, the first cell contains scrollView and second cell contains TableView

2.1- ScrollView Cell
In this cell, we will create stack view and add views in it and put stack view in ContainerView then add container in scrollView, you can replace stack with any views, it is just for simplified but the main Component is scrollView and container view

2.2- TableView Cell

In this cell, we just create tableView and add constraints [leading, top, trailing, bottom]

Now we need to register these cells in collectionView and set delegate and dataSource for tableView to self, and add the extension in viewController to implement functions for tableView. then make cell size equal width and height of collectionView.
So, ViewController will look like this

Don’t forget to implement UICollectionViewDelegateFlowLayout to add function sizeForItemAt.

The result will look like this.

add variables in ViewController to determine max and min hight for header

let headerViewMaxHeight : CGFloat = 250
let headerViewMinHeight : CGFloat = 50

Now inside function cellForItemAt set delegate for scrollView in CellScrollView

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellScroll", for: indexPath) as! CellScrollView
cell.scrollView.delegate = self
return
cell

We need to implement UIScrollViewDelegate in viewController, so we Will add an extension for ViewController and implement function scrollViewDidScroll and add this code inside it

extension ViewController : UIScrollViewDelegate {func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y: CGFloat = scrollView.contentOffset.y
guard let headerViewHeightConstraint = heightView else {return}
let newHeaderViewHeight: CGFloat =
headerViewHeightConstraint.constant - y
if newHeaderViewHeight > headerViewMaxHeight {
headerViewHeightConstraint.constant = headerViewMaxHeight
} else if newHeaderViewHeight <= headerViewMinHeight {
headerViewHeightConstraint.constant = headerViewMinHeight
} else {
headerViewHeightConstraint.constant = newHeaderViewHeight
scrollView.contentOffset.y = 0 // block scroll view
}
}
}

Note:
scrollViewDidScroll is called also in the delegate of tableView because tableView is inherited from scrollView

If you run the app you can see this behavior.

Oops

cells did not resize for collectionView, really I don’t know why this behavior appears but this happens because of flow layout and changing the height of the CollectionView, and we will solve this problem by creating “custom collectionView”

We will create a dictionary with the key is indexPath and value is LayoutAttribute, so in “prepare” function will get every indexPath in all sections, then add it as Key and value is layout attribute by function
layoutAttributesForItem(at indexPath: IndexPath)

In function layoutAttributesForElements We will set itemSize is the frame of collectionView to change the frame for the cell when size collectionView is changing and filter dictionary to get the intersection of frames in attributes with rect in parameter

Add custom FlowLayout to collectionView

And add this code In viewDidLoad

if let l = collectionView.collectionViewLayout as? ExpandableFlowLayout {
l.scrollDirection = .horizontal
}

If you run the app you should see a smooth scroll, like this

In the next part, we will see some cases with bugs and how to solve them.

link for part two

Thanks for Reading, Tap the 👏 button if you found this article useful!

Sources code

Follow Me :
Twitter
Linkedin

--

--