Native contacts app has interesting effect - when user tries to scroll, the scroll view pushes the top view with avatar and only when top view is in "small mode" it is scrolled up.
I was able to resize view on scrolling, via didScroll
method. But problem is, content offset is also changing while i push the top vied. In native contacts, content offset changes only when top view is in "small mode"
Any suggestions, how did they made this?
Here's a sample project of how you could do it:
https://github.com/lukaswuerzburger/resizeable-tableview-header
You simply have a view controller with a table view and a custom view (resizable header) in it. On scrollViewDidScroll
you change the height constraint.
PS: The transitions between the master and detail view controller are not prefect, but maybe someone can help me with that. The change of navigationBar
's backgroundImage
and shadowImage
doesn't really work animated when popping back to the master view controller.
The bounty answer is good, but it still gives somewhat choppy solution to this problem. If you want exacly like native this is your solution. I have been playing a lot lately with collection views, and have gained much more experience. One of the things that I found is that this problem can easily be solved with custom layout:
class CustomCollectionViewFlowLayout: UICollectionViewFlowLayout {
let minHeightForHeader: CGFloat = 50
let offsetFromTop: CGFloat = 20
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
// get current attributes
let attributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath)
// get first section
if let attributes = attributes, elementKind == UICollectionElementKindSectionHeader && indexPath.section == 0 {
if let collectionView = collectionView {
// now check for content offset
var frame = attributes.frame
let yOffset = collectionView.contentOffset.y
if yOffset >= -offsetFromTop {
let calculatedHeight = frame.size.height - (yOffset + offsetFromTop)
let maxValue = minHeightForHeader > calculatedHeight ? minHeightForHeader : calculatedHeight
frame.size.height = maxValue
attributes.frame = frame
}
else {
frame.origin.y = offsetFromTop + yOffset
attributes.frame = frame
}
}
return attributes
}
// default return
return attributes
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
Just replace your default layout with this custom class and add this line somewhere in your setup method
flowLayout.sectionHeadersPinToVisibleBounds = true
Voila!
EDIT: Just one more optional method for clipping part, when you scroll to certain part, scroll might continue or return:
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
if let collectionView = collectionView {
let yOffset = collectionView.contentOffset.y
if yOffset + offsetFromTop >= maxHeightForHeader / 2 && yOffset + offsetFromTop < maxHeightForHeader && !(velocity.y < 0) || velocity.y > 0{
return CGPoint.init(x: 0, y: maxHeightForHeader - minHeightForHeader - offsetFromTop)
}
if yOffset + offsetFromTop < maxHeightForHeader / 2 && yOffset + offsetFromTop > 0 || velocity.y < 0 {
return CGPoint.init(x: 0, y: -offsetFromTop)
}
}
return proposedContentOffset
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With