shinobiessentials Progress/Activity Indicators User Guide

Overview

This document provides an overview of the ShinobiEssentials Indicator controls. It describes the features of the controls and their associated concepts, together with some step-by-step guides for achieving common tasks.

Progress and activity indicators are both used as visual feedback to the user that the app is working on something in the background - whether it be a computationally complex task or, more likely in an iOS app, waiting for a response from a remote resource.

  • Progress indicators are used when a task is able to provide regular updates to the user as to the proportion of the task which has been completed. These take the form of a visual indicator which gradually works towards completion.
  • Activity indicators are used when the task is of indeterminate length - e.g. waiting for a network resource to return a response. These take the form of a cyclical animation which repeats.

ShinobiEssentials provides a selection of different progress and activity indicators. Progress indicators are created using the SEssentialsProgressIndicator class, and activity indicators with the SEssentialsActivityIndicator. Progress indicators have a progress property ranging between 0 and 1, which, when updated will update the display of the indicator.

Both progress and activity indicators can be either radial or linear, and discrete or continuous. Discrete indicators are made up of repeated UIViews, whereas continuous indicators are formed from a bar or ring. For further information check out the SEssentialsProgressIndicator and SEssentialsActivityIndicator classes.

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

The Indicator controls have 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 SEssentialsProgressIndicator and SEssentialsActivityIndicator components as quickly as possible. In this guide, we will introduce you to the key features of the two Indicators, including initial project setup, the different implementations of the two components, and using callbacks effectively. You can follow along with the related code sample: IndicatorsGettingStarted.xcodeproj.

Creating an Indicator

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.

Open up ViewController.xib and add a new UIView component, as a subview of the ViewController’s main view. Bring up the Assistant Editor window, and drag from your new view into ViewController.h, adding an IBOutlet property called placeholder. Import the SEssentialsActivityIndicator.h header as follows:

Open up ViewController.h, and import in the Activity Indicator header;

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

@interface ViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIView *placeholder;
@end

Then in ViewController.m, create an Activity indicator, and add it to our placeholder in viewDidLoad;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    SEssentialsActivityIndicator *actIndicator = [SEssentialsActivityIndicator
                                                    activityIndicatorOfType:SEssentialsIndicatorTypeRadialContinuous
                                                                  withFrame:_placeholder.bounds];
    [_placeholder addSubview:actIndicator];
}

The Activity Indicator

Now we have an activity indicator to display, we need to link it up to do something useful. We will add a button which runs a task, but takes a few seconds to do so, so we will show an Activity Indicator while the user waits. To do this, we need to add a UIButton in ViewController.xib, next to the placeholder. Open the Assistance Editor and drag the button from the xib file into the .h file, to create an Action on “Touch Up Inside” called activityTask. This should add the following to your ViewController.h;

@interface ViewController : UIViewController
- (IBAction)activityTask:(id)sender;
@property (strong, nonatomic) IBOutlet UIView *placeholder;
@end

We only want the Activity Indicator to appear while the user is waiting, so remove the viewDidLoad method completely. Instead, we will handle creating the Activity Indicator in activityTask, and remove it on a callback to a function called finishIndicator

-(void) finishIndicator:(SEssentialsProgressIndicator*)indicator
{
    /*
     * Code to finish up the Indicator's task goes here
     */
    [indicator removeFromSuperview];
    indicator = nil;
}

- (IBAction)activityTask:(id)sender
{
    //If we already have an indicator, don't create another one
    if ([self.placeholder.subviews count] > 0)
        return;

    //Create our indicator inside the placeholder
    SEssentialsActivityIndicator *actIndicator = [SEssentialsActivityIndicator
                                                  activityIndicatorOfType:SEssentialsIndicatorTypeRadialContinuous
                                                  withFrame:self.placeholder.bounds];
    actIndicator.style.trackColor = [UIColor colorWithWhite:0.1 alpha:1];
    [self.placeholder addSubview:actIndicator];

    //Set up our callback using threads
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /*
         * Put your long-running, more-exciting-than-sleeping-for-3-seconds task here!
         */
        [NSThread sleepForTimeInterval:3.0f];
        [self performSelectorOnMainThread:@selector(finishIndicator:) withObject:actIndicator waitUntilDone:NO];
    });
}

