shinobiessentials Accordion User Guide

Overview

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

The SEssentialsAccordion provides a control with expandable sections, allowing content to be shown and hidden. Accordions may be fixed, where only one section is open at a time, or flexible, where many sections can be open and closed.

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 Accordion works and the features it presents, head over to the ShinobiEssentials Accordion Control Overview. Finally, for guides that tackle specific usage scenarios, head on over to the ShinobiEssentials Accordion How-to Guides.

The Accordion 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 SEssentialsAccordion component as quickly as possible. In this guide, we will introduce you to the key features of the Accordion, including initial project setup, adding items to the Accordion, and managing them with a datasource. You can follow along with the related code sample: AccordionGettingStarted.xcodeproj.

Creating an Accordion

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, we want to define a variable to store our Accordion. Open up ViewController.h, import the SEssentialsAccordion.h header, and define the variable as follows:

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

@interface ViewController : UIViewController
{
    SEssentialsAccordion *accordion;
}

@end

We define the new Accordion in ViewController.m as;

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

Using the Datasource

With the Accordion in place, we now need to add some sections. To do this, we provide the Accordion with a datasource. This can be a separate class, but we will use the ViewController in this example. In ViewController.h, we need to adopt the SEssentialsAccordionDataSource protocol;

@interface ViewController : UIViewController < SEssentialsAccordionDataSource >

We need to keep a reference to the content of each section in the Accordion so the datasource can return it when requested. We will use an NSMutableDictionary to store our sections, so declare this in a private interface at the top of ViewController.m;

@interface ViewController()
{
    NSMutableDictionary *mapSectionToView;
}
@end

@implementation ViewController
...

We need to initialize the dictionary on load, and we need to tell the Accordion to treat ViewController as its datasource;

- (void)viewDidLoad
{
    ...
    accordion.dataSource = self;

    //Set up the Section => View dictionary
    mapSectionToView = [[NSMutableDictionary alloc] init];

    //Fill in the sections
    [self setupSections];
}

A few other functions are needed; we need to define setupSections to create our sections, and we need to implement the methods on the SEssentialsAccordionDataSource protocol. Firstly for setupSections;

- (void)setupSections
{
    // let's add 3 sections to the accordion
    for (int i=0; i<3; i++) {
        [self addAccordionSection];
    }
}

- (void)addAccordionSection
{
    SEssentialsAccordionSection *section = [[SEssentialsAccordionSection alloc] initWithFrame:CGRectMake(0, 0, 100, 50) andTitle:@"Hello World"];

    // create the content that shows when the section is opened

    UILabel *content = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 200)];
    content.text = @"Add some content here";
    content.backgroundColor = [UIColor clearColor];

    // associate the content with the section in a dictionary
    // note we wrap the section in an NSValue which implements NSCopying

    [mapSectionToView setObject:content forKey:[NSValue valueWithNonretainedObject:section]];

    // add section to accordion
    [accordion addSection:section];
}

You might notice that we add the content for our section to the dictionary with the section as its key. This is because the datasource method we must implement requests the content for a section; by using a dictionary we can easily lookup the correct content to return.

Finally, to tell the accordion what to load, we implement the SEssentialsAccordionDataSource method;

- (UIView *)accordion:(SEssentialsAccordion *)accordion contentForSection:(SEssentialsAccordionSection *)section
{
    // given a section look it up in our dictionary to find the associated content
    // note that the key has to be wrapped as an NSValue for use in a NSDictionary
    return [mapSectionToView objectForKey:[NSValue valueWithNonretainedObject:section]];
}

With this, we have a working Accordion. Run your application, and check that you have three sections each with “Hello World” as the title and some content.

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

Quick Start Guide - Swift

Introduction

The following guide will get you up-and-running with the SEssentialsAccordion component as quickly as possible. In this guide, we will introduce you to the key features of the Accordion, including initial project setup, adding items to the Accordion, and managing them with a datasource. You can follow along with the related code sample: AccordionGettingStartedSwift.xcodeproj.

