Overview

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

The SEssentialsFlowLayout provides an easy way to layout and display views on screen. The views are arranged in a raster fashion, in the order they are provided to the control. The default behavior supports an edit mode in which the user can reorder and remove the views. Views can be removed from the FlowLayout using one of 2 delete idioms - one matching the iOS home screen icon management, and the other providing a trash can to which the views can be dragged.

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 ShinobiEssentials FlowLayout works and the features it presents, head over to the ~!@[ShinobiEssentials](../../../Classes/ShinobiEssentials.html) FlowLayout Control Overview@!~ FlowLayout Control Overview). Finally, for guides that tackle specific usage scenarios, head on over to the ~!@[ShinobiEssentials](../../../Classes/ShinobiEssentials.html) FlowLayout How-to Guides@!~ FlowLayout How-to Guides).

The ShinobiEssentials FlowLayout 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 SEssentialsFlowLayout component as quickly as possible. In this guide, we will introduce you to the key features of the FlowLayout, including initial project setup, adding items, and customizing the layout. You can follow along with the related code sample: FlowLayoutGettingStarted.xcodeproj.

Creating a FlowLayout

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.

We also want to define a variable to store our FlowLayout. Open ViewController.m, import the header, and define the variable as follows:

#import "ViewController.h"
#import <ShinobiEssentials/ShinobiEssentials.h>

@implementation ViewController {
   SEssentialsFlowLayout *myFlowLayout;
}

In viewDidLoad define the new FlowLayout, as below;

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

If you run the application now, you should see the empty FlowLayout on screen, with a dark gray textured background;

Adding Items

The FlowLayout is designed to display images, and allow them to be dragged and rearranged. Now that we have our FlowLayout displaying, we need to add some images. We will be using some sample images, so to follow along you will need to download them too. Drag the Images folder into your project as a Folder Reference (not as a Group). If done correctly, the Images folder in your project navigator will be blue, not yellow.

With the images imported into our project, load them in in ViewController.m’s viewDidLoad;

- (void)viewDidLoad
{
    ...
    [self importImages];
    [self.view addSubview:myFlowLayout];
}

