shinobiessentials Carousel User Guide

Overview

This document provides an overview of the SEssentialsCarousel control. It describes the features of the control and its associated concepts, together with some step-by-step guides for achieving common tasks.

The SEssentialsCarousel provides an easy way to layout and display views on screen, in any 2D or 3D configuration. The SEssentialsCarousel is a family of components, each providing different layout appearances, such as a Cover Flow, Linear, or Cylindrical layouts.

If you simply want to get up and running, follow the Quick Start Guide - Objective-C, or the Quick Start Guide - Swift. Alternatively, for a more detailed description of how the Carousel works and the features it presents, see ShinobiEssentials Carousel Control Overview. Finally, for guides that tackle specific usage scenarios, look at the ShinobiEssentials Carousel How-to Guides.

The Carousel has a complete set of Xamarin.iOS bindings, allowing you to make use of all of its features from within applications written in C#. In order to get up and running, follow the Quick Start Guide for Xamarin iOS.

Quick Start Guide - Objective-C

Introduction

The following guide will get you up-and-running with the SEssentialsCarousel component as quickly as possible. In this guide, we will introduce you to the key features of the Carousel, including initial project setup and adding items to the Carousel with a datasource. It may help to follow along with our related code sample: CarouselGettingStarted.xcodeproj.

Creating a Carousel

Start up Xcode and create a new project (File / New / Single View Application).

Within your newly created project, add a reference to the ShinobiEssentials framework, and the bundle which contains its resources. Instructions for doing this can be found in the first section of the EssentialsUserGuide.

Firstly, add an instance variable for the Carousel, keeping things simple for now by using the Linear 2D Carousel. Import the header in ViewController.h, and define the variable as follows:

#import <UIKit/UIKit.h>
#import <ShinobiEssentials/ShinobiEssentials.h>

@interface ViewController : UIViewController
{
    SEssentialsCarouselLinear2D *carousel;
}

@end

In ViewController.m, create a new Carousel instance as follows:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    carousel = [[SEssentialsCarouselLinear2D alloc] initWithFrame:self.view.bounds];
    [self.view addSubview:carousel];
}

Using the Datasource

Now that your Carousel has been created, you can start adding some items. The number of items within the Carousel and the items themselves are provided via the datasource. For simplicity we will use the view controller as the datasource.

Start by adopting the SEssentialsCarouselDataSource protocol in ViewController.h:

@interface ViewController : UIViewController < SEssentialsCarouselDataSource >

This protocol has two required methods; numberOfItemsInCarousel:, and carousel:itemAtIndex:. The first method tells the Carousel the total number of items in your datasource, while the second method is called from the Carousel when it needs an item. Most applications will need to keep a reference to the items passed to the Carousel, so we will too.

Firstly, add an items array to the view controller, and populate it with some dummy UIView instances.

@interface ViewController ()
{
    NSMutableArray *items;
}
@end

@implementation ViewController

...

-(void)createViews
{
    items = [[NSMutableArray alloc] init];

    for (int i = 0; i < 20; i++)
    {
        UILabel *item = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
        item.backgroundColor = [UIColor grayColor];
        item.text = [NSString stringWithFormat:@"%d", i];
        item.textAlignment = NSTextAlignmentCenter;
        [items addObject:item];
    }
}
@end

Update viewDidLoad to make use of this newly added method, and set the Carousel dataSource property to the view controller:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self createViews];
    carousel = [[SEssentialsCarouselLinear2D alloc] initWithFrame:_placeholder.bounds];
    carousel.dataSource = self;
    [_placeholder addSubview:carousel];
}

Finally, implement the methods in the SEssentialsCarouselDataSource protocol;

-(int)numberOfItemsInCarousel:(SEssentialsCarousel *)carousel
{
    return [items count];
}

-(UIView *)carousel:(SEssentialsCarousel *)carousel itemAtIndex:(int)index
{
    return [items objectAtIndex:index];
}