Setting up the project

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

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 an Accordion

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.

We define the new Accordion in ViewController.swift as;

override func viewDidLoad() {
    super.viewDidLoad()

    let accordion = SEssentialsAccordion(frame: view.bounds)
    view.addSubview(accordion)
}

Using the Datasource

With the Accordion in place, we now need to add some sections. To do this, we provide the Accordion with a datasource. This can be a separate class, but we will use the ViewController in this example. In ViewController.swift, we need to adopt the SEssentialsAccordionDataSource protocol;

class ViewController: UIViewController, SEssentialsAccordionDataSource {

We need to keep a reference to each section in the Accordion so the datasource knows what to return for the sections content when requested. Declare 3 sections as properties at the top of the class.

let section1 = SEssentialsAccordionSection(frame: CGRect(x: 0, y: 0, width: 100, height: 50), andTitle: "Section 1")
let section2 = SEssentialsAccordionSection(frame: CGRect(x: 0, y: 0, width: 100, height: 50), andTitle: "Section 2")
let section3 = SEssentialsAccordionSection(frame: CGRect(x: 0, y: 0, width: 100, height: 50), andTitle: "Section 3")

We now need to tell the Accordion to treat ViewController as its datasource by setting it’s dataSource property. Add the following line to viewDidLoad():

accordion.dataSource = self

We also need to add the sections to the accordion, so create an array and add them below setting the dataSource property:

    accordion.addSectionsFromArray([section1, section2, section3])

Finally, to tell the accordion what to load as the content for the section, we must implement the SEssentialsAccordionDataSource method:

func accordion(accordion: SEssentialsAccordion!, contentForSection section: SEssentialsAccordionSection!) -> UIView! {
    var contentLabel = UILabel(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 200))
    if section == section1 {
        contentLabel.text = "Section 1 Content"
    }
    else if section == section2 {
        contentLabel.text = "Section 2 Content"
    }
    else if section == section3 {
        contentLabel.text = "Section 3 Content"
    }

    contentLabel.textAlignment = NSTextAlignment.Center
    contentLabel.backgroundColor = UIColor.clearColor()
    return contentLabel
}

With this, we have a working Accordion. Run your application, and check that you have three sections each with the correct title and content.

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

Quick Start Guide for Xamarin.iOS

Xamarin.iOS: Introduction

The following guide will get you up-and-running with the SEssentialsAccordion component as quickly as possible. In this guide, we will introduce you to the key features of the Accordion, including initial project setup, adding items to the Accordion, and managing them via the datasource. You can follow along with the related code sample: AccordionGettingStarted.sln.

Xamarin.iOS: Creating an Accordion

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 ‘AccordionGettingStarted’.

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 Accordion. Open up AccordionGettingStartedViewController.cs, add a using directive for the the ShinobiEssentials namespace, and define the member variable as follows:

...
using ShinobiEssentials;

namespace AccordionGettingStarted
{
    public partial class AccordionGettingStartedViewController : UIViewController
    {
        SEssentialsAccordion accordion;

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

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

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

