I'm trying to accomplish a scrolling movement like the iOS weather app. The following gif shows what I achieved so far.
I put a header view on the top of the main view and changed its height depending on a scroll view positioned on the main view. I then added a constraint from the top of the scroll view to the top of the main view by the header view height constant. This is the code
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let delta = scrollView.contentOffset.y - previousContentOffset
if delta > 0 && headerView.frame.height > collapsedHeaderHeight && scrollView.contentOffset.y > 0 {
direction = .up
headerView.frame.size.height = max(collapsedHeaderHeight, headerView.frame.height - delta)
scrollView.contentOffset.y -= delta
}
if delta < 0 && headerView.frame.height < expandedHeaderHeight && scrollView.contentOffset.y < 0 {
direction = .down
headerView.frame.size.height = min(headerView.frame.height - delta, expandedHeaderHeight)
scrollView.contentOffset.y -= delta
}
topConstraint.constant = headerView.frame.height
scrollView.frame.origin.y = headerView.frame.maxY
scrollView.contentSize.height = contentView.bounds.height + headerView.bounds.height
headerView.clipsToBounds = true
previousContentOffset = scrollView.contentOffset.y
}
Here I resized the header view height and changed the scroll view content offset to force the scroll view not to scroll until it reaches the bottom of the collapsed header view. To move the scroll view up I changed the origin y
-coordinate and modified the content size. I used the scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
and the scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)
delegate methods to automatically bring the scroll view up or down and prevent the header view to be cut.
private func adjustScrollViewOrigin(animated: Bool) {
func adjust(for height: CGFloat) {
topConstraint.constant = height
headerView.frame.size.height = height
innerScrollView.frame.origin.y = height
}
switch direction {
case .up:
if animated {
UIView.animate(withDuration: 0.25) { adjust(for: self.collapsedHeaderHeight) }
} else { adjust(for: collapsedHeaderHeight) }
case .down:
if animated {
UIView.animate(withDuration: 0.25) { adjust(for: self.expandedHeaderHeight) }
} else { adjust(for: expandedHeaderHeight) }
case .none:
break
}
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
adjustScrollViewInsets(animated: true)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
adjustScrollViewInsets(animated: true)
}
Everything works fine until now. Obviously, this is not gonna work if I try to scroll from the header view. So I embedded the scroll view inside a secondary scroll view that's pinned to the main view bounds. But I can't manage to make the scroll work. Some help will be appreciated.