1+ //
2+ // ORHorizontalStackView.m
3+ // ORStackView
4+ //
5+ // Created by Dzianis Lebedzeu on 4/23/15.
6+ //
7+ //
8+
9+ #import " ORHorizontalStackView.h"
10+ #import < FLKAutoLayout/UIView+FLKAutoLayout.h>
11+ #import " ORStackView+Private.h"
12+
13+ @interface ORHorizontalStackView ()
14+
15+ // / Delay updating constraints when true
16+ @property (nonatomic , assign ) BOOL batchingUpdates;
17+ @property (nonatomic , strong ) NSLayoutConstraint *trailingConstraint;
18+
19+
20+ // TODO: move to private
21+
22+ // / An array of StackView objects in the order they should be shown
23+ @property (nonatomic , strong ) NSMutableArray *viewStack;
24+
25+ @end
26+
27+ @implementation ORHorizontalStackView
28+
29+ - (instancetype )initWithFrame : (CGRect)frame
30+ {
31+ self = [super initWithFrame: frame];
32+ if (!self) return nil ;
33+
34+ [self _setup ];
35+
36+ return self;
37+ }
38+
39+ - (instancetype )init
40+ {
41+ return [self initWithFrame: CGRectZero];
42+ }
43+
44+ - (void )awakeFromNib
45+ {
46+ [self _setup ];
47+ }
48+
49+ - (void )_setup
50+ {
51+ _viewStack = [NSMutableArray array ];
52+ _trailingMarginWidth = 0 ;
53+ }
54+
55+ - (void )updateConstraints
56+ {
57+ // Remove all constraints
58+ for (StackView *stackView in self.viewStack ) {
59+ [self removeConstraint: stackView.topConstraint];
60+ }
61+
62+ // Add the new constraints
63+ for (StackView *stackView in self.viewStack ) {
64+ UIView *view = stackView.view ;
65+ NSString *predicate = @(stackView.constant ).stringValue ;
66+ NSInteger index = [self .viewStack indexOfObject: stackView];
67+
68+ if (index == 0 ) {
69+ stackView.topConstraint = [[view alignLeadingEdgeWithView: self predicate: predicate] lastObject ];
70+ } else {
71+ UIView *viewAbove = [self .viewStack[index - 1 ] view ];
72+ stackView.topConstraint = [[view constrainLeadingSpaceToView: viewAbove predicate: predicate] lastObject ];
73+ }
74+ }
75+
76+ if (self.trailingMarginWidth != NSNotFound ) {
77+ [self removeConstraint: self .trailingConstraint];
78+ UIView *lastView = self.lastView ;
79+ if (self.lastView ) {
80+ NSString *constraint = [NSString stringWithFormat: @" %0.0f " , self .trailingMarginWidth];
81+ self.trailingConstraint = [[self alignTrailingEdgeWithView: lastView predicate: constraint] lastObject ];
82+ }
83+ }
84+
85+ [super updateConstraints ];
86+ }
87+
88+ #pragma mark - Adding Subviews
89+
90+ - (void )addSubview : (UIView *)view withTopMargin : (CGFloat)margin
91+ {
92+ [self _addSubview: view withTopMargin: margin centered: NO sideMargin: 0 ];
93+ }
94+
95+ - (void )addSubview : (UIView *)view withTopMargin : (CGFloat)topMargin sideMargin : (CGFloat)sideMargin
96+ {
97+ [self _addSubview: view withTopMargin: topMargin centered: YES sideMargin: sideMargin];
98+ }
99+
100+ - (void )_addSubview : (UIView *)view withTopMargin : (CGFloat)topMargin centered : (BOOL )centered sideMargin : (CGFloat)sideMargin
101+ {
102+ NSInteger index = self.viewStack .count ;
103+ [self _insertSubview: view atIndex: index withTopMargin: topMargin centered: centered sideMargin: sideMargin];
104+ }
105+
106+ #pragma mark - Inserting Subviews
107+
108+ - (void )insertSubview : (UIView *)view atIndex : (NSInteger )index withTopMargin : (CGFloat)margin ;
109+ {
110+ [self _insertSubview: view atIndex: index withTopMargin: margin centered: NO sideMargin: 0 ];
111+ }
112+
113+ - (void )insertSubview : (UIView *)view atIndex : (NSInteger )index withTopMargin : (CGFloat)topMargin sideMargin : (CGFloat)sideMargin
114+ {
115+ [self _insertSubview: view atIndex: index withTopMargin: topMargin centered: YES sideMargin: sideMargin];
116+ }
117+
118+
119+ - (void )insertSubview : (UIView *)view belowSubview : (UIView *)siblingSubview withTopMargin : (CGFloat)margin
120+ {
121+ BOOL hasSibling = [self .subviews containsObject: siblingSubview];
122+ NSInteger index = hasSibling ? [self indexOfView: siblingSubview] : self.viewStack .count ;
123+ [self _insertSubview: view atIndex: index withTopMargin: margin centered: NO sideMargin: 0 ];
124+ }
125+
126+ - (void )insertSubview : (UIView *)view aboveSubview : (UIView *)siblingSubview withTopMargin : (CGFloat)margin
127+ {
128+ NSAssert ([self .subviews containsObject: siblingSubview], @" SiblingSubview not found in ORStackView" );
129+
130+ NSInteger index = [self indexOfView: siblingSubview] - 1 ;
131+ [self _insertSubview: view atIndex: index withTopMargin: margin centered: NO sideMargin: 0 ];
132+ }
133+
134+ - (void )_insertSubview : (UIView *)view atIndex : (NSInteger )index withTopMargin : (CGFloat)topMargin centered : (BOOL )centered sideMargin : (CGFloat)sideMargin
135+ {
136+ NSParameterAssert (view);
137+ if ([self .subviews containsObject: view]) return ;
138+
139+ [super addSubview: view];
140+
141+ StackView *stackView = [[StackView alloc ] init ];
142+ stackView.view = view;
143+ stackView.constant = topMargin;
144+ [self .viewStack insertObject: stackView atIndex: index];
145+
146+ // NSString *vhl = [NSString stringWithFormat:@"V:[topLayoutGuide]-%@-[view]", predicate];
147+ // NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:vhl options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(view, topLayoutGuide)];
148+ // [self addConstraints:constraints];
149+ // stackView.topConstraint = [constraints firstObject];
150+
151+ if (centered) {
152+ // TODO: handle topLayoutGuide here
153+ [view constrainHeightToView: self predicate: @(-sideMargin).stringValue];
154+ [view alignCenterYWithView: self predicate: nil ];
155+ }
156+
157+ if (!self.batchingUpdates ) [self setNeedsUpdateConstraints ];
158+ }
159+
160+ #pragma mark Removal
161+
162+ - (void )removeSubview : (UIView *)subview
163+ {
164+ if (![self .subviews containsObject: subview]) return ;
165+
166+ [subview removeFromSuperview ];
167+
168+ for (StackView *stackView in self.viewStack .copy ) {
169+ if ([subview isEqual: stackView.view]) {
170+ [self .viewStack removeObject: stackView];
171+ }
172+ }
173+
174+ if (!self.batchingUpdates ) [self setNeedsUpdateConstraints ];
175+ }
176+
177+ - (void )removeAllSubviews
178+ {
179+ for (StackView *stackView in [self .viewStack copy ]) {
180+ [self .viewStack removeObject: stackView];
181+ [stackView.view removeFromSuperview ];
182+ }
183+
184+ if (!self.batchingUpdates ) [self setNeedsUpdateConstraints ];
185+ }
186+
187+ #pragma mark Batching
188+
189+ - (void )performBatchUpdates : (void (^)(void ))updates ;
190+ {
191+ NSParameterAssert (updates);
192+
193+ self.batchingUpdates = YES ;
194+ updates ();
195+ self.batchingUpdates = NO ;
196+
197+ [self setNeedsUpdateConstraints ];
198+ }
199+
200+ - (void )addViewController : (UIViewController *)viewController toParent : (UIViewController *)parentViewController withTopMargin : (CGFloat)margin
201+ {
202+ [viewController willMoveToParentViewController: parentViewController];
203+ [parentViewController addChildViewController: viewController];
204+ [self addSubview: viewController.view withTopMargin: margin];
205+ [viewController didMoveToParentViewController: parentViewController];
206+ }
207+
208+ - (void )addViewController : (UIViewController *)viewController toParent : (UIViewController *)parentViewController withTopMargin : (CGFloat)margin sideMargin : (CGFloat)sideMargin ;
209+ {
210+ [viewController willMoveToParentViewController: parentViewController];
211+ [parentViewController addChildViewController: viewController];
212+ [self addSubview: viewController.view withTopMargin: margin sideMargin: sideMargin];
213+ [viewController didMoveToParentViewController: parentViewController];
214+ }
215+
216+
217+ #pragma mark Helper functions
218+
219+ - (NSInteger )indexOfView : (UIView *)view
220+ {
221+ for (StackView *stackView in self.viewStack ) {
222+ if ([view isEqual: stackView.view]) {
223+ return [self .viewStack indexOfObject: stackView];
224+ }
225+ }
226+ return NSNotFound ;
227+ }
228+
229+ - (NSLayoutConstraint *)topConstraintForView : (UIView *)view
230+ {
231+ for (StackView *stackView in self.viewStack ) {
232+ if ([view isEqual: stackView.view]) {
233+ return stackView.topConstraint ;
234+ }
235+ }
236+ return nil ;
237+ }
238+
239+ - (UIView *)lastView
240+ {
241+ return [[self .viewStack lastObject ] view ];
242+ }
243+
244+ #pragma mark - Layout Guides
245+
246+ - (void )setTopLayoutGuide : (id <UILayoutSupport>)topLayoutGuide {
247+ _topLayoutGuide = topLayoutGuide;
248+ [self needsUpdateConstraints ];
249+ }
250+
251+ @end
0 commit comments