At this point, you can run the application, see your items displayed in a row, and pan back and forth through them.

If you got stuck at any point, take a look at our related code sample: CarouselGettingStarted.xcodeproj

Quick Start Guide - Swift

Introduction

The following guide will get you up-and-running with the SEssentialsCarousel component as quickly as possible. In this guide, we will introduce you to the key features of the Carousel, including initial project setup and adding items to the Carousel with a datasource. It may help to follow along with our related code sample: CarouselGettingStartedSwift.xcodeproj.

Setting Up the Project

Start up Xcode and create a new project (File / New / Single View Application)

Within your newly created project, add a reference to the ShinobiEssentials framework, and the bundle which contains its resources. Instructions for doing this can be found in the first section of the EssentialsUserGuide.

Linking to the bridging header file

In order for Xcode to use an Objective-C based framework in Swift, it needs to have a bridging header file. We have included a bridging header file in our framework. To link to it, you must open the build settings for the your new target, and search for the Objective-C Bridging Header setting. You must then provide the path to the ShinobiEssentials-Bridging-Header.h file, which is inside the Headers directory of the ShinobiEssentials.framework.

Setting the Swift project's bridging header file

In the screenshot above, the framework is three directories above the root of the project, hence it is set to

$(SRCROOT)/../../../ShinobiEssentials.framework/Headers/ShinobiEssentials-Bridging-Header.h

Note that this path will vary based on the location of the framework on your file system.

Adding a Carousel

In ViewController.swift, create a new Carousel instance as follows:

    override func viewDidLoad() {
    super.viewDidLoad()

    let carousel = SEssentialsCarouselLinear2D(frame: view.bounds)
    view.addSubview(carousel)
}

Using the Datasource

Now that your Carousel has been created, you can start adding some items. The number of items within the Carousel and the items themselves are provided via the datasource. For simplicity we will use the view controller as the datasource.