    accordion = new SEssentialsAccordion (View.Bounds);
    View.AddSubview (accordion);
}

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

Xamarin.iOS: Using the Datasource

With the Accordion in place, we now need to add some sections. To do this, we provide the Accordion with a datasource. In order to provide this data to the Accordion you must create a new class that inherits from SEssentialsAccordionDataSource.

Right-click the project and select the Add / New File … option, selecting the Empty Class option, naming the class AccordionDataSource. Make the class inherit from SEssentialsAccordionDataSource as follows:

public class AccordionDataSource : SEssentialsAccordionDataSource
{
}

We keep a reference to the content of each section in the Accordion so the datasource can return it when requested. We will use a Dictionary to store our sections, so declare this as a private member variable in AccordionDataSource;

Dictionary < SEssentialsAccordionSection, UIView > mapSectionToView = new Dictionary < SEssentialsAccordionSection, UIView > ();

We need a way to initialize the dictionary, so create a method on the AccordionDataSource to add a section as follows:

public SEssentialsAccordionSection CreateSection(CGFloat width)
{
    SEssentialsAccordionSection section = new SEssentialsAccordionSection (new RectangleF(0, 0, width, 50), "Hello World");

    // create the content that shows when the section is opened
    UILabel content = new UILabel (new RectangleF(0, 0, width, 200)) {
        Text = "Add some content here",
        TextAlignment = NSTextAlignment.Center,
        BackgroundColor = UIColor.Clear
    };

    // associate the content with the section in a dictionary
    mapSectionToView.Add (section, content);

    return section;
}

You might notice that we add the content for our section to the dictionary with the section as its key. This is because the datasource method we must implement requests the content for a section; by using a dictionary we can easily lookup the correct content to return.

Add the required GetContent method on AccordionDataSource to return the views for each section as follows:

    public override UIView GetContent (SEssentialsAccordion accordion, SEssentialsAccordionSection section)
    {
        return mapSectionToView [section];
    }

Now we can assign our AccordionDataSource to the Accordion and populate it with some data. Open up AccordionGettingStartedViewController.cs and update your ViewDidLoad method to set the DataSource property of the Accordion:

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

    AccordionDataSource dataSource = new AccordionDataSource ();

    accordion = new SEssentialsAccordion (View.Bounds) {
        DataSource = dataSource
    };
    View.AddSubview (accordion);

    for (int i = 0; i < 3; i++) {
        accordion.AddSection (dataSource.CreateSection(View.Bounds.Width));
    }
}

With this, we have a working Accordion. Run your application, and check that you have three sections each with “Hello World” as the title and some content.

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

shinobiessentials Accordion Control Overview

The ShinobiEssentials Accordion control provides a way to present a list of data - with a series of headers, each of which is associated with additional content views which can be revealed by interacting with the headers. Accordions come in two distinct flavors:

  • fixed accordions only allow one section to be expanded at once, and as such when a section opens, the currently open section will close.
  • flexible accordions allow multiple sections to be open simultaneously and therefore scroll to enable the content to fit within the controls bounds.

This section of the user guide will go into more detail about how the accordion works and how best to utilize the SEssentialsAccordion control.

Managing Sections

An Accordion is made up from a collection of SEssentialsAccordionSection objects, which the user has to provide to the accordion. A section comprises of a content view and a header view.

  • The content view is provided by the accordion datasource, which is a required property of an accordion, and is a class which implements the SEssentialsAccordionDatasource protocol. This class will be queried for the content of a section as it is required to render, and allows for content virtualization.
  • The header of a section is provided at the time a section is created, and is of type SEssentialsAccordionSectionHeader. This class has a label and allows the user to add text. For more control it’s possible to subclass SEssentialsAccordionSectionHeader.

For ease of use, the SEssentialsAccordionSection class has a constructor which does not require the user to provide a header, but instead takes a title string.

The content supplied by the datasource is only retained by the Accordion while the content is visible on screen, and will be released when scrolled off screen or the section is closed. It is up to the class adopting the SEssentialsAccordionDataSource protocol to retain content so it can be requested at any time.

Sections in the accordion

Once a section has been created it can be added to the accordion with the addSection: method. Consult the API documentation of SEssentialsAccordion for details on other methods for section management, such as removal, reordering and insertion.

An accordion can return an array of its current sections with the sections property.

Content for sections

The content for sections is obtained by querying the accordion’s datasource. This is a required object, which implements the SEssentialsAccordionDatasource protocol. The datasource returns UIViews for each section when they are requested, and these are displayed in the expanded section in the accordion.

