Create an iOS Flow Layout with Ease Using NucliOS (Obj-C & C#)

Torrey Betts / Friday, April 25, 2014

Introduction

With the release of NucliOS 2014 Volume 1 we've included a flow layout control (IGFlowLayoutView). This control allows you to easily create a dynamically sized list of items that can be scroll horizontal or vertically, similar to the UICollectionView. Like any other Infragistics control, this powerful flow layout can have its style fully customized, cells reordered or even resized.

Introduction

Creating a Flow Layout

The IGFlowLayoutView loads up its data in a similar data source protocol fashion as the IGGridView or Apple's own UITableView. The IGFlowLayoutView requires the implementation of 4 method found in the IGFlowLayoutDataSource protocol.

One of the many great things about the IGFlowLayoutView is that changes when adding, removing or updating cells can be animated. The code example below demonstrates this by calling an update on each cell to provide a histogram equalizer effect, similar to what you see in audio apps.

Objective-C

@interface igViewController () <IGFlowLayoutViewDataSource>
{
    IGFlowLayoutView *_flowLayoutView;
    NSInteger _totalItems;
}
@end

@implementation igViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    _totalItems = 10;
    _flowLayoutView = [[IGFlowLayoutView alloc] initWithFrame:self.view.bounds];
    _flowLayoutView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
    _flowLayoutView.backgroundColor = [UIColor colorWithWhite:0.3 alpha:1.0];
    _flowLayoutView.transitionDuration = 0.75;
    _flowLayoutView.dataSource = self;
    _flowLayoutView.transform = CGAffineTransformMakeRotation(M_PI);
    [self.view addSubview:_flowLayoutView];
    [_flowLayoutView updateData];
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTick) userInfo:nil repeats:YES];
}
- (CGSize)numberOfBlocksInViewport:(IGFlowLayoutView *)flowLayoutView
{
    CGSize result = CGSizeMake(_totalItems, 4);
    return result;
}
- (NSInteger)numberOfItemsInFlowLayoutView:(IGFlowLayoutView *)flowLayoutView
{
    return _totalItems;
}
- (CGSize)flowLayoutView:(IGFlowLayoutView *)flowLayoutView sizeForItemAtIndex:(NSInteger)index1
{
    CGSize result;
    IGFlowLayoutViewCell *cell = [_flowLayoutView cellAtIndex:index1];
    if (!cell)
        result = CGSizeMake(1, 1);
    else
        result = CGSizeMake(1, cell.tag);
    return result;
}
- (IGFlowLayoutViewCell *)flowLayoutView:(IGFlowLayoutView *)flowLayoutView cellAtIndex:(NSInteger)index1
{
    IGFlowLayoutViewCell *cell = [flowLayoutView dequeueReusableCellWithIdentifier:@"CELL"];
    if (!cell)
    {
        cell = [[IGFlowLayoutViewCell alloc] initWithReuseIdentifier:@"CELL"];
        cell.contentInset = UIEdgeInsetsMake(3, 3, 3, 3);
        cell.tag = 0;
        UIView *innerView = [[UIView alloc] init];
        innerView.layer.backgroundColor = [UIColor colorWithRed:238/255.0f green:197/255.0f blue:91/255.0f alpha:1.0f].CGColor;
        innerView.layer.cornerRadius = 10.0;
        innerView.layer.borderWidth = 3.0;
        innerView.layer.borderColor = [UIColor colorWithWhite:0.3 alpha:0.75].CGColor;
        cell.contentView = innerView;
    }
    return cell;
}
- (void)timerTick
{
    NSMutableArray *levels = [[NSMutableArray alloc] init];
    for (int k = 0; k < _totalItems; k++)
    {
        [levels addObject:[NSNumber numberWithInt:arc4random_uniform(4) + 1]];
    }
    for (NSUInteger j = 0; j < _totalItems; j++)
    {
        NSInteger levelForColumn = [[levels objectAtIndex:j] intValue];
        IGFlowLayoutViewCell *cell = [_flowLayoutView cellAtIndex:j];
        cell.tag = levelForColumn;
        [_flowLayoutView updateItemAtIndex:j];
    }
}
@end

