How to Add Headerview in Uicollectionview Like Uitableview's Tableheaderview

How to add HeaderView in UICollectionView like UITableView's tableHeaderView

I've put a view at the top of a collection view by just adding a UIView as a subview of the collection view. It does scroll up with the collection view and it has normal UIView user interaction. This works ok if you have no section header, but doesn't work if you do. In that situation, I don't see anything wrong with the way you're doing it. I'm not sure why you're not detecting touches, they work fine for me.

UICollectionView header view like table view header (not the section header)

I have the solution now. Add a subview in the collectionView and make the collectionView contentInset below the topImageView like below.

    topImageView.frame = CGRect(x: 5*SCREEN_SCALE, y: -125*SCREEN_SCALE, width: 285*SCREEN_SCALE, height: 120*SCREEN_SCALE)
collectionView.addSubview(topImageView)
collectionView.contentInset = UIEdgeInsets(top: 130*SCREEN_SCALE, left: 0, bottom: 0, right: 0)

UICollectionView header view like table view header

Eventually I put UICollectionView in UITableViewCell and added some code to calculate cell height.

ios adding a collectionView inside UITableView's header view

Define the delegates of UITableView & UICollectionView in same controller, set there delegates to the same class as

self.mytableview.delegate = self;
self.mycollectionview.delegate = self;

You can follow this tutorial, Putting a UICollectionView in a UITableViewCell

Add CollectionView as Table View Header

you use the NIB, so you should use func register(UINib?, forCellWithReuseIdentifier: String) instead of func register(AnyClass?, forCellWithReuseIdentifier: String)

tableHeaderView is overlapping cells when adding custom view to subview of container view

The reason your "blue view" is overlapping the cells is because you are constraining its Top to the red view's Bottom, but you're not updating the header view size.

One good approach is to create a UIView subclass to use as your header view. Setup all of its content with proper auto-layout constraints.

Then, in the controller's viewDidLayoutSubviews(), we use .systemLayoutSizeFitting(...) to determine the header view's height and update its frame:

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

// update table header size

guard let headerView = tableView.tableHeaderView else { return }

let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height

var frame = headerView.frame

// avoids infinite loop!
if height != frame.height {
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}

Here is a complete example...

First, our custom view class:

class SampleHeaderView: UIView {

let redView: UIView = {
let v = UIView()
v.backgroundColor = .systemRed
return v
}()
let blueView: UIView = {
let v = UIView()
v.backgroundColor = .systemBlue
return v
}()
let redTopLabel: UILabel = {
let v = UILabel()
v.backgroundColor = .yellow
v.numberOfLines = 0
return v
}()
let redBottomLabel: UILabel = {
let v = UILabel()
v.backgroundColor = .green
v.numberOfLines = 0
return v
}()
let multiLineLabel: UILabel = {
let v = UILabel()
v.backgroundColor = .cyan
v.numberOfLines = 0
return v
}()

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}

func commonInit() -> Void {

// all views will use auto-layout
[redView, blueView, redTopLabel, redBottomLabel, multiLineLabel].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
}

// prevent label vertical compression
[redTopLabel, redBottomLabel, multiLineLabel].forEach { v in
v.setContentCompressionResistancePriority(.required, for: .vertical)
}

// add top and bottom labels to red view
redView.addSubview(redTopLabel)
redView.addSubview(redBottomLabel)

// add multi-line label to blue view
blueView.addSubview(multiLineLabel)

// add red and blue views to self
addSubview(redView)
addSubview(blueView)

// the following constraints need to have less-than required to avoid
// auto-layout warnings

// blue view bottom to self
let c1 = blueView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0)

// labels trailing contraints
let c2 = redTopLabel.trailingAnchor.constraint(equalTo: redView.trailingAnchor, constant: -8.0)
let c3 = redBottomLabel.trailingAnchor.constraint(equalTo: redView.trailingAnchor, constant: -8.0)
let c4 = multiLineLabel.trailingAnchor.constraint(equalTo: blueView.trailingAnchor, constant: -8.0)

[c1, c2, c3, c4].forEach { c in
c.priority = .required - 1
}

NSLayoutConstraint.activate([

// red view top to self
redView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),

// leading / trailing to self
redView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
redView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

// blue view top to red view bottom
blueView.topAnchor.constraint(equalTo: redView.bottomAnchor, constant: 0.0),

// leading / trailing to self
blueView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
blueView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),

// top and bottom labels, constrained in red view
// with a little "padding"
redTopLabel.topAnchor.constraint(equalTo: redView.topAnchor, constant: 8.0),
redTopLabel.leadingAnchor.constraint(equalTo: redView.leadingAnchor, constant: 8.0),

redBottomLabel.topAnchor.constraint(equalTo: redTopLabel.bottomAnchor, constant: 8.0),
redBottomLabel.leadingAnchor.constraint(equalTo: redView.leadingAnchor, constant: 8.0),

redBottomLabel.bottomAnchor.constraint(equalTo: redView.bottomAnchor, constant: -8.0),

// multi-line label, constrained in blue view
// with a little "padding"
multiLineLabel.topAnchor.constraint(equalTo: blueView.topAnchor, constant: 8.0),
multiLineLabel.leadingAnchor.constraint(equalTo: blueView.leadingAnchor, constant: 8.0),
multiLineLabel.bottomAnchor.constraint(equalTo: blueView.bottomAnchor, constant: -8.0),

// the less-than-required priority constraints
c1, c2, c3, c4,

])

}
}

and a sample controller:

class TableHeaderViewController: UIViewController {

var sampleHeaderView = SampleHeaderView()

private(set) lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return tableView
}()

override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)

NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])

sampleHeaderView.redTopLabel.text = "The Red Top Label"
sampleHeaderView.redBottomLabel.text = "The Red Bottom Label, with enough text that is should wrap."
sampleHeaderView.multiLineLabel.text = "This text is for the Label in the Blue View. It is also long enough that it will require word-wrapping. Note that the header updates itself when the frame changes, such as on device rotation."
tableView.tableHeaderView = sampleHeaderView

}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

// update table header size

guard let headerView = tableView.tableHeaderView else { return }

let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.frame.width, height: .greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height

var frame = headerView.frame

// avoids infinite loop!
if height != frame.height {
frame.size.height = height
headerView.frame = frame
tableView.tableHeaderView = headerView
}
}

}

extension TableHeaderViewController: UITableViewDataSource, UITableViewDelegate {

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}

}

Output:

Sample Image

and rotated:

Sample Image

UICollectionView Header and Footer View

Try it...

First register class in ViewDidLoad

registerClass(myFooterViewClass, forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: "Footer")

then use this method

override func collectionView(collectionView: UICollectionView,   viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {

switch kind {

case UICollectionElementKindSectionHeader:

let header = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Header", forIndexPath: indexPath) as! UICollectionReusableView

header = SomeView
return header

case UICollectionElementKindSectionFooter:
let footer = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Footer", forIndexPath: indexPath) as! UICollectionReusableView

footer= SomeView
return footer

default:

print("anything")
}
}

I hope it help...



Related Topics



Leave a reply



Submit