Start by adopting the SEssentialsCarouselDataSource protocol in ViewController.swift:

    class ViewController: UIViewController, SEssentialsCarouselDataSource {

This protocol has two required methods; numberOfItemsInCarousel:, and carousel:itemAtIndex:. The first method tells the Carousel the total number of items in your datasource, while the second method is called from the Carousel when it needs an item. Most applications will need to keep a reference to the items passed to the Carousel, so we will too.

Firstly, add a mutable items Array to ViewController.swift as a property. For now, initialise it to be an empty Array.

var items:Array<UILabel> = []

At the top of viewDidLoad(), populate the array with UILables that we will use as the items in the carousel.

override func viewDidLoad() {
    super.viewDidLoad()

    ShinobiEssentials.trialKey = "" // Trial Users - Add your trial key here!

    for index in 1..20 {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        label.backgroundColor = UIColor.grayColor()
        label.textAlignment = NSTextAlignment.Center
        label.text = "\(index)"
        items.append(label)
    }
            ...

Then set the carousel’s dataSource property to be self just after it is created.

    carousel.dataSource = self

Finally, implement the methods in the SEssentialsCarouselDataSource protocol;

func numberOfItemsInCarousel(carousel: SEssentialsCarousel!) -> Int  {
    return items.count
}

func carousel(carousel: SEssentialsCarousel!, itemAtIndex index: Int) -> UIView!  {
    return items[index]
}

At this point, you can run the application, see your items displayed in a row, and pan back and forth through them.

If you got stuck at any point, take a look at our related code sample: CarouselGettingStartedSwift.xcodeproj

Quick Start Guide for Xamarin.iOS

Xamarin.iOS: Introduction

The following guide will get you up-and-running with the SEssentialsCarousel component as quickly as possible. In this guide, we will introduce you to the key features of the Carousel, including initial project setup and adding items to the Carousel via the datasource. It may help to follow along with our related code sample: CarouselGettingStarted.sln.

Xamarin.iOS: Creating a Carousel

Start up Xamarin Studio and create a new project (File / New / Solution), then selecting C# / iOS / Universal / Single View Application as your project type, naming your project ‘CarouselGettingStarted’.

Within your newly created project, add a reference to the ShinobiEssentials.dll. To do this, right click the References folder under your newly create project and choose ‘Edit References…’. Then select ‘.Net Assembly’ and browse to where you unpacked the ShinobiEssentials zip file. The dll can be found within the Xamarin.iOS / lib folder. Be sure to click ‘Add’ after selecting the dll to add it to your project, and close the window.

To check that the dll is correctly referenced, expand the References folder and you should see the ShinobiEssentials.dll listed as a reference.

Firstly, add a member variable to the view controller for the Carousel. We will keep things simple for now by using the Linear 2D Carousel. Open up CarouselGettingStartedViewController.cs, add a using directive for the ShinobiEssentials namespace, and define the member variable as follows:

...
using ShinobiEssentials;

namespace CarouselGettingStarted
{
    public partial class CarouselGettingStartedViewController : UIViewController
    {
        SEssentialsCarousel carousel;

        public CarouselGettingStartedViewController ()
            : base ("CarouselGettingStartedViewController", null)
        {
        }
        ...
    }
}

Further down the view controller, within the generated ViewDidLoad method create an instance of the Carousel and add it to the view:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    carousel = new SEssentialsCarouselLinear2D (View.Bounds);
    View.AddSubview (carousel);
}

At this point you have successfully added the carousel, and if you build and run, it will be rendered on the screen. However, there will be nothing to see until you add some views for the Carousel to render …

Xamarin.iOS: The Datasource

Now that your Carousel has been created, you can start adding some items. The number of items within the Carousel and the items themselves are provided via the datasource. In order to provide this data to the Carousel you must create a new class that inherits from SEssentialsCarouselDataSource.

Right-click the project and select the Add / New File … option, selecting the Empty Class option, naming the class CarouselDataSource. Add an items members variable to this nearly created class and populate it with views as follows:

public class CarouselDataSource : SEssentialsCarouselDataSource
{
    List<UIView> items = new List<UIView> ();

    public CarouselDataSource()
        : base()
    {
        for(int i = 0; i < 20; i++) {
            UILabel item = new UILabel (new RectangleF(0, 0, 100, 100)) {
                BackgroundColor = UIColor.Gray,
                Text = i.ToString (),
                TextAlignment = NSTextAlignment.Center
            };
            items.Add (item);
        }
    }
}

The datasource has two required methods; NumberOfItems, and GetItem. The first method tells the Carousel the total number of items in your datasource, while the second method is called from the Carousel when it needs an item.

Add these required methods to CarouselDataSource as follows:

protected override int NumberOfItems (SEssentialsCarousel carousel)
{
    return items.Count;
}

protected override UIView GetItem(SEssentialsCarousel carousel, int index)
{
    return items[index];
}

Now all that’s left to do is to tell our Carousel to use the datasource. Open up CarouselGettingStartedViewController.cs and update your ViewDidLoad method to set the DataSource property of the Carousel:

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        carousel = new SEssentialsCarouselLinear2D (View.Bounds);
        carousel.DataSource = new CarouselDataSource ();

        View.AddSubview (carousel);
    }

At this point, you can run the application, see your items displayed in a row, and pan back and forth through them.

If you got stuck at any point, take a look at our related code sample: CarouselGettingStarted.sln

shinobiessentials Carousel Control Overview

The SEssentialsCarousel control renders UIView instances in a variety of visual styles, with user interactions to navigate between them.

The SEssentialsCarousel provides an easy way to layout and display views on screen, in any 2D or 3D configuration. A number of different configurations are available, such as the SEssentialsCoverFlow, the SEssentialsCarouselRadial and the SEssentialsCarouselLinear2D. For a more in-depth list of the different types, see the SEssentialsCarousel base class. Alternatively, learn how to create your own configurations, using the How-to guide Carousel).

