Skip to content

orta/ORStackView

Repository files navigation

ORStackView

Build Status Coverage Status Version Platform

Makes setting up a collection of stacked views simple. Uses FLKAutoLayout to simplify the API, you should probably be using it anyway. Depending on demand this can be switched out. If you're interested in more information you can read ORStackView.h

Recommendation

It's probably not the best idea to use ORStackView in a new project, given that UIStackView exists, and there are a lot of pods that aim to have a compatible API with that. Some things that ORStackView does better: View Controllers, and internal margins for individual items. However, you'd probably find yourself fighting the grain in the future.

ORStackView

You can create an ORStackView and simply add subviews to it in the order in which you'd like them to appear. New subviews are added to the bottom of the ORStackView. In the this example, tapping the first subview will add a new subview to the bottom of the stack.

- (void)loadView
{
    self.view = [[ORStackView alloc] init];
}

- (void)viewDidLoad
{
  ORColourView *view1 = [[ORColourView alloc] init];
  view1.text = @"ORStackView - Tap Me";
  view1.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 40};

  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(addView)];
  [view1 addGestureRecognizer:tapGesture];

  ORColourView *view2 = [[ORColourView alloc] init];
  view2.text = @"Subtitle";
  view2.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 20 };

  ORColourView *view3 = [[ORColourView alloc] init];
  view3.text = @"By default, new subviews are added to the bottom of ORStackView.";
  view3.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 100 };

  [self.view addSubview:view1 withPrecedingMargin:20 sideMargin:30];
  [self.view addSubview:view2 withPrecedingMargin:40 sideMargin:70];
  [self.view addSubview:view3 withPrecedingMargin:30 sideMargin:20];
}

- (void)addView
{
  ORColourView *view = [[ORColourView alloc] init];
  view.text = @"Tap to remove";
  view.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 24 };
  [self.view addSubview:view withPrecedingMargin:5 sideMargin:40];
}

ORStackView with ordering

If you have views which should only appear once you've got confirmation from an external source, you can add your subviews using insertSubview:atIndex:withPrecedingMargin:, insertSubview:atIndex:withPrecedingMargin:sideMargin:, insertSubview:belowSubview:withPrecedingMargin: or insertSubview:aboveSubview:withPrecedingMargin:

In this example, subviews appear in a different order than they are added chronologically. Tapping the first subview adds a new subview to the middle of the stack.

- (void)loadView
{
    self.view = [[ORStackView alloc] init];
}

- (void)viewDidLoad
{
  ORColourView *view1 = [[ORColourView alloc] init];
  view1.text = @"1 - ORStackView - Tap Me";
  view1.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 40};
  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(addView)];
  [view1 addGestureRecognizer:tapGesture];

  ORColourView *view2 = [[ORColourView alloc] init];
  view2.text = @"2 - You can control the order your ORStackView's subviews";
  view2.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 50 };

  ORColourView *view3 = [[ORColourView alloc] init];
  view3.text = @"3 - Lorem ipsum, etc. etc.";
  view3.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 20 };

  ORColourView *view4 = [[ORColourView alloc] init];
  view4.text = @"4 - Lorem ipsum, etc. etc.";
  view4.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 20 };

  [self.view insertSubview:view2 atIndex:0 withPrecedingMargin:20 sideMargin:20];
  [self.view insertSubview:view4 atIndex:1 withPrecedingMargin:15 sideMargin:20];
  [self.view insertSubview:view1 atIndex:0 withPrecedingMargin:10 sideMargin:20];
  [self.view insertSubview:view3 atIndex:2 withPrecedingMargin:10 sideMargin:20];
}

- (void)addView
{
  ORColourView *view = [[ORColourView alloc] init];
  view.text = @"Tap to remove";
  view.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 24 };
  [self.view addSubview:view withPrecedingMargin:5 sideMargin:40];
}

ORTagBasedAutoStackView

Another option is to use ORTagBasedAutoStackView to order your subviews visually in a different order than you will be adding them chronologically. ORTagBasedAutoStackView uses view tags to specify the order in which views will appear from top to bottom. For example these views will be ordered correctly regardless of the insertion order chronologically. Tapping the first view adds a new view with a tag of 3 to the middle of the stack.

- (void)loadView
{
    self.view = [[ORTagBasedAutoStackView alloc] init];
}

