Im attempting to replicate the resizing behaviour of a screen seen in the iOS app Rdio. The screen in question provides an overview of a selected album and contains a UIView on the top half and a UITableView on the bottom half. When the tableView is scrolled, it first resizes upwards to fill the screen, then begins to scroll through its content normally once the maximum height is reached.
After some searching I found this question: Dragging UITableView which is basically asking for the same thing, however its accepted method is the same as my initial thoughts & trial, which was to use a UIPanGestureRecognizer and resize the tableviews height according to the translation of the pan.
This does not provide the behaviour i'm looking for. Using this method only allows you to statically drag the tableviews height up or down and it has the added issue of the panGesture overriding that of the tableViews which then prevents scrolling through the content.
The resizing behaviour of the Rdio app functions and feels exactly like a UIScrollView, it has inertia. You can drag it all the way, flick it up or down, and it smoothly resizes. When the tableView has reached its full-size or original half-size, the remaining inertia is seemingly passed on the tableview causing the cells to scroll as they normally would for that amount. I know they must be manipulating UIScrollViews, I just can't figure out how.
As a final note, eventually I will be using AutoLayout on this screen so i'm wondering how that will potentially hinder or help this situation as well.
Update
This approach has gotten me closest to the behaviour i'm looking for so far. Flicking the tableView upwards behaves exactly like I wanted it to (resize with inertia & continue scrolling when max height is reached), although with less sensitivity than i'd like. Flicking downwards however, provides no inertia and instantly stops.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect scrollViewFrame = scrollView.frame;
CGFloat scrollViewYOffset = scrollView.contentOffset.y;
if (scrollViewFrame.origin.y <= kTableViewMaxYOrigin || scrollViewFrame.origin.y <= _originalTableViewFrame.origin.y)
{
scrollViewFrame.origin.y -= scrollViewYOffset;
if(scrollViewFrame.origin.y >= kTableViewMaxYOrigin && scrollViewFrame.origin.y <= _originalTableViewFrame.origin.y)
{
scrollViewFrame.size.height += scrollViewYOffset;
scrollView.frame = scrollViewFrame;
scrollView.contentOffset = CGPointZero;
}
}
}
I made a version that uses autolayout instead. It took me a while of trial and error to get this one right!
Please use the comments and ask me if the answer is unclear.
In viewDidLoad
save the initial height of your layout constraint determining the lowest down you want the scrollview to be.
- (void)viewDidLoad
{
...
_initialTopLayoutConstraintHeight = self.topLayoutConstraint.constant;
...
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
BOOL leaveScrollAlone = self.topLayoutConstraint.constant == _initialTopLayoutConstraintHeight && scrollView.contentOffset.y <= 0;
if (leaveScrollAlone)
{
// This allows for bounce when swiping your finger downwards and reaching the stopping point
return;
}
// Do some capping of that layout constraint to keep it from going past the range you want it to be.
// In this case, I use self.topLayoutGuide.length so that my UICollectionView scales all the way until
// it hits the bottom of the navigation bar
CGFloat topLayoutConstraintLength = _initialTopLayoutConstraintHeight - scrollView.contentInset.top;
topLayoutConstraintLength = MAX(topLayoutConstraintLength, self.topLayoutGuide.length);
topLayoutConstraintLength = MIN(topLayoutConstraintLength, _initialTopLayoutConstraintHeight);
self.topLayoutConstraint.constant = topLayoutConstraintLength;
// Keep content seemingly still while the UICollectionView resizes
if (topLayoutConstraintLength > self.topLayoutGuide.length)
{
scrollView.contentInset = UIEdgeInsetsMake(scrollView.contentInset.top + scrollView.contentOffset.y,
scrollView.contentInset.left,
scrollView.contentInset.bottom,
scrollView.contentInset.right);
scrollView.contentOffset = CGPointZero;
}
// This helps get rid of the extraneous contentInset.top we accumulated for keeping
// the content static while the UICollectionView resizes
if (scrollView.contentOffset.y < 0)
{
self.topLayoutConstraint.constant -= scrollView.contentOffset.y;
scrollView.contentInset = UIEdgeInsetsMake(scrollView.contentInset.top + scrollView.contentOffset.y,
scrollView.contentInset.left,
scrollView.contentInset.bottom,
scrollView.contentInset.right);
}
// Prevents strange jittery artifacts
[self.view layoutIfNeeded];
}
It turns out the key component to getting the smooth inertial resizing in both directions was to update the scrollViews contentInset.top by its contentOffset.y.
I believe this makes sense in retrospect as if the content within is already at the top it cannot scroll anymore, hence the sudden stop rather than smooth scroll. At least thats my understanding.
Another key point was to make sure the cells only started scrolling once maximum or original height was achieved. This was done simply by setting the scrollViews contentOffset to CGPointZero each time the view resized until maximum or original height was reached.
Here is the - (void)scrollViewDidScroll:(UIScrollView *)scrollView
method demonstrating how to achieve this effect.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect scrollViewFrame = scrollView.frame;
CGFloat scrollViewTopContentInset = scrollView.contentInset.top;
CGFloat scrollViewYOffset = scrollView.contentOffset.y;
if (scrollViewFrame.origin.y <= kTableViewMaxYOrigin || scrollViewFrame.origin.y <= _originalTableViewFrame.origin.y)
{
scrollViewFrame.origin.y -= scrollViewYOffset;
if(scrollViewFrame.origin.y >= kTableViewMaxYOrigin && scrollViewFrame.origin.y <= _originalTableViewFrame.origin.y)
{
scrollViewFrame.size.height += scrollViewYOffset;
scrollViewTopContentInset += scrollViewYOffset;
scrollView.frame = scrollViewFrame;
scrollView.contentInset = UIEdgeInsetsMake(scrollViewTopContentInset, 0, 0, 0);
scrollView.contentOffset = CGPointZero;
}
}
}
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