The Focus Point

All carousels have the concept of a centered item, this is often referred to as the ‘item in focus’, when you swipe or move the carousel it will come to rest and one of the items will be at the ‘focus point’. Within the carousel view you can control where the focus point will lie by setting the focusPointNormalized property. The rate at which the carousel will stop moving is controlled by the frictionCoefficient property and the animation curve controlled by the momentumAnimationCurve property. Carousels can also be configured to ‘wrap’ so that as items move off one side they reappear on the other, with wrapping off the carousel stops scrolling as it reaches either end (see wrapItems property).

The Delegate

The SEssentialsCarousel uses a delegate as per the standard UIKit pattern, to notify your application when the user interacts with the Carousel. In order to react to the user scrolling or tapping the Carousel, you need to supply the Carousel with a class adopting the SEssentialsCarouselDelegate protocol.

The methods on the delegate protocol follow these conventions;

  • will - delegate methods prefixed with ‘will’ are invoked just before the interaction is processed. These are not cancellable. You typically implement a ‘will’ method if you need to capture the state of the Carousel just before a change occurs.
  • did - delegate methods prefixed with ‘did’ are invoked after the interaction has been processed. When the ‘did’ method is invoked the Carousel state will have changed to reflect the change due to the user interaction.

The DataSource

The SEssentialsCarousel has a dataSource property, as per the standard UIKit pattern, which is used to supply the UIView instances that the Carousel hosts. In order to display views in a Carousel, you will need to supply the Carousel with a class adopting the SEssentialsCarouselDataSource protocol.

The datasource has two required methods: carousel:itemAtIndex: and numberOfItemsInCarousel:. The datasource will initially request the total numberOfItemsInCarousel:, then the views for the items which are currently visible will be requested via carousel:itemAtIndex. The SEssentialsCarousel behaves in a virtualized way, so items are added and removed only when required. The SEssentialsCarousel does not retain items which have disappeared off screen, so the datasource must retain all of its views.

Styling

Unlike the other ShinobiEssentials components, the SEssentialsCarousel does not have a theme or style. This is because unlike the other ShinobiEssentials components, the Carousel is completely transparent, so has nothing to style. However styling can still be applied to the items in the datasource before the Carousel requests them. This is done using the SEssentialsDecoratedView classes, to apply effects such as reflection and fading. For example, to give a view a reflection underneath it, the datasource could do the following:

-(UIView*)carousel:(SEssentialsCarousel*)carousel itemAtIndex:(int)index
{
    UIView *baseView = [self getItem:index];
    return [[SEssentialsDecoratedReflectedView alloc] initWithView:baseView];
}

For more on styling with Decorated Views, read the SEssentialsDecoratedView API guide.

Content Offset and Offsets

A number of methods on the Carousel either take or return an offset. This can either be as an argument offset, for an individual item, or a contentOffset for the entire Carousel.

Both offsets are measured in indices; where a difference of +1 means “the next item”, and -1 means “the previous item”.

The contentOffset is the distance in indices between the current Focus Point of the Carousel, and the first item’s center. For example, if the contentOffset of the Carousel was at 2, the Focus Point would be 2 items away from the first item.

The offset of an item is subtly different. The offset of an item is the distance in indices between the item and the Focus Point of the Carousel. For example, as the Carousel is animating between positions, the offsets of each item might be;

The offset allows us to make calculations for items, regardless of how far through the carousel we have scrolled. For example, for the positionOfItemAtOffset:, we may always want the item at offset -1 (i.e. one space left of the focus point) to be 200 points away from the focus point. Likewise, for the delegate call carousel:willDisplayItem:atOffset:, we might want the items at +3 and -3 to have faded out, by modifying their alpha values, as in the Quick Start Guide.

Performance