This method may see daunting a first, so we will take it step by step;

  • We first check if we already have an Activity Indicator, to stop ourselves creating lots of them

  • We then create the Activity Indicator, as we did in viewDidLoad before.

  • We then run our command on a different thread, demonstrating the real power of the Indicators. In our example, we just sleep for 3 seconds, but we could do anything at this stage.

  • Finally, we call back to the finishIndicator: method, which removes the indicator.

Running the app, you will have a button which spools for 3 seconds when pressed, before dismissing the indicator.

The Progress Indicator

Activity Indicators can just run by themselves, but to use Progress Indicators, we need to do a little more work. The progress value of a Progress Indicator will not update automatically, so we need to set it. As before, add another UIButton and UIView in ViewController.xib, and use the Assistant Editor to link them both to ViewController.h to an Action called progressTask, and an Outlet called progressPlaceholder. You should have the following when you finish;

@interface ViewController : UIViewController
- (IBAction)progressTask:(id)sender;
- (IBAction)activityTask:(id)sender;
@property (strong, nonatomic) IBOutlet UIView *placeholder;
@property (strong, nonatomic) IBOutlet UIView *progressPlaceholder;
@end

In ViewController.h, we will implement the Progress Indicator much the same as before;

- (IBAction)progressTask:(id)sender
{
    //If we already have an indicator, don't create another one
    if ([self.progressPlaceholder.subviews count] > 0)
        return;

    //Create our indicator inside the placeholder
    SEssentialsProgressIndicator *progIndicator = [SEssentialsProgressIndicator
                                                   progressIndicatorOfType:SEssentialsIndicatorTypeLinearContinuous
                                                                 withFrame:self.progressPlaceholder.bounds];
    progIndicator.style.trackColor = [UIColor colorWithWhite:0.1 alpha:1];
    [self.progressPlaceholder addSubview:progIndicator];

    //Set up our callback using threads
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        /*
         * Your long-running progressive task goes here, instead of what's between the //------// blocks
         */
        //--------------------------------------------------------------------//
        for (NSUInteger i = 0; i < 100; ++i)  {
            [NSThread sleepForTimeInterval:0.02f];

            [self performSelectorOnMainThread:@selector(updateProgressByStep:) withObject:progIndicator waitUntilDone:YES];
        }
        //--------------------------------------------------------------------//
        [self performSelectorOnMainThread:@selector(finishIndicator:) withObject:progIndicator waitUntilDone:NO];
    });
}

- (void)updateProgressByStep:(SEssentialsProgressIndicator*)progIndicator
{
    progIndicator.progress += 0.01f;
}

The only real difference from before is what we do inside the dispatch_async block. This time, we call to another function updateProgressByStep to update the progress of our Progress Indicator 1% per 0.02 seconds. When it reaches 100%, the for loop will finish, and we call finishIndicator as before, to make it disappear.

If you got stuck at any point, you can look at our related sample code: IndicatorsGettingStarted.xcodeproj.

Quick Start Guide - Swift

Introduction

The following guide will get you up-and-running with the SEssentialsProgressIndicator and SEssentialsActivityIndicator components as quickly as possible. In this guide, we will introduce you to the key features of the two Indicators, including initial project setup, the different implementations of the two components, and using callbacks effectively. You can follow along with the related code sample: IndicatorsGettingStartedSwift.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 an Activity Indicator

