diff --git a/Example/OAStackView.xcodeproj/project.pbxproj b/Example/OAStackView.xcodeproj/project.pbxproj index ec674be..a8704cd 100644 --- a/Example/OAStackView.xcodeproj/project.pbxproj +++ b/Example/OAStackView.xcodeproj/project.pbxproj @@ -52,7 +52,7 @@ 6003F59C195388D20070C39A /* OAAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAAppDelegate.h; sourceTree = ""; }; 6003F59D195388D20070C39A /* OAAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OAAppDelegate.m; sourceTree = ""; }; 6003F5A5195388D20070C39A /* OAViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAViewController.h; sourceTree = ""; }; - 6003F5A6195388D20070C39A /* OAViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OAViewController.m; sourceTree = ""; }; + 6003F5A6195388D20070C39A /* OAViewController.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = OAViewController.m; sourceTree = ""; tabWidth = 2; }; 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 6003F5AE195388D20070C39A /* OAStackView_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OAStackView_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; diff --git a/Example/OAStackView/Main.storyboard b/Example/OAStackView/Main.storyboard index f1a0129..cf5c40e 100644 --- a/Example/OAStackView/Main.storyboard +++ b/Example/OAStackView/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -35,18 +35,18 @@ - + + + + + + + + + + + + + - + @@ -204,8 +256,11 @@ + + + diff --git a/Example/OAStackView/OAViewController.m b/Example/OAStackView/OAViewController.m index 926cb53..35bc185 100644 --- a/Example/OAStackView/OAViewController.m +++ b/Example/OAStackView/OAViewController.m @@ -80,4 +80,24 @@ - (IBAction)alignmentLeading:(UIButton*)sender { self.stackView.alignment = OAStackViewAlignmentLeading; } +- (IBAction)distributionFill:(UIButton *)sender { + self.stackView.distribution = OAStackViewDistributionFill; +} + +- (IBAction)distributionFillEqually:(UIButton *)sender { + self.stackView.distribution = OAStackViewDistributionFillEqually; +} + +- (IBAction)distributionFillProportionally:(UIButton *)sender { + self.stackView.distribution = OAStackViewDistributionFillProportionally; +} + +- (IBAction)distributionEqualSpacing:(UIButton *)sender { + self.stackView.distribution = OAStackViewDistributionEqualSpacing; +} + +- (IBAction)distributionEqualCentering:(UIButton *)sender { + self.stackView.distribution = OAStackViewDistributionEqualCentering; +} + @end diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 202c15c..6c50acd 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -446,7 +446,7 @@ BEC5A0107A7595E804369111 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; C048736C9A32352B8514854A /* KWSpec.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = KWSpec.h; path = Classes/Core/KWSpec.h; sourceTree = ""; }; C0D097D73D139EE911875D38 /* KWBeforeAllNode.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = KWBeforeAllNode.h; path = Classes/Nodes/KWBeforeAllNode.h; sourceTree = ""; }; - C23A1311AA2C1EA1D567AE58 /* OAStackViewDistributionStrategy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = OAStackViewDistributionStrategy.m; sourceTree = ""; }; + C23A1311AA2C1EA1D567AE58 /* OAStackViewDistributionStrategy.m */ = {isa = PBXFileReference; includeInIndex = 1; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = OAStackViewDistributionStrategy.m; sourceTree = ""; tabWidth = 2; }; C2C213F00BD25D367C89B5D9 /* KWCallSite.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = KWCallSite.h; path = Classes/Core/KWCallSite.h; sourceTree = ""; }; C348605B130C79C13CC42FAF /* KWCaptureSpy.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = KWCaptureSpy.h; path = Classes/Core/KWCaptureSpy.h; sourceTree = ""; }; C47EE2FAA72FBFF889D50247 /* KWObjCUtilities.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = KWObjCUtilities.m; path = Classes/Core/KWObjCUtilities.m; sourceTree = ""; }; diff --git a/Example/Tests/OAStackViewSpec.m b/Example/Tests/OAStackViewSpec.m index e6f9f21..6d6624d 100644 --- a/Example/Tests/OAStackViewSpec.m +++ b/Example/Tests/OAStackViewSpec.m @@ -448,8 +448,26 @@ }); }); - - + + context(@"OAStackViewDistributionFillProportionally", ^{ + it(@"Distributes the views proportionally based on their intrinsicContentSize", ^{ + // Views 1, 2 and 3 are UIButtons. Changing the title affects their intrinsicContentSize. + // Since we're testing the vertical layout here, we add newlines to affect their intrinsicContentSize height + + [(UIButton *)view1 setTitle:@"the title" forState:UIControlStateNormal]; + [(UIButton *)view2 setTitle:@"the title" forState:UIControlStateNormal]; + [(UIButton *)view3 setTitle:@"the\ntitle" forState:UIControlStateNormal]; + + stackView.distribution = OAStackViewDistributionFillProportionally; + + layoutView(stackView); + CGFloat proportion = view2.intrinsicContentSize.height / view1.intrinsicContentSize.height; + [[theValue(CGRectGetHeight(view2.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetHeight(view1.frame) * proportion)]; + + proportion = view3.intrinsicContentSize.height / view2.intrinsicContentSize.height; + [[theValue(CGRectGetHeight(view3.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetHeight(view2.frame) * proportion)]; + }); + }); }); }); @@ -861,6 +879,25 @@ }); + context(@"OAStackViewDistributionFillProportionally", ^{ + it(@"Distributes the views proportionally based on their intrinsicContentSize", ^{ + // Views 1, 2 and 3 are UIButtons. Changing the title affects their intrinsicContentSize. + + [(UIButton *)view1 setTitle:@"the title" forState:UIControlStateNormal]; + [(UIButton *)view2 setTitle:@"the title" forState:UIControlStateNormal]; + [(UIButton *)view3 setTitle:@"the title the title" forState:UIControlStateNormal]; + + stackView.distribution = OAStackViewDistributionFillProportionally; + + layoutView(stackView); + CGFloat proportion = view2.intrinsicContentSize.width / view1.intrinsicContentSize.width; + [[theValue(CGRectGetWidth(view2.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetWidth(view1.frame) * proportion)]; + + proportion = view3.intrinsicContentSize.width / view2.intrinsicContentSize.width; + [[theValue(CGRectGetWidth(view3.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetWidth(view2.frame) * proportion)]; + }); + }); + }); diff --git a/Pod/Classes/OAStackViewDistributionStrategy.m b/Pod/Classes/OAStackViewDistributionStrategy.m index e1c9566..1b24568 100644 --- a/Pod/Classes/OAStackViewDistributionStrategy.m +++ b/Pod/Classes/OAStackViewDistributionStrategy.m @@ -14,6 +14,15 @@ @interface OAStackViewDistributionStrategyFill : OAStackViewDistributionStrategy @interface OAStackViewDistributionStrategyFillEqually : OAStackViewDistributionStrategy @end +@interface OAStackViewDistributionStrategyFillProportionally : OAStackViewDistributionStrategy +@end + +@interface OAStackViewDistributionStrategyEqualSpacing : OAStackViewDistributionStrategy +@end + +@interface OAStackViewDistributionStrategyEqualCentering : OAStackViewDistributionStrategy +@end + @interface OAStackViewDistributionStrategy () @property(nonatomic) OAStackView *stackView; @property(nonatomic) NSMutableArray *constraints; @@ -32,6 +41,18 @@ + (OAStackViewDistributionStrategy*)strategyWithStackView:(OAStackView *)stackVi case OAStackViewDistributionFillEqually: cls = [OAStackViewDistributionStrategyFillEqually class]; break; + + case OAStackViewDistributionFillProportionally: + cls = [OAStackViewDistributionStrategyFillProportionally class]; + break; + + case OAStackViewDistributionEqualSpacing: + cls = [OAStackViewDistributionStrategyEqualCentering class]; + break; + + case OAStackViewDistributionEqualCentering: + cls = [OAStackViewDistributionStrategyEqualCentering class]; + break; default: break; @@ -101,6 +122,10 @@ - (NSString*)currentAxisString { return self.stackView.axis == UILayoutConstraintAxisHorizontal ? @"H" : @"V"; } +- (NSLayoutAttribute)equalityAxis { + return self.stackView.axis == UILayoutConstraintAxisVertical ? NSLayoutAttributeHeight : NSLayoutAttributeWidth; +} + - (NSMutableArray *)constraints { if (!_constraints) { _constraints = [@[] mutableCopy]; @@ -143,8 +168,45 @@ - (void)addEqualityConstraintsBetween:(UIView*)view otherView:(UIView*)otherView [self.stackView addConstraint:constraint]; } -- (NSLayoutAttribute)equalityAxis { - return self.stackView.axis == UILayoutConstraintAxisVertical ? NSLayoutAttributeHeight : NSLayoutAttributeWidth; +@end + +@implementation OAStackViewDistributionStrategyFillProportionally + +- (void)alignMiddleView:(UIView*)view afterView:(UIView*)previousView { + [super alignMiddleView:view afterView:previousView]; + [self addProportionalityConstraintsBetween:view otherView:previousView]; } -@end \ No newline at end of file +- (void)addProportionalityConstraintsBetween:(UIView *)view otherView:(UIView *)otherView { + if (view == nil || otherView == nil) { + return; + } + + CGFloat multiplier = 1; + if (self.stackView.axis == UILayoutConstraintAxisHorizontal) { + multiplier = view.intrinsicContentSize.width / otherView.intrinsicContentSize.width; + } else { + multiplier = view.intrinsicContentSize.height / otherView.intrinsicContentSize.height; + } + + id constraint = [NSLayoutConstraint constraintWithItem:view + attribute:[self equalityAxis] + relatedBy:NSLayoutRelationEqual + toItem:otherView + attribute:[self equalityAxis] + multiplier:multiplier + constant:0]; + + [self.constraints addObject:constraint]; + [self.stackView addConstraint:constraint]; +} + +@end + +@implementation OAStackViewDistributionStrategyEqualSpacing + +@end + +@implementation OAStackViewDistributionStrategyEqualCentering + +@end