The accordion has a lazyLoading flag which determines when the content for a section is requested from the datasource.

  • If lazyLoading is set to YES then the content for a section will be requested only when a section opens, and will be retained by the accordion until the section closes. If the section re-opens then the content will be requested again.
  • If lazyLoading is set to NO then the content for all sections will be requested when the accordion is first rendered, and will be retained by the accordion until it is deallocated.

Delegate

A ShinobiEssentials Accordion has a delegate property which is an object which implements the SEssentialsAccordionDelegate protocol. The delegate will get callbacks for different events such as section opening, closing and moving. The callbacks all take the same form, with the following prefixes:

  • should: called before a UI interaction event to ask whether the event should continue. Return a boolean specifying whether to perform the event or not. If not implemented the event will continue. This is not called before programmatic events.
  • will: called immediately before the action, applies to both programmatic and UI interaction events.
  • did: called immediately after the action, applies to both programmatic and UI interaction events.

Programmatic Control

Users can interact with the Accordion control in an extremely intuitive manner: - tapping on section headers will open/close sections - swiping the accordion will scroll it (when of flexible type) - pinching sections will open/close them - when in edit mode, dragging sections will reorder them

It is also possible to perform the same operations programmatically, with the section management methods on the SEssentialsAccordion class. Sections can be:

  • inserted (insertSection:atIndex:)
  • removed (removeSection:)
  • moved (moveSectionFromIndex:toIndex:)

Opening and closing sections is controlled with the open property on the section itself. Setting this to YES will open a section, and to NO will close it.

As mentioned above, none of these methods will call the should delegate callbacks, but they will call will and did.

Styling

The only part of the Accordion control which has any style associated with it is the header, therefore a section header has a style property, which is a SEssentialsAccordionSectionHeaderStyle object.

The SEssentialsAccordionSectionHeaderStyle object allows you to configure the following visual aspects of an accordion section header:

  • The background color of the section header. It has different colors for when it is in its normal state, when it is selected, and when it is pressed.
  • The texture which is applied to the section header.
  • The font and color of the title in the section header.
  • The decoration which is applied to the section header. This refers to the color of the shine which is applied to the header, the color of the chisel at the top of the header, the color of the chisel at the bottom of the header, and the color and depth of the drop shadow which is drawn below the header onto its section.

The style object also allows you to set the background color of the section with which the header is associated, and the radius of its corners.

iOS7 vs. Earlier iOS versions

By default, iOS7 devices will use the SEssentialsIOS7Theme, while devices on previous versions will use the SEssentialsDarkTheme. The general style of the images and theme in iOS7 is a much flatter, less cluttered appearance, in keeping with the Apple style guidelines. For the Accordion, this means a lot of the decoration we use in previous versions has been reduced or removed, such as removing shine, and lessening the drop shadows.

The accordion in the iOS7 theme (left) and the Dark theme (right)

Default styling properties

SEssentialsAccordionSectionHeaderStyle derives from SEssentialsStyle, and so it is initialized using a SEssentialsTheme object. The theme sets the default properties of the style.

The following settings are set by default from the theme:

  • The primaryTintColor on the theme is used for the background color of the header (backgroundColor).
  • The secondaryTintColor on the theme is used for the section background color (sectionBackgroundColor) and the selected header color (selectedBackgroundColor).
  • The activeTintColor on the theme is used for the background color of the header when it is pressed (pressedBackgroundColor).
  • The primaryFont on the theme is used as the font for the header title (font).
  • The primaryTextColor on the theme is used for the font color in the header (fontColor).
  • The primaryDecorationTintColor on the theme is used for the color of the chisel at the top of the header (chiselPrimaryColor).
  • The secondaryDecorationTintColor on the theme is used for the color of the chisel at the bottom of the header (chiselSecondaryColor).
  • The shineColor on the theme is used for the shine color (shineColor).
  • The shadowColor and shadowDepth properties on the theme set the matching properties on the style (shadowColor and shadowDepth).