-(void)importImages
{
    NSArray *imgPaths = [[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:@"Images"];
    for (NSString *path in imgPaths)
    {
        //Create image from filepath, and add it to the flow layout
        UIImage *newImg = [[UIImage alloc] initWithContentsOfFile:path];
        UIImageView *newView = [[UIImageView alloc] initWithImage:newImg];
        [myFlowLayout addManagedSubview:newView];
    }
}

With this set up, run the app and check the letters appear. You can use edit mode by holding one down, which will let you rearrange or delete any of them.

As you can see though, the letters are all different heights, and align to the top by default. For some applications that would be exactly what is required, but for letters, it makes more sense to have them align to the bottom. To do this, we set the verticalSubviewAlignment on the FlowLayout;

- (void)viewDidLoad
{
    ...
    myFlowLayout.verticalSubviewAlignment = SEssentialsAlignBottom;
    [self.view addSubview:myFlowLayout];
}

With that, our images are now aligned properly;

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

Quick Start Guide - Swift

Introduction

The following guide will get you up-and-running with the SEssentialsFlowLayout component as quickly as possible. In this guide, we will introduce you to the key features of the FlowLayout, including initial project setup, adding items, and customizing the layout. You can follow along with the related code sample: FlowLayoutGettingStartedSwift.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 FlowLayout

Open up ViewController.swiftand in viewDidLoad() define the new FlowLayout, as specified below:

override func viewDidLoad() {
    super.viewDidLoad()

    let flowLayout = SEssentialsFlowLayout(frame: view.bounds, withDeleteIdiom: SEssentialsFlowDeleteIdiom.TrashCan)
    view.addSubview(flowLayout)
}

Adding Items

The FlowLayout is designed to display images, and allow them to be dragged and rearranged. Now that we have our FlowLayout displaying, we need to add some images. We will be using some sample images, so to follow along you will need to download them too. Drag the Images folder into your project as a Folder Reference (not as a Group). If done correctly, the Images folder in your project navigator will be blue, not yellow.

With the images imported into our project, load them in viewDidLoad() in ViewController.swift;

    let paths = NSBundle.mainBundle().pathsForResourcesOfType("png", inDirectory: "Images") as Array<String>
    var fileCount = 0
    for path : String in paths {
        let imgView = UIImageView(image: UIImage(contentsOfFile: path))
        imgView.tag = fileCount++
        flowLayout.addManagedSubview(imgView)
    }

With this set up, run the app and check the letters appear. You can use edit mode by holding one down, which will let you rearrange or delete any of them.

As you can see though, the letters are all different heights, and align to the top by default. For some applications that would be exactly what is required, but for letters, it makes more sense to have them align to the bottom. To do this, we set the verticalSubviewAlignment on the FlowLayout;

flowLayout.verticalSubviewAlignment = SEssentialsVerticalAlignment.AlignBottom

With that, our images are now aligned properly;

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

Quick Start Guide for Xamarin.iOS

Xamarin.iOS: Introduction

The following guide will get you up-and-running with the SEssentialsFlowLayout component as quickly as possible. In this guide, we will introduce you to the key features of the FlowLayout, including initial project setup, adding items, and customizing the layout. You can follow along with the related code sample: FlowLayoutGettingStarted.sln.

Xamarin.iOS: Creating a FlowLayout

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

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

...
using ShinobiEssentials;

namespace FlowLayoutGettingStarted
{
    public partial class FlowLayoutGettingStartedViewController : UIViewController
    {
        SEssentialsFlowLayout myFlowLayout;

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

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

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

    myFlowLayout = new SEssentialsFlowLayout (View.Bounds, SEssentialsFlowDeleteIdiom.TrashCan);
    View.AddSubview (myFlowLayout);
}

If you run the application now, you should see the empty FlowLayout on screen, with a dark gray textured background:

Xamarin.iOS: Adding Items

The FlowLayout is designed to display images, and allow them to be dragged and rearranged. Now that we have our FlowLayout displaying, we need to add some images. We will be using some sample images, so to follow along you will need to download them too. Right click the Resources folder, select Add -> Add Files… -> Select all the files and click open. If done correctly, you can expand the Resources folder and all the images should be listed with the images imported into our project, load them in ViewDidLoad in FlowLayoutGettingStartedViewController.cs:

public override void ViewDidLoad ()
{
    ...
    ImportImages ();
    View.AddSubview (myFlowLayout);
}

void ImportImages ()
{
    int fileCount = 0;
    foreach (string file in new string[] { "b", "h", "i", "i2", "logo", "n", "o", "s" }) {
        //Create image from filepath, and add it to the flow layout
        UIImage newImg = new UIImage ("./" + file + ".png");
        UIImageView newView = new UIImageView (newImg);

        //Tag the image with an order
        newView.Tag = fileCount++;

        myFlowLayout.AddManagedSubview (newView);
    }
}

With this set up, run the app and check the letters appear. You can use edit mode by a long press on any element, which will let you rearrange or delete any of them.

As you can see though, the letters are all different heights, and align to the top by default. For some applications that would be exactly what is required, but for letters, it makes more sense to have them align to the bottom. To do this, we set the VerticalSubviewAlignment on the FlowLayout:

public override void ViewDidLoad ()
{
    ...
    myFlowLayout.VerticalSubviewAlignment = SEssentialsVerticalAlignment.Bottom;
    View.AddSubview (myFlowLayout);
}

With that, our images are now aligned properly;

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

ShinobiEssentials FlowLayout Control Overview

The SEssentialsFlowLayout provides an easy way to layout and display views on screen. The default behavior supports an edit mode in which the user can reorder and remove the views in the flow layout.

This section describes the features and concepts behind the FlowLayout.

Managing the content of the FlowLayout

In order for the FlowLayout to display views you must first supply them. You can supply the flow layout with any instance or subclass of UIView. Two methods control adding views to the FlowLayout, these are addManagedSubview: which adds the view to the FlowLayout at the end of all currently displayed views. The second method is addManagedSubview:atIndex: which adds the view to the FlowLayout at the specified index.

Views can be removed from the FlowLayout using removeManagedSubview:animated:. The first argument of this method asks for the view to remove, you therefore either need to keep a reference to all views that have been added to the FlowLayout or use the managedViews: method. managedViews: returns an NSArray of all views in the order they are arranged in the FlowLayout. This method is also convenient when you want to reorder views.

To programmatically move a single view in the FlowLayout, moveManagedSubview:toIndex: can be used. This methods moves the specified view to the specified index. Should you want to move multiple views at the same time you must use reorderManagedSubviews:animated:. This method allows a new ordering to be supplied to the views currently in the FlowLayout. Note that the content of the NSArray supplied to the first argument of reorderManagedSubviews:animated: must match the content of the array returned by managedViews:.

Delete idioms

The FlowLayout’s ‘edit’ mode can be accessed by long pressing on a view in the FlowLayout. Views can be removed from the FlowLayout whilst in edit mode. The FlowLayout has two idioms which control the style of edit mode and the method used for removal of views by the user. The SEssentialsFlowLayout is initialized with one of these delete idioms.

The first of these idioms is SEssentialsFlowDeleteIdiomIcon, which mimics the behavior of the Apple home screen. Views wobble when in edit mode and a ‘delete’ button is applied to each view. When you tap this button the view is removed from the FlowLayout. The location of the delete button can be controlled with the editButtonLocation property. You can also change the image used for the ‘delete’ button by initializing the FlowLayout’s style property with an image to use.

SEssentialsFlowDeleteIdiomTrashCan is a delete idiom which allows users to drag views to a trash can to remove them. The location of the trash can is controlled with the editButtonLocation property. You can also change the image used for the trash can by initializing the FlowLayout’s style property with an image to use.

The delegate

The SEssentialsFlowLayout has a delegate property which follows the standard UIKit pattern for notifying your application when the user interacts with the FlowLayout. In order to react to user interaction such as removing or dragging items, you need to supply the FlowLayout with a class that adopts the SEssentialsFlowLayoutDelegate protocol.

For most user interactions there are a number of corresponding methods on the delegate protocol. These methods follow the convention:

  • should - delegate methods prefixed with ‘should’ are invoked before a user interaction is processed. The ‘should’ methods give you the opportunity to cancel an interaction. For example, you can use a ‘should’ method to cancel the removal of views in your FlowLayout.
  • 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 FlowLayout 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 FlowLayout state will have changed to reflect the change due to the user interaction.

There are other delegate methods which allow you to further customize how your FlowLayout looks and behaves. flowLayout:deleteIconPositionInView: allows you to specify an exact location for the ‘delete’ icon which appears on views during edit mode. Similarly, editButtonPositionInFlowLayout: allows you to specify a location for the editing button in the FlowLayout.

Styling the FlowLayout

The SEssentialsFlowLayoutStyle defines the look and feel for an instance of the SEssentialsFlowLayout class. If you wish to configure how your flow layout looks, you should modify the properties on this class. You can configure the following visual aspects of the FlowLayout:

  • The background color and texture of the FlowLayout.
  • The color of the trash can when you’re using the trash can delete idiom.
  • The images which are used for the trash can, the done button, and the delete button within the FlowLayout.
  • The amount by which items are scaled when they are selected.

Customizing images within the FlowLayout

A set of images are used within the FlowLayout. These are:

  • A trash can image for when you are using this idiom to delete items in the FlowLayout. You can change the tint of this image, so we have a mask for it as well.
  • Images for the done button when you are using this idiom to end editing. We have an image for the done button in its normal state, and one for when it is pressed.
  • Images for the delete buttons which are displayed on items when you are using this edit idiom. We have an image for the button in its normal state, and one for when it is pressed.

The style contains these images. Once you’ve created the style, the images are readonly. You can customize the images in the style when you construct it.

To do this, you first create an instance of a SEssentialsFlowLayoutImagesWrapper object. This has properties for each of the images in the FlowLayout. You can then set the properties on the images wrapper which you would like to customize. Any properties on the images wrapper which aren’t set will just use the default image. An example is shown below where we customize the trash can image, and the mask which is used when changing the tint of the trash can:

SEssentialsFlowLayoutImagesWrapper *customImagesWrapper = [SEssentialsFlowLayoutImagesWrapper new];
customImagesWrapper.trashcanImage = [UIImage imageNamed:@"myTrashCan.png"];
customImagesWrapper.trashcanMask = [UIImage imageNamed:@"myTrashCanMask.png"];

SEssentialsFlowLayoutStyle *myStyle = [[SEssentialsFlowLayoutStyle alloc] initWithTheme:yourTheme customImages:customImagesWrapper];
SEssentialsFlowLayout *myFlowLayout = [[SEssentialsFlowLayout alloc] initWithFrame:myFrame withDeleteIdiom:myDeleteIdiom style:myStyle];

iOS7 vs. Earlier iOS versions

In the SEssentialsIOS7Theme, we use images very differently from the other themes. 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 Flow Layout, this means the done button and trashcan have been simplified, and the delete buttons use smaller images (while still keeping the large hit-area for taps).

The Done and Delete buttons in iOS7 theme (left) and Dark theme (right)

The Trash icon in iOS7 theme (left) and Dark theme (right)

To create your own custom flow appearance using images for the Trashcan idiom, you will need to provide a trashcanImage and trashcanMask, where the mask is tinted by the trashcanTintColor. To create a custom flow appearance using images for the Icon idiom, you will need to provide a deleteImage and deletePressedImage for the delete buttons over items, and a doneButtonImage and doneButtonPressedImage for the done button. If you only want the entire image of the trash can to be tintable, then provide a blank trashcanImage with the same dimensions as the trashcanMask.

Default style settings

SEssentialsFlowLayoutStyle derives from SEssentialsStyle, and so it is initialized from a SEssentialsTheme object. The theme provides the default settings for the style.

The following default settings are taken from the theme:

  • The primaryTintColor on the theme is used for the background color of the flow layout (mainViewTintColor).
  • The primaryTexture on the theme is used for the background texture of the flow layout (mainViewTexture).
  • The tertiaryTintColor on the theme is used for the color of the trash can in the flow layout (trashCanTintColor).

ShinobiEssentials FlowLayout How-to Guides

How-to sort a ShinobiEssentials FlowLayout

The SEssentialsFlowLayout control allows sorting of the items it manages in several ways:

  • If editable is enabled (default) then a user can long press an item and then drag it around as desired.
  • Items can be individually reordered programmatically.
  • A new ordering can be provided to the flow layout, and it will redraw appropriately.

This how-to will demonstrate how to sort programmatically, and how to provide a new ordering. The majority of the sample project, FlowLayoutHowToSortItems.xcodeproj, is involved in setting up a flow layout with content. There is a custom UIView subclass which represents a tile which displays a large number and a smaller text label. The values associated with these are available as properties.

The FlowLayout and items are created as follows:

CGRect frame = CGRectMake(30, 100, self.view.bounds.size.width-60, self.view.bounds.size.height-130);

flowLayout = [[SEssentialsFlowLayout alloc] initWithFrame:frame withDeleteIdiom:SEssentialsFlowDeleteIdiomTrashCan];
flowLayout.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
// Create some items to add to the flow layout
NSArray *fruit = @[@"banana", @"apple", @"pear", @"cherry", @"tomato",
                   @"grape", @"blackberry", @"blueberry", @"orange", @"grapefruit",
                   @"watermelon", @"kiwi", @"strawberry", @"apricot"];

for(NSUInteger i=0; i<fruit.count; i++) {
    // Create the individual items and add them to the flow layout
    ItemView *item = [[ItemView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) text:fruit[i] index:(i+1)];
    [flowLayout addManagedSubview:item];
}

Running the app up with this FlowLayout added to an appropriate view will allow a user to press and drag each of the tiles to reorder them. If we were interested in acting on these moves we would implement appropriate methods (such as flowLayout:didMoveView:) from the SEssentialsFlowLayoutDelegate protocol.

In order to perform programmatic reordering of the views we add a couple of buttons - one which will represent alphabetical ordering and the other numerical:

// Create an alphabetical sort button
UIButton *sortAlpha = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[sortAlpha setTitle:@"Sort Alphabetically" forState:UIControlStateNormal];
[sortAlpha addTarget:self action:@selector(sortAlphabetically:) forControlEvents:UIControlEventTouchUpInside];
// Create a numerical sort button
UIButton *sortNum = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[sortNum setTitle:@"Sort Numerically" forState:UIControlStateNormal];
[sortNum addTarget:self action:@selector(sortNumerically:) forControlEvents:UIControlEventTouchUpInside];

These buttons are wired up to sort methods. The alphabetic sort looks a bit like this:

- (void)sortAlphabetically:(id)sender
{
    // Get an array of the managed views
    NSMutableArray *subviews = [NSMutableArray arrayWithArray:flowLayout.managedViews];
    // Sort them
    [subviews sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        // We know that they are of type ItemView, and therefore have a text property
        ItemView *iv1 = (ItemView*)obj1;
        ItemView *iv2 = (ItemView*)obj2;
        return [iv1.text compare:iv2.text];
    }];
    // Tell the flow layout the new ordering
    [flowLayout reorderManagedSubviews:subviews animated:YES];
}

There are several important parts to this method:

  1. The managedViews property will return an array of the views it currently manages, in the correct (raster) order.
  2. In our case we know the type of these views, and that it has a text property, which we can use in the sort block to determine the ordering.
  3. The important method is reorderManagedSubviews:animated: on a FlowLayout. This method takes an array of UIView objects and arranges them in the specified order. The content of this array must be identical to the managedViews property - i.e. this method cannot be used to add or remove managed views.

A similar approach is used with the numerical ordering. In the sample app, there is some additional code to make the buttons flip between ascending and descending order, but that is beyond the scope of this how-to.

The source code for this how-to is available in the related sample app: FlowLayoutHowToSortItems.xcodeproj