C# (Xamarin.iOS)

public class FlowLayoutDataSource : IGFlowLayoutViewDataSource
{
      int _totalItems;
      public FlowLayoutDataSource(int TotalItems)
      {
            _totalItems = TotalItems;
      }
      public override SizeF NumberOfBlocksInViewport (IGFlowLayoutView flowLayoutView)
      {
            SizeF result = new SizeF(_totalItems, 4);
            return result;
      }
      public override int NumberOfItems (IGFlowLayoutView flowLayoutView)
      {
            return _totalItems;
      }
      public override SizeF SizeForItem (IGFlowLayoutView flowLayoutView, int index)
      {
            SizeF result;
            IGFlowLayoutViewCell cell = flowLayoutView.CellAtIndex (index);
            if (cell == null)
                  result = new SizeF (1, 1);
            else
                  result = new SizeF(1, cell.Tag);
            return result;
      }
      public override IGFlowLayoutViewCell CreateCell (IGFlowLayoutView flowLayoutView, int index)
      {
            IGFlowLayoutViewCell cell = flowLayoutView.DequeueReusableCell ("CELL") as IGFlowLayoutViewCell;
            if (cell == null)
            {
                  cell = new IGFlowLayoutViewCell ("CELL");
                  cell.ContentInset = new UIEdgeInsets (3, 3, 3, 3);
                  UIView innerView = new UIView ();
                  innerView.Layer.BackgroundColor = UIColor.FromRGBA (238 / 255.0f, 197 / 255.0f, 91 / 255.0f, 1.0f).CGColor;
                  innerView.Layer.CornerRadius = 10.0f;
                  innerView.Layer.BorderWidth = 3.0f;
                  innerView.Layer.BorderColor = UIColor.FromWhiteAlpha (0.3f, 0.75f).CGColor;
                  cell.ContentView = innerView;
            }
            return cell;
      }
}

public partial class FlowLayoutUpdateItem_CSViewController : UIViewController
{
      IGFlowLayoutView _flowLayoutView;
      int _totalItems;
      public FlowLayoutUpdateItem_CSViewController ()
      {
      }
      public override void ViewDidLoad ()
      {
            base.ViewDidLoad ();
            _totalItems = 10;
            _flowLayoutView = new IGFlowLayoutView ();
            _flowLayoutView.Frame = this.View.Bounds;
            _flowLayoutView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight|UIViewAutoresizing.FlexibleWidth;
            _flowLayoutView.BackgroundColor = UIColor.FromWhiteAlpha (0.3f, 0.75f);
            _flowLayoutView.TransitionDuration = 0.75f;
            _flowLayoutView.DataSource = new FlowLayoutDataSource(_totalItems);
            _flowLayoutView.Transform = CGAffineTransform.MakeRotation((float)Math.PI);
            this.View.Add (_flowLayoutView);
            _flowLayoutView.UpdateData ();
            NSTimer.CreateRepeatingScheduledTimer (1.0f, () => {
                  Random rnd = new Random();
                  List<int> levels = new List<int>();
                  for (int k = 0; k < _totalItems; k++)
                  {
                        levels.Add(rnd.Next(4) + 1);
                  }
                  for (int j = 0; j < _totalItems; j++)
                  {
                        int levelForColumn = levels[j];
                        IGFlowLayoutViewCell cell = _flowLayoutView.CellAtIndex(j);
                        cell.Tag = levelForColumn;
                        _flowLayoutView.UpdateItemAtIndex(j);
                  }
            });
      }
}

The final result when the IGFlowLayoutView renders is shown below.

Creating a Flow Layout

Further Reference

Below are links to the API reference page as well as the developer's guide, NucliOS forums & NucliOS trial.

API Reference for IGFlowLayoutView -http://help.infragistics.com/iOS/2014.1/gridapi/Classes/IGFlowLayoutView.html
Developer's Guide Documentation for IGFlowLayoutView - http://help.infragistics.com/iOS/2014.1/?page=IGFlowLayoutView.html
NucliOS Forums - http://www.infragistics.com/community/forums/default.aspx?GroupID=92
NucliOS Trial - http://www.infragistics.com/products/ios

By Torrey Betts