Open up Main.storyboard and add a new UIView component, as a subview of the ViewController’s main view. Bring up the Assistant Editor window, and drag from your new view into ViewController.swift, adding an IBOutlet property called activityPlaceholder. Also add a button, which will trigger the activity indicator’s animation. Again, bring up the Assistant Editor window and drag from your button to ViewController.swiftto create an action outlet. Call thisbeginActivityTask`.

In this method we will show an Activity Indicator for a few seconds. To do this, we need to add a UIButton in ViewController.xib, next to the placeholder. Add the following code to the method:

@IBAction func beginActivityTask(sender: UIButton) {
    if(activityPlaceholder.subviews.count > 0) {
        return
    }

    let activityIndicator = `SEssentialsActivityIndicator`(ofType: SEssentialsIndicatorType.RadialContinuous,
        withFrame:activityPlaceholder.bounds)
    activityIndicator.style.trackColor = UIColor(white: 0.1, alpha: 1)
    activityPlaceholder.addSubview(activityIndicator)

    // Set up our callback using threads
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
        NSThread.sleepForTimeInterval(3.0)
        dispatch_async(dispatch_get_main_queue(), {
            activityIndicator.removeFromSuperview();
            });
        });
}

This method may see daunting a first, so we will take it step by step;

  • We first check if we already have an Activity Indicator, to stop ourselves creating lots of them.

  • We then create the Activity Indicator and add it as a subview to the activity placeholder.

  • We spawn a new thread using grand central dispatch, and sleep for 3 seconds.

  • Once the 3 seconds is up, we call back to the main thread (again using grand central dispatch) and remove the activity indicator from the superview.

Running the app, you will have a button which causes the activity indicator to spool for 3 seconds when pressed.

The Progress Indicator

Activity Indicators can just run by themselves, but to use Progress Indicators, we need to do a little more work. The progress value of a Progress Indicator will not update automatically, so we need to set it. As before, add another UIButton and UIView in Main.storyboard, and use the Assistant Editor to link them both to ViewController.swift to an Action called beginProgressTask, and an Outlet called progressPlaceholder.

In ViewController.swift, we will implement the Progress Indicator much the same as before;

@IBAction func beginProgressTask(sender: UIButton) {
    if(progressPlaceholder.subviews.count > 0) {
        return
    }

    let progressIndicator = `SEssentialsActivityIndicator`(ofType: SEssentialsIndicatorType.LinearContinuous,
        withFrame:progressPlaceholder.bounds)
    progressIndicator.style.trackColor = UIColor(white: 0.1, alpha: 1)
    progressPlaceholder.addSubview(progressIndicator)

    // Set up our callback using threads
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
        for index in 0..100 {
            NSThread.sleepForTimeInterval(0.02)

            dispatch_async(dispatch_get_main_queue(), {
                progressIndicator.progress += 0.01;
            });
        }

        dispatch_async(dispatch_get_main_queue(), {
            progressIndicator.removeFromSuperview();
        });
    });
}

The only real difference from before is what we do inside the dispatch_async block. This time, we update the progress of our progressIndicator by 1% every 0.02 seconds. When it reaches 100%, the for loop will finish, and we remove it just as we did above.

If you got stuck at any point, you can look at our related sample code: IndicatorsGettingStartedSwift.xcodeproj.

Quick Start Guide for Xamarin.iOS

Xamarin.iOS: Introduction

The following guide will get you up-and-running with the SEssentialsProgressIndicator and SEssentialsActivityIndicator components as quickly as possible. In this guide, we will introduce you to the key features of the two Indicators, including initial project setup, the different implementations of the two components, and using callbacks effectively. You can follow along with the related code sample: IndicatorsGettingStarted.sln.

Xamarin.iOS: Creating an Indicator

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

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.

Now we want to create two placeholder views for our indicators, so update IndicatorsGettingStarttedViewController.cs adding a using directive for the ShinobiEssentials namespace, and add the placeholder views as follows:

...
using ShinobiEssentials;

namespace IndicatorsGettingStarted
{
    public partial class IndicatorsGettingStartedViewController : UIViewController
    {
        UIView activityPlaceholder;
        UIView progressPlaceholder;

        public IndicatorsGettingStartedViewController ()
            : base ("IndicatorsGettingStartedViewController", null)
        {
        }

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

            // Create the placeholder for the activity indicator
            activityPlaceholder = new UIView (new RectangleF(270, 310, 230, 200));
            View.AddSubview (activityPlaceholder);

            // Create the placeholder for the progress indicator
            progressPlaceholder = new UIView (new RectangleF(185, 700, 400, 45));
            View.AddSubview (progressPlaceholder);
        }
    }
}

Xamarin.iOS: The Activity Indicator

Now that we have the views set up, we can add our Activity Indicator, however we want to link it up to something useful. We will add a button which runs a task, but takes a few seconds to complete, so we will show an Activity Indicator while the user waits.

Add the button first by updating the ViewDidLoad method to include the following:

public override void ViewDidLoad ()
{
    ...
    // Create the begin activity button
    UIButton beginActivity = new UIButton (UIButtonType.RoundedRect) {
        Frame = new RectangleF(270, 200, 230, 60)
    };
    beginActivity.SetTitle ("Begin Activity Task", UIControlState.Normal);
    beginActivity.AddTarget (new EventHandler(StartActivityTask), UIControlEvent.TouchUpInside);
    View.AddSubview (beginActivity);
}

Now implement the StartActivityTask method that will be called when this button is pressed. Add the following method to IndicatorsGettingStartedViewController:

void StartActivityTask (object sender, EventArgs e)
{
    //If we already have an indicator, don't create another one
    if (activityPlaceholder.Subviews.Length > 0) {
        return;
    }

    // Create our indicator inside the placeholder
    SEssentialsActivityIndicator actIndicator = SEssentialsActivityIndicator.Create (SEssentialsIndicatorType.RadialContinuous, activityPlaceholder.Bounds);
    actIndicator.Style.TrackColor = UIColor.FromWhiteAlpha (0.1f, 1);
    activityPlaceholder.AddSubview (actIndicator);

    // Set up our callback using threads
    new Thread (new ThreadStart(() => {
        /*
         * Put your long-running, more-exciting-than-sleeping-for-3-seconds task here!
         */
        Thread.Sleep(3000);
        InvokeOnMainThread(() => {
            actIndicator.RemoveFromSuperview();
            actIndicator = null;
        });
    })).Start ();
}

This method may seem daunting a first, so we will take it step by step;

  • We first check if we already have an ActivityIndicator, to stop ourselves creating lots of them.

  • We then create the Activity Indicator using the SEssentialsActivityIndicator.Create static method. We tell this method that we want a radial indicator that will spin continuously.

  • We then run our command on a different thread, demonstrating the real power of the Indicators. In our example, we just sleep for 3 seconds, but we could do anything at this stage.

  • Finally, we remove the indicator, taking care to do this on the UI thread.

Running the app, you will have a button which spools for 3 seconds when pressed, before dismissing the indicator.

Xamarin.iOS: The Progress Indicator

Activity Indicators can just run by themselves, but to use Progress Indicators, we need to do a little more work. The progress value of a Progress Indicator will not update automatically, so we need to set it. As before, create a button to start our long running task by adding the following to the ViewDidLoad method:

public override void ViewDidLoad ()
{
    ...
    // Create the being progress button
    UIButton beginProgress = new UIButton (UIButtonType.RoundedRect) {
        Frame = new RectangleF(270, 620, 230, 60)
    };
    beginProgress.SetTitle ("Begin Progress Task", UIControlState.Normal);
    beginProgress.AddTarget (new EventHandler(StartProgressTask), UIControlEvent.TouchUpInside);
    View.AddSubview (beginProgress);
}

Now implement the StartProgressTask method that gets called when this button is pressed. Add the following method to IndicatorsGettingStartedViewController;

void StartProgressTask (object sender, EventArgs e)
{
    // If we already have an indicator, don't create another one
    if (progressPlaceholder.Subviews.Length > 0) {
        return;
    }

    // Create our indicator inside the placeholder
    SEssentialsProgressIndicator progIndicator = SEssentialsProgressIndicator.Create (SEssentialsIndicatorType.LinearContinuous, progressPlaceholder.Bounds);
    progIndicator.Style.TrackColor = UIColor.FromWhiteAlpha (0.1f, 1);
    progressPlaceholder.AddSubview (progIndicator);

    // Set up our callback using threads
    new Thread (new ThreadStart(() => {
        /*
         * Your long-running progressive task goes here, instead of what's between the //------// blocks
         */
        //--------------------------------------------------------------------//
        for(int i = 0; i < 100; i++) {
            Thread.Sleep(20);
            BeginInvokeOnMainThread(() => {
                progIndicator.Progress = i / 100.0f;
            });
        }
        //--------------------------------------------------------------------//
        InvokeOnMainThread(() => {
            progIndicator.RemoveFromSuperview();
            progIndicator = null;
        });
    })).Start();
}

The only real difference from the StartActivityTask method is that we constantly update the Progress property of the Progress Indicator, taking care to only update it on the UI thread. When it reaches 100%, the for loop will finish, and we remove the indicator, to make it disappear.

If you got stuck at any point, you can look at our related sample code: IndicatorsGettingStarted.sln.

shinobiessentials Progress/Activity Indicator Control Overview

The ShinobiEssentials Indicators represent a powerful set of UI elements for giving users feedback whilst your app is busy.

This section details the different indicators available and how to best utilize them.

Indicator Types

There are two types of indicator in ShinobiEssentials:

  • Progress Indicators can show how complete a task is - i.e. update the user on what proportion of a task is finished and, by implication, how much longer they should expect to wait. Progress indicators can only be used when you have a task which can regularly report its progress.

  • Activity Indicators are used when a task of unknown length is running. In this case it’s not possible to display completion, so instead a ‘busy’ signal is used. An example use of an activity indicator is for network requests.

Progress Indicators

Progress indicators all inherit from the SEssentialsProgressIndicator class, which have a progress property. progress is a CGFloat which varies from 0.0 to 1.0.

To create a progress indicator there are class methods on SEssentialsProgressIndicator:

  • + progressIndicatorOfType:withFrame:
  • + progressIndicatorOfType:withFrame:style:

Both methods require a type (which is discussed in the following sections), and a frame. Optionally, an SEssntialsProgressIndicatorStyle can be supplied.

Creating a progress indicator is as simple as:

SEssentialsProgressIndicator *indicator = [SEssentialsProgressIndicator progressIndicatorOfType:SEssentialsIndicatorTypeRadialContinuous
                                                                                      withFrame:CGRectMake(0,0,100,100)];
[self.view addSubview:indicator];
for(int i=0; i<100; i++) {
    indicator.progress = i / 100.f;
    [[NSThread currentThread] sleepForTimeInterval:1.f];
}

This creates a progress indicator and then adds 1% to the progress every second for 100s. Obviously this is not a very realistic demonstration, but it explains how the progress of a progress indicator is updated.

Activity Indicators

Activity indicators look very much like their progress counterparts, but rather than representing a shape (or shapes) filling as time passes, to reach an endpoint, activity indicators show a proportion of the indicator filled at all times, and this cycles around/across the control continuously.

Activity indicators all inherit from the SEssentialsActivityIndicator class, which has 2 properties of interest:

  • fillProportion - How much of the indicator should be filled
  • cycleTime - How long should it take for the filled segment to cycle around/across the indicator and return to its original position

To create an activity indicator there are 2 class methods in much the same way as there are for progress indicators:

  • + activityIndicatorOfType:withFrame:
  • + activityIndicatorOfType:withFrame:style:

Both methods require a type (which is discussed in the following sections), and a frame. Optionally, an SEssentialsProgressIndicatorStyle can be supplied.

Creating an activity indicator is as simple as:

SEssentialsActivityIndicator *indicator = [SEssentialsActivityIndicator activityIndicatorOfType:SEssentialsIndicatorTypeRadialContinuous
                                                                                      withFrame:CGRectMake(0, 0, 200, 200)];
[self.view addSubview:indicator];

This create an activity indicator which will start animating as soon as the view is rendered.

Indicator Layout

ShinobiEssentials indicators can be laid out in 2 different arrangements:

  • Radially - the indicator takes the form of a circle
  • Linearly - the indicator is a horizontal line

NOTE: this section applies equally to both activity and progress indicators.

Radial indicators

To create radial indicators use the following indicator types in the appropriate class factory method:

  • SEssentialsIndicatorTypeRadialContinuous
  • SEssentialsIndicatorTypeRadialDiscrete

Radial progress indicators can have a progress label - a label in the center of the circle which displays the current progress value, as a percentage. The appearance of this label is controlled via the indicator style - see the style guide for more information.

Linear indicators

To create radial indicators use the following indicator types in the appropriate class factory method:

  • SEssentialsIndicatorTypeLinearContinuous
  • SEssentialsIndicatorTypeLinearDiscrete

Indicator Construction

Indicators can either be continuous or discrete:

NOTE: this section applies equally to both activity and progress indicators.

Continuous Indicators

Continuous indicators are formed from a solid region which changes size (and position) to indicate progress or activity. Use the following indicator types in the appropriate class factory method:

  • SEssentialsIndicatorTypeRadialContinuous
  • SEssentialsIndicatorTypeLinearContinuous

Discrete Indicators

Discrete indicators are constructed from a number of individual components. The default components are circles, but it is not only possible to change the number of components which make up the indicator, but also to provide your own components.

To create discrete indicators use the following indicator types in the appropriate class factory method:

  • SEssentialsIndicatorTypeRadialDiscrete
  • SEssentialsIndicatorTypeLinearDiscrete

Indicators have a numberOfElements property, which discrete indicators use to determine how many elements they are constructed from. Change this to vary the appearance.

To provide your own elements the indicators have an elementFactory property. This is an object which implements the SEssentialsDiscreteIndicatorElementFactory protocol, which has one method: progressIndicatorElementAtIndex:(NSInteger)index forIndicator:(SEssentialsProgressIndicator *)indicator

This method should return a SEssentialsDiscreteIndicatorElement subclass, for each element index. It’s therefore possible to create different views for each of the different components in the indicator.

The SEssentialsDiscreteIndicatorElement is a UIView subclass with some additional methods added for use in the discrete indicators. An element must have an on state and an off state - i.e. what does the element look like when it is on and when it is off. The subclass provides two constructors the first of which takes UIViews for the two states, and the second is a helper for the user, which accepts UIImages.

See the related how-to guide for an example which implements this functionality.

Styling an Indicator

All ShinobiEssentials Indicators have a style property which allows the user to configure the look of the indicator. It is an instance of SEssentialsProgressIndicatorStyle.

In the style object, you can configure the following visual aspects of your indicator:

  • The color of the complete and incomplete portions of the indicator.
  • Whether shading effects are applied to the indicator.
  • The size of elements within the indicator. For a discrete indicator, this refers to the discrete elements. For a continuous indicator, this refers to the indicator itself.

There are also several properties within the style object which are specific to either continuous or discrete indicators. See the SEssentialsProgressIndicatorStyle API for more information on these.

Progress label

Radial progress indicators have the option of displaying a progress label, which displays the current progress as a percentage, in the center of the indicator. In order to display this there is a property on the style object: showProgressLabel, along with properties to specify the font and color.

iOS7 vs. Earlier iOS versions

By default, iOS7 devices will use the SEssentialsIOS7Theme, while devices on previous versions will use the SEssentialsDarkTheme. In iOS7, the general style of the images and theme is a much flatter, less cluttered appearance, in keeping with the Apple style guidelines. For the indicators, this means a much more subtle background color, with no borders around the track.

Radial indicator in iOS7 theme (left) and Dark theme (right)

Default style properties

An instance of a SEssentialsProgressIndicatorStyle object is created using an instance of a SEssentialsTheme object. The theme provides the default settings for the style object. The following default settings are provided by the theme:

  • The tertiaryTintColor on the theme is used for the color of the completed portion of the indicator (tintColor).
  • The inactiveTintColor on the theme is used for the color of the incomplete portion of the indicator (trackColor).
  • The primaryFont on the theme defines the font of the progress label (progressLabelFont).
  • The primaryTextColor on the theme defines the text color of the progress label (progressLabelColor).
  • The elementStyle on the theme defines whether shading is applied to the completed portion of the indicator (shadingEffect), and whether a border is drawn around it (trackBorderWidth).

Using Threading with Indicators

It is important to note that all the ShinobiEssentials Indicators are updating the UI, and therefore have to operate on the main thread of an app. This means that any intensive operations you are hoping to show the progress of (or simply display the fact that you are busy) must allow the main thread’s run loop to run.

The simplest way to do this is to push your intensive operation onto a different thread, provided that is possible. The quick start guide at the beginning of this document demonstrates how to accomplish this.

If you are unable to use another thread, then you should ensure that your code periodically (i.e. many times per second) allows the run-loop to continue, ensuring that the UI can be updated, and hence the indicators will continue to run.

shinobiessentials Progress/Activity Indicator How-to Guides

How-to provide custom elements to a discrete indicator

Discrete activity and progress indicators are made up of elements. This how-to guide will show you how to create and use custom indicator elements in your discrete progress or activity indicator. You can follow along with the related code sample: IndicatorsHowToCustomElements.xcodeproj.

After having created your indicator you need to set the indicators element factory. The object set as the element factory must conform to the SEssentialsDiscreteIndicatorElementFactory protocol. First, tell the indicator where to find the element factory. In this case, we will use the same class creating the indicator as the element factory.

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // Create an activity indicator
    indicator = [SEssentialsActivityIndicator activityIndicatorOfType:SEssentialsIndicatorTypeLinearDiscrete withFrame:CGRectMake(100, 100, 600, 400)];
    indicator.style.numberOfElements = 10;
    // Set self as the element factory - so we can make the elements!
    indicator.elementFactory = self;
    [self.view addSubview:indicator];
}

From here, it is very easy to create your own indicators; there is only one required method on this protocol:

- (SEssentialsDiscreteIndicatorElement *)progressIndicatorElementAtIndex:(NSInteger)index forIndicator:(SEssentialsProgressIndicator *)indicator
{
    // Create a frame for the element
    CGFloat elementSize = 10+(index*5);
    CGRect elementFrame = CGRectMake(0, 0, elementSize, elementSize);

    // Create a view for the on state of the element - this is a green circle with black border
    UIView *onView = [[UIView alloc] initWithFrame:elementFrame];
    onView.layer.cornerRadius = elementSize/2;
    onView.layer.borderColor = [UIColor blackColor].CGColor;
    onView.layer.borderWidth = 1.f;
    onView.backgroundColor = [UIColor greenColor];

    // Create a view for the off state of the element - this is a clear circle with black border
    UIView *offView = [[UIView alloc] initWithFrame:elementFrame];
    offView.layer.cornerRadius = elementSize/2;
    offView.layer.borderColor = [UIColor blackColor].CGColor;
    offView.layer.borderWidth = 1.f;
    offView.backgroundColor = [UIColor clearColor];

    // Create the element
    SEssentialsDiscreteIndicatorElement *element = [[SEssentialsDiscreteIndicatorElement alloc] initWithFrame:elementFrame onView:onView offView:offView];
    return element;
}

The code above implements the required method and returns an SEssentialsDiscreteIndicatorElement for the indicator to use. The indicator element is made up of two UIViews. The indicator displays the elements onView for when the element is on and the offView for when it is off.

You can use any UIView subclass as your elements on and off views. We have kept it simple in this example however and use a simple UIView for each. We have made a circular on view with a green background and black border. The off view is similar except that the background color is clear.

You also do not have to return the same indicator element for each element in the indicator! In this example the elements change size but we are not limited by just the changing size, you can return any view you like.

See related code sample: IndicatorsHowToCustomElements.xcodeproj

How-to provide custom styling to a ShinobiEssentials Indicator

It is very straight forward to change what your progress or activity indicator looks like. This how-to guide will show you how to change the styling of a ShinobiEssentials Indicator. You can follow along with the related code sample: IndicatorsHowToCustomStyling.xcodeproj.

Every ShinobiEssentials Indicator has a style property of type SEssentialsProgressIndicatorStyle. By default the properties on the style are set to sensible values and some are inherited from the theme, see Styling an Indicator for more details. You can of course change these properties to style your indicator in any way you like.

// Create an activity indicator
SEssentialsActivityIndicator *indicator = [SEssentialsActivityIndicator activityIndicatorOfType:SEssentialsIndicatorTypeRadialContinuous
                                                                                      withFrame:CGRectMake(100, 100, 300, 300)];

// Change the color of the indicator
indicator.style.trackColor = [UIColor colorWithRed:0.2 green:0.5 blue:0.5 alpha:1.0];
indicator.style.tintColor = [UIColor colorWithRed:0.5 green:0.2 blue:0.4 alpha:1.0];

// Change the size of the indicator
indicator.style.elementSize = 30;
indicator.style.trackBorderWidth = 10;

// Add a background to the indicator
indicator.style.showBackground = YES;
indicator.style.indicatorBackgroundColor = [UIColor colorWithWhite:0.7 alpha:1.0];
indicator.style.backgroundPadding = 40;

The code here creates an SEssentialsActivityIndicator and applies custom styling. Firstly we change the color of the indicator using the trackColor and tintColor properties. The size of the indicator is then changed with elementSize and trackBorderWidth. We also add a background to the indicator using the showBackground property, this background is then customized further using indicatorBackgroundColor and backgroundPadding. By changing just a few properties on the indicators style we have customized the look of our indicator.

It is worth noting that some style properties are only applicable to specific types of indicator. For example, fadeTail and numberOfElements only apply to discrete indicators. The SEssentialsActivityIndicator class also has two further properties to style the indicator. fillProportion controls the amount of the indicator which is filled and cycleTime controls the speed at which the indicator rotates.

There are further properties which can also be used to customize the styling of an indicator. A full list is available and documented on the SEssentialsProgressIndicatorStyle API.

See related code sample: IndicatorsHowToCustomStyling.xcodeproj

How-to use an activity indicator whilst waiting for network requests

One of the most popular uses for an activity indicator on an iOS device is to signal that the app is waiting for a response from a network service.

This how-to will demonstrate a simple app which shows an activity indicator whilst waiting for a response from a network resource. The example here is very simple and so just uses native iOS libraries - if you were building an app with network-centric components then use of a library such as AFNetworking is recommended. You can follow along with our related code sample: IndicatorsHowToWaitForNetwork.xcodeproj.

This sample app has a really simple UI - a button to send the request, a label to show the received data and an SEssentialsActivityIndicator that is shown whilst waiting for the response from the request. As a sample web service we’ll use SlowAPI, which will return random words after a delay specified in the URL - perfect for demonstrating an activity indicator.

We need 3 ivars to keep hold of the aforementioned UI components:

@implementation ViewController {
    UIButton *requestButton;
    UILabel  *responseLabel;
    SEssentialsActivityIndicator *activityIndicator;
}

A setup method is created to instantiate the components. Note that we don’t add the activity indicator as a subview at this stage, as we only want it to appear once the network request has begun:

- (void)setupView
{
    // Create a button
    requestButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [requestButton setTitle:@"Send Network Request" forState:UIControlStateNormal];
    [requestButton sizeToFit];
    requestButton.frame = CGRectMake(100, 100, requestButton.bounds.size.width, requestButton.bounds.size.height);
    [requestButton addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:requestButton];

    // Create a label for the response
    responseLabel = [[UILabel alloc] init];
    responseLabel.frame = CGRectMake(100, 150, 300, 30);
    responseLabel.backgroundColor = [UIColor clearColor];
    [self.view addSubview:responseLabel];

    // Create an activity indicator (but don't display it)
    activityIndicator = [SEssentialsActivityIndicator activityIndicatorOfType:SEssentialsIndicatorTypeRadialContinuous
                                                                    withFrame:CGRectMake(100, 200, 100, 100)];
    activityIndicator.style.elementSize = 15;
    activityIndicator.style.trackBorderWidth = 3;
    activityIndicator.style.trackColor = [UIColor grayColor];
    activityIndicator.style.tintColor = [UIColor purpleColor];
}

The button created has a target method for when it is clicked:

- (void)buttonPressed:(id)sender
{
    // Show the activity indicator. Add it with alpha=0 and then fade it in.
    activityIndicator.alpha = 0;
    [self.view addSubview:activityIndicator];
    [UIView animateWithDuration:0.5 animations:^{
        activityIndicator.alpha = 1;
    }];

    // Disable the button
    requestButton.enabled = NO;

    // Update the label text:
    responseLabel.text = @"Request sent...";

    // Send network request
    NSURL *url = [NSURL URLWithString:@"http://slowapi.com/delay/5.0"];
    // Perform request on background thread
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:url];
        NSString *response;
        if(!data) {
            response = @"";
        } else {
            response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            [self networkResponseReceived:response];
        });
    });
}

This method is fairly self-explanatory, but there are a few salient points:

  • For an extra bit of polish we fade the indicator in.
  • It’s important that the network request is performed on a background thread. The activity indicator will only animate when the runloop on the main thread is allowed to run (since it is a UI component). Grand Central Dispatch (GCD) makes it easy to push a block of work onto a background queue. Since we know that the service we are using (SlowAPI) will just return a string we perform the appropriate conversion here. It’s at this point you would need to implement the required functionality for the service to which your app is connecting.

Once the data has been received we call another method on the main thread to remove the activity indicator:

- (void)networkResponseReceived:(NSString *)response
{
    // Update the label with the content
    responseLabel.text = [NSString stringWithFormat:@"Response: %@", response];

    // Remove the activity indicator. Fade it out nicely first, then remove.
    [UIView animateWithDuration:0.5 animations:^{
        activityIndicator.alpha = 0;
    } completion:^(BOOL finished) {
        [activityIndicator removeFromSuperview];
    }];

    // Re-enable the button
    requestButton.enabled = YES;
}

This method undoes some of the things we prepared in the button press method:

  • Fade out and remove the activity indicator
  • Re-enable the currently disabled button

We also put the content of the response string into the response label.

The source code for this sample is available in IndicatorsHowToWaitForNetwork.xcodeproj.