To maintain a smooth frame-rate when using a carousel there are several principles to consider.

  • Use simple views. If your view items contain many subviews or layers it can slow down the carousel. If the content is not dynamic one option is to cache the view into a UIImageView. Helpfully we supply a SEssentialsDecoratedCachedView that will do just this.
  • Drop shadows are slow. The best solution for this is to use a pre-rendered shadow, again see SEssentialsDecoratedCachedView
  • Reduce the number of visible views. The carousel will attempt to layout maxNumberOfItemsToDisplay items each frame, it is often possible to reduce this number and improve performance.
  • Setting item.layer.shouldRasterize on items in the datasource will improve anti-aliasing but this can lead to a small slowdown. Caching a view with a clear border can produce similar results with no performance costs.

Carousel Designer

Each of the carousel types has a set of properties which are used to configure the appearance of the carousel - e.g. the SEssentialsCarousel base class has properties including frictionCoefficient, wrapItems and snapToFocusPoint. To assist in the configuration of the carousel there is a CarouselDesigner code sample.

The CarouselDesigner shows controls to configure the relevant properties for each carousel type, whilst showing the effects of changes on the carousel itself. This will allow determination of the correct settings without having to create a similar app.

The CarouselDesigner sample code is available with the other code samples in the ShinobiEssentialsSamples workspace. To use it simply compile and run up on either a device or a simulator.

Related sample code: CarouselDesigner.xcodeproj

shinobiessentials Carousel How-to Guides

How to: style an item using the ShinobiEssentials Carousel Delegate

As the Carousel scrolls through its items, it informs the SEssentialsCarouselDelegate. We can use this to apply some extra styling to the Carousel, such as fading out items at the edges, or changing the background color. It may help to follow along with our related code sample: CarouselHowToStyleWithDelegate.xcodeproj.

When setting up the carousel, we need to make sure we set the Delegate before we set the Datasource. This way, we can respond to the first carousel:willLayoutItem:atOffset: and carousel:didLayoutItem:atOffset: calls when the items are first added to the carousel.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    SEssentialsCarousel *carousel = [[SEssentialsCarouselLinear2D alloc] initWithFrame:self.view.bounds];
    carousel.delegate = self;
    carousel.dataSource = self;
    [self.view addSubview:carousel];
}

For our example, we will change the current item in focus to be a different color from the other items;

-(void)carousel:(SEssentialsCarousel *)carousel willLayoutItem:(UIView *)item atOffset:(CGFloat)offset
{
    item.backgroundColor = (round(offset) == 0) ? [UIColor lightGrayColor] : [UIColor darkGrayColor];
}

Alternatively, we could fade out items as they move away from the center;

-(void)carousel:(SEssentialsCarousel *)carousel willLayoutItem:(UIView *)item atOffset:(CGFloat)offset
{
    if (ABS(offset) > 4)
        item.alpha = 0;
    else
        item.alpha = 1 - ABS(offset)/4;
}

To see this in action, take a look at our related code sample: CarouselHowToStyleWithDelegate.xcodeproj

How to: use tap-to-select with a ShinobiEssentials Carousel

When using a Carousel, the user will want to interact with it in many different ways. One option available to them is to tap an item to perform some action. You may find it useful while reading along to reference our related code sample: CarouselHowToTapToSelect.xcodeproj.

The SEssentialsCarouselDelegate offers the carousel:didTapItem:atOffset: callback, to alert the delegate that an item was tapped. Different applications may want to handle this in different ways, but one common way to handle this is to focus on the tapped item. To do this, we use setContentOffset:, or setContentOffset:animated:withDuration.

-(void)carousel:(SEssentialsCarousel *)carousel didTapItem:(UIView *)item atOffset:(CGFloat)offset
{
    [carousel setContentOffset:offset animated:YES withDuration:1.0];
}

To see this in action, take a look at our related code sample: CarouselHowToTapToSelect.xcodeproj

How to: create custom paths with a ShinobiEssentials Carousel

