First let me respond directly the to the three main points here. The request was for the following points,

1) Using Storyboard primarily to create separate ViewControllers
2) Swiping backwards and forwards between the ViewControllers to navigate.
3) Double Tapping to show a contents menu with thumbnails of each ViewController

Let me address each point one at a time. For this tutorial using the Storyboard to create separate ViewControllers was not entirely appropriate giving the simplicity of the views however please check out XXX tutorial which will show you how to do it.

With regard to swiping back and forth to achieve the carousel effect, this can be achieved by simply using a UIScrollView. I have created a custom component here called SSPageView that should take care of all of this for you and can be reused in your projects commercial or otherwise with a little accreditation. However should you require more on UIGestureRecognisers and how to use them Storyboard then check out this tutorial on gestures.

I use the final point to briefly illustrate how to use animation blocks to achieve the zooming in and zooming out of a given article.

I have changed my API conventions slightly having read this article by Matt Gemmell on API design. It is a great article on how to design a good API and the change I have made is to follow Rule 20: Put distinguishing params first in query methods. I will also be using this in the future to create better APIs and hopefully easier code for you to use.


With the background to the problem now defined lets take our first look at the SSPageView header. There are several things to notice here, some of which are very basic and others that are core to the UI widget itself.

@property (weak ,nonatomic) IBOutlet id<SSPagingViewDelegate> delegate;
@property (weak ,nonatomic) IBOutlet id<SSPagingViewDataSource> dataSource;
@property (strong, nonatomic) UIScrollView *scrollView;
@property int centeredIndex;
- (void)reloadData;
- (SSPage *)dequeueRecycledPage;

1) First notice there are no iVars only properties. Using ARC there is no need to create iVars to hold onto your variables instead properties create these for you with the appropriate retain count.

2) Secondly, using ARC properties are now defined as weak or strong not assign and retain. Using weak over assign will assign nil to the pointer stopping your app from crashing if you try to send a message to it unlike assign. Some also use assign for base types, however as you can see in the header this is not necessary.

3) You should always create weak properties for you IBOutlets see this for more details.

OK! So that is the basics over with lets get to the API of the wdiget itself. You will notice that the header defines both a delegate and data source to delegate actions else where. I have kept both simple here, the data source is required to provide the total number of pages to be shown and the page to be displayed at a given index.

@protocol SSPagingViewDataSource <NSObject>
    - (SSPage *)pageAtIndex:(NSInteger)index inPagingView:(SSPagingView *)fipvc;
    - (NSInteger)numberOfPagesInPagingView:(SSPagingView *)fipvc;

The delegate is required to specify the width of each page with the height of a page being specified by the height of the control. Over time should this develop further of if anyone would like to add to the git repository this may be somthing that is added to support different sized pages in the view. The delgate can optionally choose to implement page changed should a parent wish to update another UI component when this happens such as a UIPageControl. Finally the is a method to determine when the next item has snapped into place, this could possibly be merged with the page changed method, however I will think about this in the future and again I am more than happy to accept changes to the repository should anyone want to get involved.

@protocol SSPagingViewDelegate <NSObject>
    - (CGFloat)widthOfPagingView:(SSPagingView *)fipvc;
    - (void)pagingViewPageChanged:(SSPagingView *)fipvc;
    - (void)pagingView:(SSPagingView *)fipvc didSnapToPageAtIndex:(NSInteger)index;

Using SSPageView in the SSMagazine example.

Drag on a UIView onto the storyboard and in the identity inspector set its class to SSPagingView. Alternatively you can instanciate a SSPagingView in your code and add it as a subview however, I prefer to layout my UI elements on the scene so I can see how they all interact with one another when they are on screen.

Set the SSViewController as both the delegate and data source for the SSPagingView and implement their methods.

- (CGFloat)widthOfPagingView:(SSPagingView *)fipvc{
    return 324;
- (NSInteger)numberOfPagesInPagingView:(SSPagingView *)fipvc{
    return 3;
- (SSPage *)pageAtIndex:(NSInteger)aIndex inPagingView:(SSPagingView *)fipvc{
    ImagePage *page = (ImagePage *)[fipvc dequeueRecycledPage];
    if (page == nil){
        page = [[ImagePage alloc] initWithFrame:self.pageView.frame];
        UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pageDoubleTapped:)];
        [doubleTap setNumberOfTapsRequired:2];
        [page addGestureRecognizer:doubleTap];
    page.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"article%d_thumb.png",aIndex+1]];    
    return page;
- (void)pagingView:(SSPagingView *)fipvc didSnapToPageAtIndex:(NSInteger)index{
        [self showDetailedViewForPageAtIndex:index];
    self.textLabel.text = [ objectAtIndex:index];
    self.showDetailedViewAfterSnapToGrid = NO;

In the pageAtIndex:inPagingView: method I try to dequeue a recycled page, a page which has been created but is no longer in use. If theire is no page available then I construct a new page. In this version the size of the cache is defined in the SSPagingView however, again this should be calculated based on the number of pages that can fit on screen. In the SSMagazine I have extended the SSPage class to create a ImagePage that will simply create a page with an UIImageView that fills the page (minus a small margin). When a new ImagePage is created I create and attach a UITapGestureRecogniser that responds to double taps and fires the pageDoubleTapped: method. The page doubled tapped method handles the case where the item tapped is not the item selected to ensure that the SSPageView scrolls to the correct item before expanding that article. The showDetailedViewForPageAtIndex: starts the animation to expand the view, below you can see how blocks allow you to perform actions when the given call has finished ececuting, in this case the animation. By setting the size of the frame in the animation the UIView scales up and anything that must happen on completion such as a reload of data can be performed in the completion section.

[UIView animateWithDuration:0.5 
        self.detailedView.frame = CGRectMake(0,0, 1024, 748);
    completion:^(BOOL finished){

To complete the UI drag on a UILabel to display the meta data about an item when selected and a pointer image to show the connection between the selected item and the meta data. Hook the label up to an IBOutlet so that you can update it when a new page is snapped into position.

I hope you find this useful and as always would appreciate all of your feedback.