I have a UIView
with a UITableView
below it:
You can make it like this:
Add these as your class variables:
private let NUMBER_OF_ROWS: Int = 256
private let ROW_HEIGHT: CGFloat = 75.0
private let MINIMUM_CONSTANT_VALUE: CGFloat = -150.0 /// This is the hidable view's height
@IBOutlet weak var hidableViewTopConstraint: NSLayoutConstraint! /// This is an outlet from your storyboard
private var lastContentOffset: CGFloat = 0.0
This goes on your viewDidLoad()
:
tableView.delegate = self
And then you add this as an extension for your view controller:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let delta = tableView.contentOffset.y - lastContentOffset
let canScrollUp: Bool =
delta < 0 &&
hidableViewTopConstraint.constant < 0 &&
scrollView.contentOffset.y < 0
let canScrollDown: Bool =
delta > 0 &&
hidableViewTopConstraint.constant > MINIMUM_CONSTANT_VALUE &&
tableView.contentOffset.y > 0
if canScrollUp || canScrollDown{
hidableViewTopConstraint.constant -= delta
tableView.contentOffset.y -= delta
}
lastContentOffset = tableView.contentOffset.y
}
}
But note that the scroll mechanism will only work when the TableView is scrolled. The top view wont collapse or expand if you scroll it.
I made an example project you can check, it is called iOSFixedHeaderList
Someone asked for the code for my solution so I am posting it here as an answer. The credit for the idea should still go to NobodyNada.
In my UITableViewController
I implement this delegate method:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[[NSNotificationCenter defaultCenter] postNotificationName:@"TableViewScrolled" object:nil userInfo:scrollUserInfo];
}
scrollUserInfo
is a NSDictionary
where I put my UITableView
to pass it with the notification (I do this in viewDidLoad
so I only have to do it once):
scrollUserInfo = [NSDictionary dictionaryWithObject:self.tableView forKey:@"scrollView"];
Now, in the view controller that has the view I want to move off screen while scrolling I do this in viewDidLoad
:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleScroll:) name:@"TableViewScrolled" object:nil];
And finally I have the method:
- (void)handleScroll:(NSNotification *)notification {
UIScrollView *scrollView = [notification.userInfo valueForKey:@"scrollView"];
CGFloat currentOffset = scrollView.contentOffset.y;
CGFloat height = scrollView.frame.size.height;
CGFloat distanceFromBottom = scrollView.contentSize.height - currentOffset;
if (previousOffset < currentOffset && distanceFromBottom > height) {
if (currentOffset > viewHeight)
currentOffset = viewHeight;
self.topVerticalConstraint.constant += previousOffset - currentOffset;
previousOffset = currentOffset;
}
else {
if (previousOffset > currentOffset) {
if (currentOffset < 0)
currentOffset = 0;
self.topVerticalConstraint.constant += previousOffset - currentOffset;
previousOffset = currentOffset;
}
}
}
previousOffset
is an instance variable CGFloat previousOffset;
.
topVerticalConstraint
is a NSLayoutConstraint
that is set as a IBOutlet
. It goes from the top of the view to the top of its superview and the initial value is 0.
It's not perfect. For instance, if the user scrolls very aggressively up the movement of the view can get a bit jerky. The issue is worse for large views; if the view is small enough there is no problem.
Solution for Swift (Works perfectly with bounce enabled for scroll view):
var oldContentOffset = CGPointZero
let topConstraintRange = (CGFloat(120)..<CGFloat(300))
func scrollViewDidScroll(scrollView: UIScrollView) {
let delta = scrollView.contentOffset.y - oldContentOffset.y
//we compress the top view
if delta > 0 && topConstraint.constant > topConstraintRange.start && scrollView.contentOffset.y > 0 {
topConstraint.constant -= delta
scrollView.contentOffset.y -= delta
}
//we expand the top view
if delta < 0 && topConstraint.constant < topConstraintRange.end && scrollView.contentOffset.y < 0{
topConstraint.constant -= delta
scrollView.contentOffset.y -= delta
}
oldContentOffset = scrollView.contentOffset
}