I want to detect when the UITableView
section header snaps on top of the screen and then modify header's height, but I have no idea how to do that. Can anyone help?
I was able to accomplish detecting which header is stuck to top of the table view by using the didEndDisplayingHeaderView
and willDisplayHeaderView
delegate methods. The idea here is that the while we are scrolling through the sections of the table view, the rows around the header are becoming visible and we can grab the index paths of all visible rows using tableView.indexPathsForVisibleRows
. Depending on if we are scrolling up or down, we compare the first or last indexPath on screen to the index path of the view that just appeared/disappeared.
//pushing up
func tableView(_ tableView: UITableView,
didEndDisplayingHeaderView view: UIView,
forSection section: Int) {
//lets ensure there are visible rows. Safety first!
guard let pathsForVisibleRows = tableView.indexPathsForVisibleRows,
let lastPath = pathsForVisibleRows.last else { return }
//compare the section for the header that just disappeared to the section
//for the bottom-most cell in the table view
if lastPath.section >= section {
print("the next header is stuck to the top")
}
}
//pulling down
func tableView(_ tableView: UITableView,
willDisplayHeaderView view: UIView,
forSection section: Int) {
//lets ensure there are visible rows. Safety first!
guard let pathsForVisibleRows = tableView.indexPathsForVisibleRows,
let firstPath = pathsForVisibleRows.first else { return }
//compare the section for the header that just appeared to the section
//for the top-most cell in the table view
if firstPath.section == section {
print("the previous header is stuck to the top")
}
}
I went to make my comment into codes and it works like:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == yourTableView {
if yourTableView.rectForHeader(inSection: <#sectionIWant#>).origin.y <= tableView.contentOffset.y-defaultOffSet.y &&
yourTableView.rectForHeader(inSection: <#sectionIWant#>+1).origin.y > tableView.contentOffset.y-defaultOffSet.y {
print("Section Header I want is at top")
}
} else {
//Handle other scrollViews here
}
}
So you replace sectionIWant
with an Int
and when that section's header is at top, the print
will run.Note that yourTableView
is not a parameter of scrollViewDidScroll
so you have to keep a reference to your UITableView
elsewhere and use it here.
Alternatively if you want to be constantly updated on which section header is at top you can use:
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == yourTableView {
var currentSection = 0
while !(tableView.rectForHeader(inSection: currentSection).origin.y <= (tableView.contentOffset.y-defaultOffSet.y) &&
tableView.rectForHeader(inSection: currentSection+1).origin.y > (tableView.contentOffset.y)-defaultOffSet.y) {
if tableView.contentOffset.y <= tableView.rectForHeader(inSection: 0).origin.y {
break //Handle tableView header - if any
}
currentSection+=1
if currentSection > tableView.numberOfSections-2 {
break //Handle last section
}
}
print(currentSection)
}
}
Note that in both codes there is a defaultOffSet
which is the contentOffSet
of the tableView
on load, this is to take into account that the contentOffSet
does not start at 0 in some situations - I am not sure when but I do encounter such cases before.
guard let tableView = scrollView as? UITableView else { return }
–
Rambo This solution works for me.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.visibleHeaders.forEach { header in
let headerOriginalFrame = self.myTableView.rectForHeader(inSection: header.tag)
let headerCurrentFrame = header.frame
let fixed = headerOriginalFrame != headerCurrentFrame
// Do things you want
}
}
Here is full source code. https://github.com/jongwonwoo/CodeSamples/blob/master/TableView/TableviewFixedHeader/Tableview/ViewController.swift
My solution to this problem turned out to be really working and versatile enough. I would be very glad if this helps someone
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let sectionPosition = tableView.rect(forSection: sectionIndex)
let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
if let dataSource = dataSource,
scrollView.contentOffset.y >= sectionPosition.minY {
//Next section
guard dataSource.count - 1 > sectionIndex else { return }
sectionIndex += 1
} else if translation.y > 0,
scrollView.contentOffset.y <= sectionPosition.minY {
//Last section
guard sectionIndex > 0 else { return }
sectionIndex -= 1
}
Late to the party, but reviewing up all responses didn't solve my issue.
The following approach, borrow a little bit from each one, adding at the same time, a more general and not fixed approach.
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let visibleIndexs = self.tableView.indexPathsForVisibleRows,
!visibleIndexs.isEmpty else { return }
var visibleHeaders = [Int]()
visibleIndexs.forEach { anIndexPath in
if !visibleHeaders.contains(anIndexPath.section) {
visibleHeaders.append(anIndexPath.section)
}
}
visibleHeaders.forEach { header in
let headerView = tableView.headerView(forSection: header)
let headerOriginalFrame = self.tableView.rectForHeader(inSection: header)
if headerOriginalFrame != headerView?.frame {
//do something with top headerView
} else {
//do something else with all the others headerView(s)
}
}
}
© 2022 - 2024 — McMap. All rights reserved.
frame.x == 0
? – BalcerrectForHeader
method which you can use to get the frame of the header for that section, then there is acontentOffset
property ofUItableView
. I believe you can calculate to find out if the header is at top of screen. There is alsoUIScrollViewdelegate
methodscrollViewDidScroll
which you can use to check when theUITableView
is scrolled – Balcer