diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 6c50acd..6ac1ce0 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -206,6 +206,8 @@ E91726963A1D70D65A0C6428 /* KWRespondToSelectorMatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CA8AD367C971DD82427F4AE /* KWRespondToSelectorMatcher.m */; settings = {COMPILER_FLAGS = "-DOS_OBJECT_USE_OBJC=0"; }; }; E943786F62DF61D7A6454356 /* KWBeMemberOfClassMatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 0EFCED26B5D9A256C2B1F238 /* KWBeMemberOfClassMatcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; E961F1E03FB1C2349361DD43 /* KWProbe.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ED94E0B9B0394509B3E09C5 /* KWProbe.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E98AFA0C1B3C336D00CDF427 /* OAStackView_ArrangedSubviews.h in Headers */ = {isa = PBXBuildFile; fileRef = E98AFA0B1B3C336D00CDF427 /* OAStackView_ArrangedSubviews.h */; }; + E98AFA0E1B3C337400CDF427 /* OAStackView_ArrangedSubviews.h in Headers */ = {isa = PBXBuildFile; fileRef = E98AFA0B1B3C336D00CDF427 /* OAStackView_ArrangedSubviews.h */; }; EB6342DDFC16EA1697673367 /* NSInvocation+OCMAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 87EF738D1C4A43D46DF7218C /* NSInvocation+OCMAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; EBA3CB1AFC1C14BE0CE682AD /* KWRegisterMatchersNode.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC8D3E191EA0619F7399012 /* KWRegisterMatchersNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; EBD4862464FD997A831166EA /* KWSuiteConfigurationBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 30DA7C8C1EFC416457FC8B42 /* KWSuiteConfigurationBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -498,6 +500,7 @@ E91B86F74BD64D5D3108D23A /* Pods-OAStackView_Example-OAStackView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-OAStackView_Example-OAStackView-umbrella.h"; sourceTree = ""; }; E96052DEDBABAD6DE67AB9CC /* KWVerifying.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = KWVerifying.h; path = Classes/Verifiers/KWVerifying.h; sourceTree = ""; }; E9763017ABDD60733231C7B9 /* KWBeforeEachNode.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = KWBeforeEachNode.m; path = Classes/Nodes/KWBeforeEachNode.m; sourceTree = ""; }; + E98AFA0B1B3C336D00CDF427 /* OAStackView_ArrangedSubviews.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAStackView_ArrangedSubviews.h; sourceTree = ""; }; E9DCF701A29E982F7F76150C /* Pods-OAStackView_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-OAStackView_Example.release.xcconfig"; sourceTree = ""; }; EC4FD93B76378B54BA02E9D3 /* KWGenericMatchEvaluator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = KWGenericMatchEvaluator.m; path = Classes/Matchers/KWGenericMatchEvaluator.m; sourceTree = ""; }; EE003196DB6E4BE2B7583341 /* Pods-OAStackView_Tests-OAStackView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OAStackView_Tests-OAStackView.xcconfig"; path = "../Pods-OAStackView_Tests-OAStackView/Pods-OAStackView_Tests-OAStackView.xcconfig"; sourceTree = ""; }; @@ -586,6 +589,7 @@ children = ( 8B90E6BD21591A4585962496 /* OAStackView.h */, 5D2F4F1397A17E203EA8112B /* OAStackView.m */, + E98AFA0B1B3C336D00CDF427 /* OAStackView_ArrangedSubviews.h */, C809B772AB6D8B7D66C7D8A2 /* OAStackView+Constraint.h */, E822B703CD09D5556B8F8265 /* OAStackView+Constraint.m */, E86334892F73798F2B097D5F /* OAStackView+Hiding.h */, @@ -1067,6 +1071,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + E98AFA0E1B3C337400CDF427 /* OAStackView_ArrangedSubviews.h in Headers */, D69542A762933D4119F4037C /* OAStackView+Constraint.h in Headers */, 05794B5F4247E3A648C40B51 /* OAStackView+Hiding.h in Headers */, B1868D729956D7DEC9C9FE06 /* OAStackView+Traversal.h in Headers */, @@ -1089,6 +1094,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + E98AFA0C1B3C336D00CDF427 /* OAStackView_ArrangedSubviews.h in Headers */, 06EC4010B3D981437FC3D92B /* OAStackView+Constraint.h in Headers */, 19308B2A9CB35F4AD26FD0CF /* OAStackView+Hiding.h in Headers */, 46CA8779A92EBC2D28E4C1EC /* OAStackView+Traversal.h in Headers */, diff --git a/Pod/Classes/OAStackView+Constraint.m b/Pod/Classes/OAStackView+Constraint.m index d4e7582..b5e5e25 100644 --- a/Pod/Classes/OAStackView+Constraint.m +++ b/Pod/Classes/OAStackView+Constraint.m @@ -21,8 +21,7 @@ - (NSArray*)constraintsBetweenView:(UIView*)firstView andView:(UIView*)otherView viewMatches = viewMatches || (firstView == constraint.secondItem && otherView == constraint.firstItem); } - BOOL isCorrectAxis = [self isConstraintAttribute:constraint.firstAttribute affectingAxis:axis] || - [self isConstraintAttribute:constraint.secondAttribute affectingAxis:axis]; + BOOL isCorrectAxis = [self isConstraint:constraint affectingAxis:axis]; if (viewMatches && isCorrectAxis) { [arr addObject:constraint]; @@ -141,15 +140,29 @@ - (BOOL)isConstraintAttribute:(NSLayoutAttribute)attribute affectingAxis:(UILayo case UILayoutConstraintAxisHorizontal: return attribute == NSLayoutAttributeLeft || attribute == NSLayoutAttributeRight || attribute == NSLayoutAttributeLeading || attribute == NSLayoutAttributeTrailing || - attribute == NSLayoutAttributeCenterX || attribute == NSLayoutAttributeWidth; + attribute == NSLayoutAttributeCenterX || attribute == NSLayoutAttributeWidth +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1 + || attribute == NSLayoutAttributeLeftMargin || attribute == NSLayoutAttributeRightMargin || + attribute == NSLayoutAttributeLeadingMargin || attribute == NSLayoutAttributeTrailingMargin || + attribute == NSLayoutAttributeCenterXWithinMargins +#endif + ; break; case UILayoutConstraintAxisVertical: return attribute == NSLayoutAttributeTop || attribute == NSLayoutAttributeBottom || - attribute == NSLayoutAttributeCenterY || attribute == NSLayoutAttributeHeight; + attribute == NSLayoutAttributeCenterY || attribute == NSLayoutAttributeHeight || + attribute == NSLayoutAttributeBaseline +#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1 + || attribute == NSLayoutAttributeLastBaseline || attribute == NSLayoutAttributeFirstBaseline || + attribute == NSLayoutAttributeTopMargin || attribute == NSLayoutAttributeBottomMargin || + attribute == NSLayoutAttributeCenterYWithinMargins +#endif + ; break; default: + return NO; break; } } diff --git a/Pod/Classes/OAStackView+Hiding.m b/Pod/Classes/OAStackView+Hiding.m index 6f4774f..73687ee 100644 --- a/Pod/Classes/OAStackView+Hiding.m +++ b/Pod/Classes/OAStackView+Hiding.m @@ -51,16 +51,4 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } -#pragma mark subviews - -- (void)didAddSubview:(UIView *)subview { - [super didAddSubview:subview]; - [self addObserverForView:subview]; -} - -- (void)willRemoveSubview:(UIView *)subview { - [super willRemoveSubview:subview]; - [self removeObserverForView:subview]; -} - @end diff --git a/Pod/Classes/OAStackView+Traversal.h b/Pod/Classes/OAStackView+Traversal.h index 92c4007..0f6610c 100644 --- a/Pod/Classes/OAStackView+Traversal.h +++ b/Pod/Classes/OAStackView+Traversal.h @@ -10,15 +10,13 @@ @interface OAStackView (Traversal) -- (UIView*)visibleViewBeforeIndex:(NSInteger)index; -- (UIView*)visibleViewBeforeView:(UIView*)view; +- (UIView*)arrangedSubviewBeforeIndex:(NSInteger)index; +- (UIView*)arrangedSubviewBeforeView:(UIView*)view; -- (UIView*)visibleViewAfterIndex:(NSInteger)index; -- (UIView*)visibleViewAfterView:(UIView*)view; +- (UIView*)arrangedSubviewAfterIndex:(NSInteger)index; +- (UIView*)arrangedSubviewAfterView:(UIView*)view; -- (void)iterateVisibleViews:(void (^) (UIView *view, UIView *previousView))block; - -- (UIView*)lastVisibleItem; +- (void)iterateArrangedSubviews:(void (^) (UIView *view, UIView *previousView))block; - (NSLayoutConstraint*)firstViewConstraint; - (NSLayoutConstraint*)lastViewConstraint; diff --git a/Pod/Classes/OAStackView+Traversal.m b/Pod/Classes/OAStackView+Traversal.m index 234606b..e56d1d3 100644 --- a/Pod/Classes/OAStackView+Traversal.m +++ b/Pod/Classes/OAStackView+Traversal.m @@ -7,72 +7,50 @@ // #import "OAStackView+Traversal.h" +#import "OAStackView_ArrangedSubviews.h" @implementation OAStackView (Traversal) -- (UIView*)visibleViewBeforeView:(UIView*)view { - NSInteger index = [self.subviews indexOfObject:view]; +- (UIView*)arrangedSubviewBeforeView:(UIView*)view { + NSInteger index = [self indexInArrangedSubviewsOfObject:view]; if (index == NSNotFound) { return nil; } - return [self visibleViewBeforeIndex:index]; + return [self arrangedSubviewBeforeIndex:index]; } -- (UIView*)visibleViewAfterView:(UIView*)view { - NSInteger index = [self.subviews indexOfObject:view]; +- (UIView*)arrangedSubviewAfterView:(UIView*)view { + NSInteger index = [self indexInArrangedSubviewsOfObject:view]; if (index == NSNotFound) { return nil; } - return [self visibleViewAfterIndex:index]; + return [self arrangedSubviewAfterIndex:index]; } -- (UIView*)visibleViewAfterIndex:(NSInteger)index { - for (NSInteger i = index + 1; i < self.subviews.count; i++) { - UIView *theView = self.subviews[i]; - if (!theView.hidden) { - return theView; - } +- (UIView*)arrangedSubviewAfterIndex:(NSInteger)index { + if ((index + 1) < [self countOfArrangedSubviews]) { + return [self objectInArrangedSubviewsAtIndex:index+1]; } return nil; } -- (UIView*)visibleViewBeforeIndex:(NSInteger)index { - for (NSInteger i = index - 1; i >= 0; i--) { - UIView *theView = self.subviews[i]; - if (!theView.hidden) { - return theView; - } +- (UIView*)arrangedSubviewBeforeIndex:(NSInteger)index { + if (index > 0) { + return [self objectInArrangedSubviewsAtIndex:index-1]; } return nil; } -- (UIView*)lastVisibleItem { - return [self visibleViewBeforeIndex:self.subviews.count]; -} - -- (void)iterateVisibleViews:(void (^) (UIView *view, UIView *previousView))block { - - id previousView; - for (UIView *view in self.subviews) { - if (view.isHidden) { continue; } +- (void)iterateArrangedSubviews:(void (^) (UIView *view, UIView *previousView))block { + UIView *previousView = nil; + for (NSUInteger i = 0; i < [self countOfArrangedSubviews]; i++) { + UIView *view = [self objectInArrangedSubviewsAtIndex:i]; block(view, previousView); previousView = view; } } -- (NSArray*)currentVisibleViews { - NSMutableArray *arr = [@[] mutableCopy]; - [self iterateVisibleViews:^(UIView *view, UIView *previousView) { - [arr addObject:view]; - }]; - return arr; -} - -- (BOOL)isLastVisibleItem:(UIView*)view { - return view == [self lastVisibleItem]; -} - - (NSLayoutConstraint*)lastViewConstraint { for (NSLayoutConstraint *constraint in self.constraints) { @@ -113,7 +91,7 @@ - (NSLayoutConstraint*)firstViewConstraint { } - (BOOL)isViewLastItem:(UIView*)view excludingItem:(UIView*)excludingItem { - NSArray *visible = [self currentVisibleViews]; + NSArray *visible = [self arrangedSubviews]; NSInteger index = [visible indexOfObject:view]; NSInteger exclutedIndex = [visible indexOfObject:excludingItem]; diff --git a/Pod/Classes/OAStackView.h b/Pod/Classes/OAStackView.h index 4a358d6..d1c20df 100644 --- a/Pod/Classes/OAStackView.h +++ b/Pod/Classes/OAStackView.h @@ -76,13 +76,16 @@ typedef NS_ENUM(NSInteger, OAStackViewAlignment) { #define NS_ASSUME_NONNULL_BEGIN #define NS_ASSUME_NONNULL_END #define nullable -#define nonnullable +#define nonnull #define __nullable +#define __nonnull #endif NS_ASSUME_NONNULL_BEGIN @interface OAStackView : UIView +// The views that are currently being arranged. +// Views that are hidden will be removed from this list @property(nonatomic,readonly,copy) NSArray *arrangedSubviews; //Default is Vertical diff --git a/Pod/Classes/OAStackView.m b/Pod/Classes/OAStackView.m index ed2aa82..91849ee 100644 --- a/Pod/Classes/OAStackView.m +++ b/Pod/Classes/OAStackView.m @@ -7,6 +7,7 @@ // #import "OAStackView.h" +#import "OAStackView_ArrangedSubviews.h" #import "OAStackView+Constraint.h" #import "OAStackView+Hiding.h" #import "OAStackView+Traversal.h" @@ -14,7 +15,8 @@ #import "OAStackViewDistributionStrategy.h" @interface OAStackView () -@property(nonatomic, copy) NSArray *arrangedSubviews; + +@property (nonatomic) NSMutableArray *mutableArrangedSubviews; @property(nonatomic) OAStackViewAlignmentStrategy *alignmentStrategy; @property(nonatomic) OAStackViewDistributionStrategy *distributionStrategy; @@ -33,17 +35,26 @@ - (instancetype)initWithCoder:(NSCoder *)coder { if (self) { [self commonInit]; + [self addAllSubviewsAsArrangedSubviews]; } return self; } +- (instancetype)init { + self = [self initWithArrangedSubviews:@[]]; + if (self) { + + } + return self; +} + - (instancetype)initWithArrangedSubviews:(NSArray*)views { self = [super initWithFrame:CGRectZero]; if (self) { - [self addViewsAsSubviews:views]; [self commonInit]; + [self addArrangedSubviews:views]; } return self; @@ -54,6 +65,7 @@ - (instancetype)initWithFrame:(CGRect)frame { } - (void)commonInit { + _mutableArrangedSubviews = [NSMutableArray new]; _axis = UILayoutConstraintAxisVertical; _alignment = OAStackViewAlignmentFill; _distribution = OAStackViewDistributionFill; @@ -64,6 +76,12 @@ - (void)commonInit { [self layoutArrangedViews]; } +- (void)addAllSubviewsAsArrangedSubviews { + for (UIView *view in self.subviews) { + [self addArrangedSubview:view]; + } +} + #pragma mark - Properties - (void)setBackgroundColor:(UIColor *)backgroundColor { @@ -84,8 +102,8 @@ - (void)setSpacing:(CGFloat)spacing { (constraint.firstAttribute == NSLayoutAttributeWidth) || (constraint.firstAttribute == NSLayoutAttributeHeight); - if ([self.subviews containsObject:constraint.firstItem] && - [self.subviews containsObject:constraint.secondItem] && + if ([_mutableArrangedSubviews containsObject:constraint.firstItem] && + [_mutableArrangedSubviews containsObject:constraint.secondItem] && !isWidthOrHeight) { constraint.constant = spacing; } @@ -114,7 +132,7 @@ - (void)setAlignment:(OAStackViewAlignment)alignment { [self.alignmentStrategy removeAddedConstraints]; self.alignmentStrategy = [OAStackViewAlignmentStrategy strategyWithStackView:self]; - [self iterateVisibleViews:^(UIView *view, UIView *previousView) { + [self iterateArrangedSubviews:^(UIView *view, UIView *previousView) { [self.alignmentStrategy addConstraintsOnOtherAxis:view]; }]; } @@ -135,7 +153,7 @@ - (void)setDistribution:(OAStackViewDistribution)distribution { self.alignmentStrategy = [OAStackViewAlignmentStrategy strategyWithStackView:self]; self.distributionStrategy = [OAStackViewDistributionStrategy strategyWithStackView:self]; - [self iterateVisibleViews:^(UIView *view, UIView *previousView) { + [self iterateArrangedSubviews:^(UIView *view, UIView *previousView) { [self.alignmentStrategy addConstraintsOnOtherAxis:view]; [self.distributionStrategy alignView:view afterView:previousView]; }]; @@ -150,96 +168,68 @@ - (void)setDistributionValue:(NSInteger)distributionValue { - (void)layoutSubviews { [super layoutSubviews]; - [self invalidateIntrinsicContentSize]; } -- (CGSize)intrinsicContentSize { - CGSize size = [super intrinsicContentSize]; - - __block float maxSize = 0; - - [self iterateVisibleViews:^(UIView *view, UIView *previousView) { - if (self.axis == UILayoutConstraintAxisVertical) { - maxSize = fmaxf(maxSize, CGRectGetWidth(view.frame)); - } else { - maxSize = fmaxf(maxSize, CGRectGetHeight(view.frame)); - } - }]; - - if (self.axis == UILayoutConstraintAxisVertical) { - size.width = maxSize; - } else { - size.height = maxSize; +#pragma mark - Adding and removing + +- (void)addArrangedSubviews:(NSArray *)views { + for (UIView *view in views) { + [self addArrangedSubview:view]; } - - return size; } -#pragma mark - Adding and removing - - (void)addArrangedSubview:(UIView *)view { - [self insertArrangedSubview:view atIndex:self.subviews.count]; + [self insertArrangedSubview:view atIndex:[self countOfArrangedSubviews]]; } -- (void)removeArrangedSubview:(UIView *)view { +- (void)insertArrangedSubview:(UIView * __nonnull)view atIndex:(NSUInteger)stackIndex { + NSUInteger previousIndex = [self indexInArrangedSubviewsOfObject:view]; + NSUInteger newIndex = stackIndex; - if (self.subviews.count == 1) { - [view removeFromSuperview]; - return; - } + if (previousIndex == newIndex) { return; } - [self removeViewFromArrangedViews:view permanently:YES]; + if (previousIndex != NSNotFound) { + // view was already arranged, remove first + [self removeArrangedSubview:view]; + if (previousIndex < newIndex) { + newIndex--; + } + } + + [self addSubview:view]; + [self insertObject:view inArrangedSubviewsAtIndex:newIndex]; } -- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex { - [self insertArrangedSubview:view atIndex:stackIndex newItem:YES]; +- (void)removeArrangedSubview:(UIView *)view { + NSUInteger index = [_mutableArrangedSubviews indexOfObject:view]; + if (index != NSNotFound) { + [self removeObjectFromArrangedSubviewsAtIndex:index]; + } } -- (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex newItem:(BOOL)newItem { +- (void)didAddArrangedSubview:(UIView *)view { + NSAssert([_mutableArrangedSubviews containsObject:view] && [self.subviews containsObject:view], @"View should already be added as subview and arranged view"); + NSUInteger stackIndex = [_mutableArrangedSubviews indexOfObject:view]; - id previousView, nextView; + UIView *previousView = [self arrangedSubviewBeforeIndex:stackIndex]; + UIView *nextView = [self arrangedSubviewAfterIndex:stackIndex]; view.translatesAutoresizingMaskIntoConstraints = NO; - BOOL isAppending = stackIndex == self.subviews.count; - if (isAppending) { + if (stackIndex == [self countOfArrangedSubviews]-1) { //Appending a new item - - previousView = [self lastVisibleItem]; - nextView = nil; - + NSArray *constraints = [self lastConstraintAffectingView:self andView:previousView inAxis:self.axis]; [self removeConstraints:constraints]; - - if (newItem) { - [self addSubview:view]; - } - + } else if (stackIndex == 0) { + // Prepending a new item + NSArray *constraints = [self firstConstraintAffectingView:self andView:nextView inAxis:self.axis]; + [self removeConstraints:constraints]; } else { - //Item insertion - - previousView = [self visibleViewBeforeIndex:stackIndex]; - nextView = [self visibleViewAfterIndex:newItem ? stackIndex - 1: stackIndex]; - - NSArray *constraints; - BOOL isLastVisibleItem = [self isViewLastItem:previousView excludingItem:view]; - BOOL isFirstVisibleView = previousView == nil; - BOOL isOnlyItem = previousView == nil && nextView == nil; - - if (isLastVisibleItem) { - constraints = @[[self lastViewConstraint]]; - } else if(isOnlyItem) { - constraints = [self constraintsBetweenView:previousView ?: self andView:nextView ?: self inAxis:self.axis]; - } else if(isFirstVisibleView) { - constraints = @[[self firstViewConstraint]]; - } else { - constraints = [self constraintsBetweenView:previousView ?: self andView:nextView ?: self inAxis:self.axis]; - } + NSAssert(previousView && nextView, @"Should have both a next and previous view"); + //Item insertion, never as last or first view + NSArray *constraints = [self constraintsBetweenView:previousView andView:nextView inAxis:self.axis]; [self removeConstraints:constraints]; - - if (newItem) { - [self insertSubview:view atIndex:stackIndex]; - } } [self.distributionStrategy alignView:view afterView:previousView]; @@ -247,36 +237,42 @@ - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex newI [self.distributionStrategy alignView:nextView afterView:view]; } -- (void)removeViewFromArrangedViews:(UIView*)view permanently:(BOOL)permanently { - NSInteger index = [self.subviews indexOfObject:view]; - if (index == NSNotFound) { return; } - - id previousView = [self visibleViewBeforeView:view]; - id nextView = [self visibleViewAfterView:view]; +- (void)willRemoveArrangedSubview:(UIView *)view { + id previousView = [self arrangedSubviewBeforeView:view]; + id nextView = [self arrangedSubviewAfterView:view]; - if (permanently) { - [view removeFromSuperview]; - } else { - NSArray *constraint = [self constraintsAffectingView:view]; - [self removeConstraints:constraint]; - } + NSArray *constraints = [self constraintsAffectingView:view]; + [self removeConstraints:constraints]; - if (nextView) { - [self.distributionStrategy alignView:nextView afterView:previousView]; - } else if(previousView) { - [self.distributionStrategy alignView:nil afterView:[self lastVisibleItem]]; - } + [self.distributionStrategy alignView:nextView afterView:previousView]; } #pragma mark - Hide and Unhide - (void)hideView:(UIView*)view { - [self removeViewFromArrangedViews:view permanently:NO]; + [self removeArrangedSubview:view]; } - (void)unHideView:(UIView*)view { + // insert the view just before the next arranged view + NSInteger index = [self.subviews indexOfObject:view]; - [self insertArrangedSubview:view atIndex:index newItem:NO]; + NSAssert(index != NSNotFound, @"Cannot handle a view that is becoming visible that is not our subview"); + if (index != NSNotFound) { + UIView *nextArrangedSubview = nil; + NSUInteger i = index+1; + for (; i < [self.subviews count] && !nextArrangedSubview; i++) { + UIView *view = self.subviews[i]; + if ([_mutableArrangedSubviews containsObject:view]) { + nextArrangedSubview = view; + } + } + if (nextArrangedSubview) { + [self insertArrangedSubview:view atIndex:[self indexInArrangedSubviewsOfObject:nextArrangedSubview]]; + } else { + [self addArrangedSubview:view]; + } + } } #pragma mark - Align View @@ -284,19 +280,76 @@ - (void)unHideView:(UIView*)view { - (void)layoutArrangedViews { [self removeDecendentConstraints]; - [self iterateVisibleViews:^(UIView *view, UIView *previousView) { + [self iterateArrangedSubviews:^(UIView *view, UIView *previousView) { [self.distributionStrategy alignView:view afterView:previousView]; [self.alignmentStrategy addConstraintsOnOtherAxis:view]; }]; - [self.distributionStrategy alignView:nil afterView:[self lastVisibleItem]]; + [self.distributionStrategy alignView:nil afterView:[self lastArrangedSubview]]; } -- (void)addViewsAsSubviews:(NSArray*)views { - for (UIView *view in views) { - view.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:view]; +- (void)didAddSubview:(UIView *)subview { + [self addObserverForView:subview]; +} + +- (void)willRemoveSubview:(UIView *)subview { + [self removeArrangedSubview:subview]; + [self removeObserverForView:subview]; +} + +- (UIView *)viewForBaselineLayout { + UIView *res = nil; + if (self.axis == UILayoutConstraintAxisHorizontal) { + for (UIView *arrangedView in _mutableArrangedSubviews) { + if (!res || CGRectGetHeight(res.frame) < CGRectGetHeight(arrangedView.frame)) { + res = arrangedView; + } + } + + } else { + res = [self lastArrangedSubview]; + } + + if ([res isKindOfClass:[self class]]) { + res = [res viewForBaselineLayout]; } + return res; +} + +#pragma mark KVO-compatible Mutable Indexed Accessors for arrangedSubviews +- (NSUInteger)countOfArrangedSubviews { + return [_mutableArrangedSubviews count]; +} + +- (UIView * __nonnull)objectInArrangedSubviewsAtIndex:(NSUInteger)index { + return [_mutableArrangedSubviews objectAtIndex:index]; +} + +- (NSUInteger)indexInArrangedSubviewsOfObject:(UIView * __nonnull)view { + return [_mutableArrangedSubviews indexOfObject:view]; +} + +- (void)insertObject:(UIView * __nonnull)object inArrangedSubviewsAtIndex:(NSUInteger)index { + [_mutableArrangedSubviews insertObject:object atIndex:index]; + [self didAddArrangedSubview:object]; +} + +- (void)removeObjectFromArrangedSubviewsAtIndex:(NSUInteger)index { + UIView *view = _mutableArrangedSubviews[index]; + [self willRemoveArrangedSubview:view]; + [_mutableArrangedSubviews removeObjectAtIndex:index]; +} + +- (NSArray * __nonnull)arrangedSubviews { + return [_mutableArrangedSubviews copy]; +} + +- (UIView * __nullable)firstArrangedSubview { + return [_mutableArrangedSubviews firstObject]; +} + +- (UIView * __nullable)lastArrangedSubview { + return [_mutableArrangedSubviews lastObject]; } @end diff --git a/Pod/Classes/OAStackViewAlignmentStrategy.m b/Pod/Classes/OAStackViewAlignmentStrategy.m index 631ab5a..7e7778e 100644 --- a/Pod/Classes/OAStackViewAlignmentStrategy.m +++ b/Pod/Classes/OAStackViewAlignmentStrategy.m @@ -113,7 +113,7 @@ @implementation OAStackViewAlignmentStrategyLeading - (NSArray*)constraintsalignViewOnOtherAxis:(UIView*)view { - id constraintString = [NSString stringWithFormat:@"%@:|-0-[view]", [self otherAxisString]]; + id constraintString = [NSString stringWithFormat:@"%@:|-0-[view]-(>=0)-|", [self otherAxisString]]; return [NSLayoutConstraint constraintsWithVisualFormat:constraintString options:0 @@ -127,7 +127,7 @@ @implementation OAStackViewAlignmentStrategyTrailing - (NSArray*)constraintsalignViewOnOtherAxis:(UIView*)view { - id constraintString = [NSString stringWithFormat:@"%@:[view]-0-|", [self otherAxisString]]; + id constraintString = [NSString stringWithFormat:@"%@:|-(>=0)-[view]-0-|", [self otherAxisString]]; return [NSLayoutConstraint constraintsWithVisualFormat:constraintString options:0 @@ -141,12 +141,22 @@ @implementation OAStackViewAlignmentStrategyCenter - (NSArray*)constraintsalignViewOnOtherAxis:(UIView*)view { - return @[[NSLayoutConstraint constraintWithItem:view - attribute:[self centerAttribute] - relatedBy:NSLayoutRelationEqual - toItem:view.superview - attribute:[self centerAttribute] - multiplier:1 constant:0]]; + NSString *constraintString = [NSString stringWithFormat:@"%@:|-(>=0)-[view]-(>=0)-|", [self otherAxisString]]; + + NSArray *edgeConstraints = [NSLayoutConstraint constraintsWithVisualFormat:constraintString + options:0 + metrics:nil + views:NSDictionaryOfVariableBindings(view)]; + + NSLayoutConstraint *centerContraint = [NSLayoutConstraint constraintWithItem:view + attribute:[self centerAttribute] + relatedBy:NSLayoutRelationEqual + toItem:view.superview + attribute:[self centerAttribute] + multiplier:1 + constant:0]; + + return [edgeConstraints arrayByAddingObject:centerContraint]; } @end diff --git a/Pod/Classes/OAStackView_ArrangedSubviews.h b/Pod/Classes/OAStackView_ArrangedSubviews.h new file mode 100644 index 0000000..d37ffdc --- /dev/null +++ b/Pod/Classes/OAStackView_ArrangedSubviews.h @@ -0,0 +1,27 @@ +// +// OAStackView_ArrangedSubviews.h +// Pods +// +// Created by Thomas Visser on 25/06/15. +// +// + +#import + +@interface OAStackView () + +- (NSUInteger)countOfArrangedSubviews; + +- (UIView* __nonnull)objectInArrangedSubviewsAtIndex:(NSUInteger)index; + +- (NSUInteger)indexInArrangedSubviewsOfObject:(UIView * __nonnull)view; + +- (void)insertObject:(UIView * __nonnull)object inArrangedSubviewsAtIndex:(NSUInteger)index; + +- (void)removeObjectFromArrangedSubviewsAtIndex:(NSUInteger)index; + +- (UIView * __nullable)firstArrangedSubview; + +- (UIView * __nullable)lastArrangedSubview; + +@end