Coder Social home page Coder Social logo

orstackview's Introduction

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, [email protected]

License

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

orstackview's People

Contributors

alloy avatar dasmer avatar dblock avatar delebedev avatar dstnbrkr avatar duemunk avatar kylef avatar orta avatar readmecritic avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

orstackview's Issues

Nested ORStackView with different orientation

@orta It seems if i have a nested ORStackView with a different orientation (.Vertical vs .Horizontal) it always constrains this at the top of the view:

Example:

class MainViewController: ORStackViewController {
...

    override func viewDidLoad() {
        self.stackView.direction = .Vertical
        ... add a ton of views here
        let otherStackView = ORStackView(frame: CGRectZero)
        otherStackView.direction = .Horizontal
        otherStackView.addSubview(someOtherView, withPrecedingMargin: 20)
        stackView.addSubView(otherStackView, withPrecedingMargin: 0) //Always at the top of the view

Is a nested ORStackView supported and if so, in different orientations? It seems just fine if I nest one in the same orientation. Let me know. Thanks!

Layout in the example is not working in iOS 8

This has also affected the specs. Of the 5 tabs, the last 2 (which have a UIView as the root view) look fine, the first 3 are squished like this...

orstackviewexample

I can make a PR for this, just wanted to log the issue.

Change margin type from NSString * to CGFloat?

I haven't seen the use case when real predicate was passed to margin param + it looks like exposing implementation details of FLKAutoLayout.

I can make PR if this works for you, @orta

UPD: there are predicates in split view, but they should not be affected though

Stack view doesn't work inside UITableViewCell

Not sure if this is a valid use case for this class but can't see anything to say it isn't so...

I'm trying to stack a few UIViews on top of each other inside a UITableViewCell. I have created a simple example of just one of the issues i'm facing: https://github.com/foulkesjohn/StackViewTest

My actual implementation is a little more complex but I'm getting the same issue in that it just won't layout views on top of one another. Am I using the class is the intended way?

Crash on iOS8

I'm getting a crash when calling [view layoutIfNeeded] or whenever the view calls updateConstraints due self.lastView returning nil.

I have no idea why lastView returns nil but it causes this line to crash:

self.bottomConstraint = [[self alignBottomEdgeWithView:lastView predicate:constraint] lastObject];

Just validating if lastView != nil before calling the method fixes the issue; although I'm sure there's a deeper issue there. For now I'm leaving my code with that "patch" but thought it would be worth to mention it.

If I get some time to check why this is happening (on a deeper level) and I'm able to fix it i'll issue a PR fixing this issue.

Here's the console output of the crash.

*** Assertion failure in -[ORStackView nsli_lowerAttribute:intoExpression:withCoefficient:forConstraint:], /SourceCache/UIKit_Sim/UIKit-3318/NSLayoutConstraint_UIKitAdditions.m:3454

Support for UIWebView

Hello!

I'm facing problems when trying to add a UIWebView into the stackview. There's no way to see it, even if I add it before of loading the HTML, or after.

Does ORStackView support UIWebView?
Which steps should I do in order to make it work?

Thanks!

Create an ORStackViewController

We do a lot of this:

ARFairSearchViewController *searchVC = [[ARFairSearchViewController alloc] init];
[self addChildViewController:searchVC];
[self.view.stackView addSubview:searchVC.view withTopMargin:@"10" sideMargin:@"50"];
[searchVC didMoveToParentViewController:self];

Remove FLKAutoLayout

There's only 3-4 uses of it in the entire app, think this could be an easy unbundling win

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.