- (void)viewDidLoad
{
  ORColourView *view1 = [[ORColourView alloc] init];
  view1.text = @"Tap Me\ntag = 1";
  view1.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 70};
  view1.tag = 1;

  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(addView)];
  [view1 addGestureRecognizer:tapGesture];

  ORColourView *view2 = [[ORColourView alloc] init];
  view2.text = @"ORTagBasedAutoStackView uses view tags to order your subviews\ntag = 2";
  view2.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 70 };
  view2.tag = 2;

  ORColourView *view4 = [[ORColourView alloc] init];
  view4.text = @"tag = 4";
  view4.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 50 };
  view4.tag = 4;

  ORColourView *view5 = [[ORColourView alloc] init];
  view5.text = @"tag = 5";
  view5.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 60 };
  view5.tag = 5;

  [self.view addSubview:view2 withPrecedingMargin:10 sideMargin:40];
  [self.view addSubview:view5 withPrecedingMargin:20 sideMargin:20];
  [self.view addSubview:view4 withPrecedingMargin:10 sideMargin:20];
  [self.view addSubview:view1 withPrecedingMargin:20 sideMargin:30];
}

- (void)addView
{
  if ([[self.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag = 3"]] count] > 0) return;

  ORColourView *view3 = [[ORColourView alloc] init];
  view3.text = @"tap to remove me\ntag = 3";
  view3.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 50 };
  view3.tag = 3;

  [self.view addSubview:view3 withPrecedingMargin:20 sideMargin:70];
}

ORSplitStackView

ORSplitStackView is a view containing two ORStackView columns. Add subviews to the leftStack and rightStack views. ORSplitStackView adjusts its height to fit the taller of the two stack views.

- (void)loadView
{
  self.view = [[UIView alloc] init];
}

- (void)viewDidLoad
{
  ORSplitStackView *splitView = [[ORSplitStackView alloc] initWithLeftPredicate:@"155" rightPredicate:@"130"];
  [self.view addSubview:splitView];
  [self.view addConstraint:[NSLayoutConstraint constraintWithItem:splitView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
  if ([self respondsToSelector:@selector(topLayoutGuide)]) {
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:splitView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]];
  }

  splitView.backgroundColor = [UIColor purpleColor];
  ORColourView *left1 = [[ORColourView alloc] init];
  left1.text = @"Tap Me";
  left1.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 50};

  ORColourView *right1 = [[ORColourView alloc] init];
  right1.text = @"Tap Me";
  right1.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 60};

  UITapGestureRecognizer *leftGesture = [[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(addView:)];
  [left1 addGestureRecognizer:leftGesture];
  UITapGestureRecognizer *rightGesture = [[UITapGestureRecognizer alloc]
    initWithTarget:self action:@selector(addView:)];
  [right1 addGestureRecognizer:rightGesture];

  ORColourView *left2 = [[ORColourView alloc] init];
  left2.text = @"ORSplitStackView is a view containing two stack views.";
  left2.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 90};

  ORColourView *left3 = [[ORColourView alloc] init];
  left3.text = @"ORSplitStackView adjusts its height to fit its content";
  left3.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 75};

  ORColourView *right2 = [[ORColourView alloc] init];
  right2.text = @"a view";
  right2.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 45};

  ORColourView *right3 = [[ORColourView alloc] init];
  right3.text = @"a view";
  right3.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 40};

  [splitView.leftStack addSubview:left1 withPrecedingMargin:0 sideMargin:10];
  [splitView.leftStack addSubview:left2 withPrecedingMargin:10 sideMargin:5];
  [splitView.leftStack addSubview:left3 withPrecedingMargin:10 sideMargin:15];
  [splitView.rightStack addSubview:right1 withPrecedingMargin:0 sideMargin:15];
  [splitView.rightStack addSubview:right2 withPrecedingMargin:10 sideMargin:10];
  [splitView.rightStack addSubview:right3 withPrecedingMargin:10 sideMargin:5];
}

- (void)addView:(UITapGestureRecognizer *)gesture
{
  ORColourView *view = [[ORColourView alloc] init];
  view.text = @"Tap to remove";
  view.fakeContentSize = (CGSize){ UIViewNoIntrinsicMetric , 24 };
  [(ORStackView *)gesture.view.superview addSubview:view withPrecedingMargin:5 sideMargin:10];
}

Example Usage

pod try ORStackView or to run the example project; clone the repo, and run pod install from the Project directory.

Installation

ORStackView is available through CocoaPods, to install it simply add the following line to your Podfile:

pod "ORStackView"

Author

Orta Therox, orta.therox@gmail.com

License

ORStackView is available under the MIT license. See the LICENSE file for more info.

About

Vertically stack views using Auto Layout, with an order specific subclass that uses view tags for ordering.

Resources

License

Stars

Watchers

Forks

Packages

No packages published