Last week the new Path 2 app was released with a bunch of great UI tweaks, which I promise we will definitely see in other apps really soon.
One of the most impressive one to me was the info panel attached to the scroll view indicator which appears as you begin scrolling and disappears after the scroll view stops.

In contrast to the existing table view sections the info panel approach is non-persistent. It allows you to temporarily group data in a table view. Therefore it is not qualified to simply replace every table view section in your app, but it could be a good alternative or even an addition in some cases. So let’s dive into one possible solution I’ve figured out how to create an attached info panel.
A UIScrollViewIndicator is the top most subview of a UIScrollView or of one of it’s subclasses, e.g. a UITableView. The indicator itself is simply an UIImageView. Unless you are not scrolling it’s alpha is 0 and therefore not visible - but it’s there right from the beginning. When the user scrolls to the top or bottom and drags it’s finger a little further – like a rubber band - the size of the indicator shrinks until it finally became a dot.
The basic idea is to add the info panel’s root layer as a sublayer to the indicator’s root layer. With that, the info panel scrolls smoothly and stays in parallel to the indicator automatically. In the case were the scroll view hit the edges, we want the info panel to stay visible at the top or bottom of the screen, so we need to do some extra calculation to let it stay centered.
Here we just need four ivars which are quite self-explanatory:
#import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #import <QuartzCore/QuartzCore.h> @interface FMInfoPanelViewController : UIViewController{ UIScrollView *scrollView; UIView *infoPanel; CGSize initialSizeOfInfoPanel; CGFloat initialHeightOfScrollIndicator; } @end
In our implementation we first create an UIImageView which simply acts as a background of our info panel. After that we create the info panel itself with a negative x-origin so it will appear left to the scroll indicator.
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad
{
CGRect viewFrame = [[self view] frame];
scrollView = [[UIScrollView alloc] initWithFrame:viewFrame];
[scrollView setDelegate:self];
[scrollView setContentSize:CGSizeMake(viewFrame.size.width, viewFrame.size.height * 4)];
[[self view] addSubview:scrollView];
UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"infoPanel.png"]];
initialSizeOfInfoPanel = [backgroundImage frame].size;
infoPanel = [[UIView alloc] initWithFrame:CGRectMake(initialSizeOfInfoPanel.width * (-1), 0, initialSizeOfInfoPanel.width, initialSizeOfInfoPanel.height)];
[infoPanel addSubview:backgroundImage];
[backgroundImage release], backgroundImage = nil;
}
scrollViewWillBeginDragging: When the user starts scrolling we check whether the info panel is currently visible or not. If not we will add it’s root layer as as sublayer of the scroll indicators root layer. One important thing I’ve figured out is to set the background color of our info panels layer to the same as the scroll indicator’s root layer. Otherwise the scroll indicator wont be translucent anymore.
#pragma mark -
#pragma mark Scroll view delegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)aScrollView
{
if (![infoPanel superview])
{
UIView *indicator = [[aScrollView subviews] lastObject];
CGRect indicatorFrame = [indicator frame];
initialHeightOfScrollIndicator = indicatorFrame.size.height;
[[infoPanel layer] setBackgroundColor:[[indicator layer] backgroundColor]];
[[indicator layer] addSublayer:[infoPanel layer]];
// Center the info panel
CGRect infoPanelFrame = [infoPanel frame];
infoPanelFrame.size = initialSizeOfInfoPanel;
infoPanelFrame.origin.y = indicatorFrame.size.height / 2 - infoPanelFrame.size.height / 2;
[infoPanel setFrame:CGRectIntegral(infoPanelFrame)];
}
}
scrollViewDidScroll: As our info panel is already attached to the scroll indicator we can avoid a lot of unnecessary calculations when the scroll view just scrolls within it’s content area. Though we have to do some work when we reach the top or bottom of the screen:
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
UIView *indicator = [[aScrollView subviews] lastObject];
CGRect indicatorFrame = [indicator frame];
// We are somewhere at the edge (top or bottom)
if (indicatorFrame.size.height < initialHeightOfScrollIndicator)
{
CGRect infoPanelFrame = [infoPanel frame];
// The indicator starts shrinking, so we need to adjust our info panel's y-origin to stays centered
if (indicatorFrame.size.height > infoPanelFrame.size.height + 2)
{
infoPanelFrame.origin.y = (indicatorFrame.size.height / 2) - (infoPanelFrame.size.height / 2);
}
// We are at the bottom of the screen and the indicator is now smaller than our info panel
else if (indicatorFrame.origin.y > 0)
{
infoPanelFrame.origin.y = (infoPanelFrame.size.height - indicatorFrame.size.height) * (-1);
}
[infoPanel setFrame:infoPanelFrame];
}
}
scrollViewDidEndScrollingAnimation: As we are good citizens we will remove our info panel when the scroll view stops scrolling.
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[infoPanel removeFromSuperview];
}
The Path 2 app has some nice additional animations to fade in and fade out the info panel. It also stays visible for a second after the scroll view has stopped scrolling so the user knows if he scrolled to the right position.
I’ve created a public github gist and Xcode sample project so you can download the source files.
I hope this solution gives you an idea of how to implement this great UI approach to your app to increase the user experience. If you have any suggestions on how to solve it better, more easily or in a completely different way, please contact me @FlorianMielke.
Take care,
Florian