Whilst ShinobiEssentials comes packed with many different types of Carousel, you may find that none of them quite fit the path you want in your app. In this situation, you can create your own path by subclassing the SEssentialsCarousel base class and overriding two methods; positionOfItemAtOffset: and transformOfItemAtOffset:.

Before we begin, it is recommended that you read the explanation of offsets, as we will be working in terms of the item offsets. As a simple example, we will make a diagonal path, but your own method can be as complex as you like. You may also want to look at the related code sample: CarouselHowToCustomPath.xcodeproj.

Create your class, subclassing the SEssentialsCarousel, and override the positionOfItemAtOffset: as follows:

-(CGPoint)positionOfItemAtOffset:(CGFloat)offset
{
    return CGPointMake(offset*150.0, offset*150.0);
}

The CGPoint we return is a position relative to the Focus Point of the Carousel, so at offset 0, we always want to be returning zero. For other offsets, as the offset becomes negative, the CGPoint returned will be negative in both x and y, and the offset becomes positive, the CGPoint will be positive in both x and y.

The other consideration we need to make is which direction we swipe in. This is controlled by the panVector property:

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.panVector = CGPointMake(1, 1);
    }
    return self;
}

The panVector direction should match the forwards direction of our item positions. In our case, as the offset becomes positive, the CGPoint returned by positionOfItemAtOffset: has positive x and y, so our panVector has positive x and y too.

If you got stuck at any point, take a look at our related code sample: CarouselHowToCustomPath.xcodeproj

How to: create a cylindrical carousel with double-sided items

Items in an SEssentialsCarouselCylindrical rotate as they travel along the path of the carousel, it is therefore possible to see the back of an item when it is rotated through more than 90 degrees. By default, the back of an item will simply be a flipped version of the front of an item. You can follow along with the related code sample: CarouselHowToDoubleSidedViews.xcodeproj.

There are cases where you might want to see a different view when looking at the back on an item. For example, imagine a carousel of playing cards where if an item is facing towards you, the face of the card with the suit can be seen. If however, the item is facing away from you then the pattern on the back of the card can be seen. This how-to will show you how this can be achieved.

After having created our carousel it is very easy to set our items so that the view at the back is different to the view at the front. SEssentialsDecoratedDoubleSidedView can be initialized with both a front and back view and the carousel will automatically show the correct view depending on which way the item is facing. To use this all we need to do is return an object of this type from the carousel datasource:

- (UIView *)carousel:(SEssentialsCarousel *)carousel itemAtIndex:(int)index
{
    // Create the view that will be seen when the carousel item is facing forwards
    UILabel *frontView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    frontView.text = [NSString stringWithFormat:@"%i",index];
    frontView.textAlignment = NSTextAlignmentCenter;
    frontView.font = [UIFont systemFontOfSize:50.f];
    frontView.backgroundColor = [UIColor greenColor];

    // Create the view that will be seen when the carousel item is facing backwards
    UIView *backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    backView.backgroundColor = [UIColor redColor];

    // Return an SEssentialsDecoratedDoubleSidedView with the front and back views we have just created
    SEssentialsDecoratedDoubleSidedView *item = [[SEssentialsDecoratedDoubleSidedView alloc] initWithView:frontView andBackView:backView];
    return item;
}

In this code we first create the front view for the item (a green view with a number representing the index of the item in the carousel). We then create the back view of the item, in this case a simple red view. We then initialize a double sided view with the front and back views we have just created and return it from the datasource. The carousel does the rest! If we pan the carousel we can now see that as the items reach the edges of the carousel they rotate around to show the back of the item.

Instances of SEssentialsDecoratedView can be wrapped around each other. For example, you can put an SEssentialsDecoratedReflectedView inside an SEssentialsDecoratedDoubleSidedView. It is important to note that if you want the carousel to show the back of a view, the SEssentialsDecoratedDoubleSidedView must be the last wrapper you apply to the item.

See related code sample: CarouselHowToDoubleSidedViews.xcodeproj