shinobiessentials Accordion How-to Guides

How-to create an accordion with custom header views

This How To guide will teach you how to create an accordion with custom header views. It may help to follow along with our related code sample: AccordionHowToCustomHeaders.

The SEssentialsAccordion uses helper functions to quickly create sections and headers, however sometimes these can be a bit too basic for our needs. For example, to create a two-line header, or to add some icons, we need to create a custom header view.

To create a custom header, use the initWithFrame:andHeader: method when creating your SEssentialsAccordionSection, and pass in your header. For example, for a custom header creating text on two lines;

-(void)addSectionForAccordion:(SEssentialsAccordion*)accordion firstLine:(NSString*)first secondLine:(NSString*)second
{
    SEssentialsAccordionSectionHeader *header = [[CustomHeader alloc] initWithFrame:CGRectMake(0, 0, accordion.bounds.size.width, 40)
                                                                          firstLine:first
                                                                        secondLine:second];
    SEssentialsAccordionSection *section = [[SEssentialsAccordionSection alloc] initWithFrame:header.bounds andHeader:header];
    [accordion addSection:section];
}

To create your custom header, simply subclass SEssentialsAccordionSectionHeader, and customize it when you create it.

@interface CustomHeader : SEssentialsAccordionSectionHeader
-(id)initWithFrame:(CGRect)frame firstLine:(NSString*)first secondLine:(NSString*)second;
@end



@implementation CustomHeader

-(id)initWithFrame:(CGRect)frame firstLine:(NSString*)first secondLine:(NSString*)second
{
    self = [self initWithFrame:frame andTitle:@""];
    if (self)
    {
        //Put your customizations here
    }
    return self;
}

@end

Note that we use initWithFrame:andTitle:, rather than initWithFrame:, to use the custom ShinobiControls styling, which is present on all default instances of the SEssentialsAccordion.

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

How-to move sections in an accordion

The SEssentialsAccordion behaves like a UITableView in many ways; one way is how to rearrange sections in the accordion. Rearrangement can either be done programmatically, or through gestures. This how-to guide will show you how to do both. It may help to follow along with our related code sample: AccordionHowToMoveSections.xcodeproj.

To allow your users to rearrange sections, you will need to set the editing property on the SEssentialsAccordion to YES. This adds small icons to each section to allow moving or deleting. By holding down over a movement icon (3 horizontal lines), the section can be dragged up or down over other sections to reorder them.

To do the same programmatically, use the moveSectionFromIndex:toIndex: method on the SEssentialsAccordion. For example, if we added 3 sections to our Accordion, then wanted to put the last section above the first two, we would call it as;

-(void)viewDidLoad
{
    ...

    SEssentialsAccordionSection *firstSection = [[SEssentialsAccordionSection alloc] initWithFrame:sectionFrame andTitle:@"First section"];
    SEssentialsAccordionSection *secondSection = [[SEssentialsAccordionSection alloc] initWithFrame:sectionFrame andTitle:@"Second section"];
    SEssentialsAccordionSection *thirdSection = [[SEssentialsAccordionSection alloc] initWithFrame:sectionFrame andTitle:@"Third section"];

    [accordion addSectionsFromArray:@[firstSection, secondSection, thirdSection]];

    //Move the third section above the first
    [accordion moveSectionFromIndex:2 toIndex:0];
}

This will place “Third section” above “First section” and “Second section”.

When items are moved programmatically or with the edit mode, the Accordion’s datasource needs to be updated to reflect the change. The Accordion tells the SEssentialsAccordionDelegate about a change in ordering with the accordion:shouldMoveSectionFromIndex:, accordion:willMoveSectionFromIndex: and `accordion:didMoveSectionFromIndex: callbacks. Depending on the datasource items, we may want to update on the will or did callbacks, but should do so on one to keep the indices intact for other callbacks.

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