├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Example ├── OAStackView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── OAStackView-Example.xcscheme ├── OAStackView.xcworkspace │ └── contents.xcworkspacedata ├── OAStackView │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Launch Screen.xib │ ├── Main.storyboard │ ├── OAAppDelegate.h │ ├── OAAppDelegate.m │ ├── OAStackView-Info.plist │ ├── OAStackView-Prefix.pch │ ├── OAStackViewProxyViewController.h │ ├── OAStackViewProxyViewController.m │ ├── OAStackViewViewController.h │ ├── OAStackViewViewController.m │ ├── OAUIStackViewViewController.h │ ├── OAUIStackViewViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── Podfile ├── Podfile.lock └── Tests │ ├── OAStackViewAlignmentSpec.m │ ├── OAStackViewSpec.m │ ├── TestHelpers.h │ ├── TestHelpers.m │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── en.lproj │ └── InfoPlist.strings │ └── tests.transcript.txt ├── Info.plist ├── LICENSE ├── OAStackView.podspec ├── OAStackView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── OAStackView.xcscheme ├── Pod ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── OAStackView+Constraint.h │ ├── OAStackView+Constraint.m │ ├── OAStackView+Hiding.h │ ├── OAStackView+Hiding.m │ ├── OAStackView+Traversal.h │ ├── OAStackView+Traversal.m │ ├── OAStackView.h │ ├── OAStackView.m │ ├── OAStackViewAlignmentStrategy.h │ ├── OAStackViewAlignmentStrategy.m │ ├── OAStackViewAlignmentStrategyBaseline.h │ ├── OAStackViewAlignmentStrategyBaseline.m │ ├── OAStackViewDistributionStrategy.h │ ├── OAStackViewDistributionStrategy.m │ ├── OATransformLayer.h │ ├── OATransformLayer.m │ ├── _OALayoutGuide.h │ └── _OALayoutGuide.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | # Note: if you ignore the Pods directory, make sure to uncomment 30 | # `pod install` in .travis.yml 31 | # 32 | # Pods/ 33 | Example/Pods/ 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | language: objective-c 6 | osx_image: xcode7.1 7 | cache: cocoapods 8 | podfile: Example/Podfile 9 | before_install: 10 | - gem install cocoapods # Since Travis is not always on latest version 11 | - pod install --project-directory=Example 12 | install: 13 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 14 | script: 15 | - set -o pipefail && xcodebuild test -workspace Example/OAStackView.xcworkspace -scheme "OAStackView-Example" -destination "platform=iOS Simulator,OS=9.1,name=iPhone 5" ONLY_ACTIVE_ARCH=NO | xcpretty -c 16 | - set -o pipefail && xcodebuild test -workspace Example/OAStackView.xcworkspace -scheme "OAStackView-Example" -destination "platform=iOS Simulator,OS=8.1,name=iPhone 5" ONLY_ACTIVE_ARCH=NO | xcpretty -c 17 | - pod lib lint --quick 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [Unreleased](https://github.com/oarrabi/OAStackView/tree/HEAD) 4 | 5 | [Full Changelog](https://github.com/oarrabi/OAStackView/compare/0.1.0...HEAD) 6 | 7 | **Implemented enhancements:** 8 | 9 | - Add normal framework target so this can be installed outside of CocoaPods. [\#18](https://github.com/oarrabi/OAStackView/issues/18) 10 | 11 | **Closed issues:** 12 | 13 | - OAStackViewAlignmentStrategyCenter does not ensure the stack view is at least as big as the arranged subviews [\#12](https://github.com/oarrabi/OAStackView/issues/12) 14 | 15 | - tomvanzummeren/TZStackView [\#10](https://github.com/oarrabi/OAStackView/issues/10) 16 | 17 | **Merged pull requests:** 18 | 19 | - Add newline to the end of file to suppress Xcode warnings [\#20](https://github.com/oarrabi/OAStackView/pull/20) ([akaralar](https://github.com/akaralar)) 20 | 21 | - Added dynamic framework project [\#19](https://github.com/oarrabi/OAStackView/pull/19) ([avalanched](https://github.com/avalanched)) 22 | 23 | - Silence log message [\#15](https://github.com/oarrabi/OAStackView/pull/15) ([garnett](https://github.com/garnett)) 24 | 25 | - Break retain cycle between stack view and alignment strategy [\#9](https://github.com/oarrabi/OAStackView/pull/9) ([jamieQ](https://github.com/jamieQ)) 26 | 27 | - OAStackViewDistributionFillProportionally implemented [\#8](https://github.com/oarrabi/OAStackView/pull/8) ([bartvandendriessche](https://github.com/bartvandendriessche)) 28 | 29 | - Is it 6 or 7? Podspec specifies 7+ [\#6](https://github.com/oarrabi/OAStackView/pull/6) ([jstart](https://github.com/jstart)) 30 | 31 | - Update README.md [\#5](https://github.com/oarrabi/OAStackView/pull/5) ([jstart](https://github.com/jstart)) 32 | 33 | - Couple of spellings [\#4](https://github.com/oarrabi/OAStackView/pull/4) ([ay8s](https://github.com/ay8s)) 34 | 35 | - Make view non-renderable, fix Xcode7 initializer warnings [\#3](https://github.com/oarrabi/OAStackView/pull/3) ([garnett](https://github.com/garnett)) 36 | 37 | - I think this is a typo? [\#1](https://github.com/oarrabi/OAStackView/pull/1) ([jstart](https://github.com/jstart)) 38 | 39 | ## [0.1.0](https://github.com/oarrabi/OAStackView/tree/0.1.0) (2015-06-16) 40 | 41 | 42 | 43 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /Example/OAStackView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3C21E5C515971A65F7787C3E /* libPods-OAStackView_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D5C5103D9BEF8145B70ECED /* libPods-OAStackView_Tests.a */; }; 11 | 535F82651B7681B000841D0D /* OAStackViewAlignmentSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 535F82631B76819500841D0D /* OAStackViewAlignmentSpec.m */; }; 12 | 535F82681B7681E200841D0D /* TestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 535F82671B7681E200841D0D /* TestHelpers.m */; }; 13 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 14 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 15 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 16 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 17 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 18 | 6003F59E195388D20070C39A /* OAAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* OAAppDelegate.m */; }; 19 | 6003F5A7195388D20070C39A /* OAStackViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* OAStackViewViewController.m */; }; 20 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 21 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 22 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 23 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 24 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 25 | 6B5FCDD21E3139890010BCDB /* OAStackViewProxyViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B5FCDD11E3139890010BCDB /* OAStackViewProxyViewController.m */; }; 26 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 27 | A7CC22B31B2E0B8E009C9911 /* OAStackViewSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = A7CC22B11B2E0B46009C9911 /* OAStackViewSpec.m */; }; 28 | A7CC22B51B2E0BC7009C9911 /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A7CC22B41B2E0BC7009C9911 /* Launch Screen.xib */; }; 29 | D21E827A1BCF97D400D8379B /* OAUIStackViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D21E82791BCF97D400D8379B /* OAUIStackViewViewController.m */; }; 30 | E71CDC0CCF5EBB7F9D8090AE /* libPods-OAStackView_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A034F8EDA2F096ABF11855AA /* libPods-OAStackView_Example.a */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 6003F582195388D10070C39A /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 6003F589195388D20070C39A; 39 | remoteInfo = OAStackView; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 2E337E5258F10C742E4A5C11 /* OAStackView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = OAStackView.podspec; path = ../OAStackView.podspec; sourceTree = ""; }; 45 | 317D80D715D45DBE5FF06007 /* Pods-OAStackView_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OAStackView_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OAStackView_Example/Pods-OAStackView_Example.debug.xcconfig"; sourceTree = ""; }; 46 | 535F82631B76819500841D0D /* OAStackViewAlignmentSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAStackViewAlignmentSpec.m; sourceTree = ""; }; 47 | 535F82661B7681E200841D0D /* TestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestHelpers.h; sourceTree = ""; }; 48 | 535F82671B7681E200841D0D /* TestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestHelpers.m; sourceTree = ""; }; 49 | 5EB1D13FCC2F307EBA41CE00 /* Pods-OAStackView_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OAStackView_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-OAStackView_Tests/Pods-OAStackView_Tests.release.xcconfig"; sourceTree = ""; }; 50 | 6003F58A195388D20070C39A /* OAStackView_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OAStackView_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 52 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 53 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 54 | 6003F595195388D20070C39A /* OAStackView-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "OAStackView-Info.plist"; sourceTree = ""; }; 55 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 56 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 57 | 6003F59B195388D20070C39A /* OAStackView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OAStackView-Prefix.pch"; sourceTree = ""; }; 58 | 6003F59C195388D20070C39A /* OAAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAAppDelegate.h; sourceTree = ""; }; 59 | 6003F59D195388D20070C39A /* OAAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OAAppDelegate.m; sourceTree = ""; }; 60 | 6003F5A5195388D20070C39A /* OAStackViewViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OAStackViewViewController.h; sourceTree = ""; }; 61 | 6003F5A6195388D20070C39A /* OAStackViewViewController.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = OAStackViewViewController.m; sourceTree = ""; tabWidth = 2; }; 62 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 63 | 6003F5AE195388D20070C39A /* OAStackView_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OAStackView_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 65 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 66 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 67 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 68 | 6B5FCDD01E3139890010BCDB /* OAStackViewProxyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAStackViewProxyViewController.h; sourceTree = ""; }; 69 | 6B5FCDD11E3139890010BCDB /* OAStackViewProxyViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAStackViewProxyViewController.m; sourceTree = ""; }; 70 | 79754B7429578C082AA6B662 /* Pods-OAStackView_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OAStackView_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-OAStackView_Example/Pods-OAStackView_Example.release.xcconfig"; sourceTree = ""; }; 71 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 72 | 8D5C5103D9BEF8145B70ECED /* libPods-OAStackView_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OAStackView_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 73 | A034F8EDA2F096ABF11855AA /* libPods-OAStackView_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OAStackView_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | A7CC22B11B2E0B46009C9911 /* OAStackViewSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAStackViewSpec.m; sourceTree = ""; }; 75 | A7CC22B41B2E0BC7009C9911 /* Launch Screen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "Launch Screen.xib"; sourceTree = ""; }; 76 | AE9C0956AC3290D64CFDB6CE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 77 | C583B3EE309ADAB8181DEA28 /* Pods-OAStackView_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OAStackView_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-OAStackView_Tests/Pods-OAStackView_Tests.debug.xcconfig"; sourceTree = ""; }; 78 | D21E82781BCF97D400D8379B /* OAUIStackViewViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OAUIStackViewViewController.h; sourceTree = ""; }; 79 | D21E82791BCF97D400D8379B /* OAUIStackViewViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OAUIStackViewViewController.m; sourceTree = ""; }; 80 | E84D2DC1C0119AB0E1D43BF1 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 81 | /* End PBXFileReference section */ 82 | 83 | /* Begin PBXFrameworksBuildPhase section */ 84 | 6003F587195388D20070C39A /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 89 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 90 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 91 | E71CDC0CCF5EBB7F9D8090AE /* libPods-OAStackView_Example.a in Frameworks */, 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | 6003F5AB195388D20070C39A /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 100 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 101 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 102 | 3C21E5C515971A65F7787C3E /* libPods-OAStackView_Tests.a in Frameworks */, 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | /* End PBXFrameworksBuildPhase section */ 107 | 108 | /* Begin PBXGroup section */ 109 | 2C7BBA48E2B086190BEB147E /* Pods */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 317D80D715D45DBE5FF06007 /* Pods-OAStackView_Example.debug.xcconfig */, 113 | 79754B7429578C082AA6B662 /* Pods-OAStackView_Example.release.xcconfig */, 114 | C583B3EE309ADAB8181DEA28 /* Pods-OAStackView_Tests.debug.xcconfig */, 115 | 5EB1D13FCC2F307EBA41CE00 /* Pods-OAStackView_Tests.release.xcconfig */, 116 | ); 117 | name = Pods; 118 | sourceTree = ""; 119 | }; 120 | 6003F581195388D10070C39A = { 121 | isa = PBXGroup; 122 | children = ( 123 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 124 | 6003F593195388D20070C39A /* Example for OAStackView */, 125 | 6003F5B5195388D20070C39A /* Tests */, 126 | 6003F58C195388D20070C39A /* Frameworks */, 127 | 6003F58B195388D20070C39A /* Products */, 128 | 2C7BBA48E2B086190BEB147E /* Pods */, 129 | ); 130 | sourceTree = ""; 131 | }; 132 | 6003F58B195388D20070C39A /* Products */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 6003F58A195388D20070C39A /* OAStackView_Example.app */, 136 | 6003F5AE195388D20070C39A /* OAStackView_Tests.xctest */, 137 | ); 138 | name = Products; 139 | sourceTree = ""; 140 | }; 141 | 6003F58C195388D20070C39A /* Frameworks */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 6003F58D195388D20070C39A /* Foundation.framework */, 145 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 146 | 6003F591195388D20070C39A /* UIKit.framework */, 147 | 6003F5AF195388D20070C39A /* XCTest.framework */, 148 | A034F8EDA2F096ABF11855AA /* libPods-OAStackView_Example.a */, 149 | 8D5C5103D9BEF8145B70ECED /* libPods-OAStackView_Tests.a */, 150 | ); 151 | name = Frameworks; 152 | sourceTree = ""; 153 | }; 154 | 6003F593195388D20070C39A /* Example for OAStackView */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 6003F59C195388D20070C39A /* OAAppDelegate.h */, 158 | 6003F59D195388D20070C39A /* OAAppDelegate.m */, 159 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 160 | D21E82781BCF97D400D8379B /* OAUIStackViewViewController.h */, 161 | D21E82791BCF97D400D8379B /* OAUIStackViewViewController.m */, 162 | 6003F5A5195388D20070C39A /* OAStackViewViewController.h */, 163 | 6003F5A6195388D20070C39A /* OAStackViewViewController.m */, 164 | 6B5FCDD01E3139890010BCDB /* OAStackViewProxyViewController.h */, 165 | 6B5FCDD11E3139890010BCDB /* OAStackViewProxyViewController.m */, 166 | 6003F5A8195388D20070C39A /* Images.xcassets */, 167 | 6003F594195388D20070C39A /* Supporting Files */, 168 | A7CC22B41B2E0BC7009C9911 /* Launch Screen.xib */, 169 | ); 170 | name = "Example for OAStackView"; 171 | path = OAStackView; 172 | sourceTree = ""; 173 | }; 174 | 6003F594195388D20070C39A /* Supporting Files */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 6003F595195388D20070C39A /* OAStackView-Info.plist */, 178 | 6003F596195388D20070C39A /* InfoPlist.strings */, 179 | 6003F599195388D20070C39A /* main.m */, 180 | 6003F59B195388D20070C39A /* OAStackView-Prefix.pch */, 181 | ); 182 | name = "Supporting Files"; 183 | sourceTree = ""; 184 | }; 185 | 6003F5B5195388D20070C39A /* Tests */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 535F82661B7681E200841D0D /* TestHelpers.h */, 189 | 535F82671B7681E200841D0D /* TestHelpers.m */, 190 | A7CC22B11B2E0B46009C9911 /* OAStackViewSpec.m */, 191 | 535F82631B76819500841D0D /* OAStackViewAlignmentSpec.m */, 192 | 6003F5B6195388D20070C39A /* Supporting Files */, 193 | ); 194 | path = Tests; 195 | sourceTree = ""; 196 | }; 197 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 201 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 202 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 203 | ); 204 | name = "Supporting Files"; 205 | sourceTree = ""; 206 | }; 207 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 2E337E5258F10C742E4A5C11 /* OAStackView.podspec */, 211 | AE9C0956AC3290D64CFDB6CE /* README.md */, 212 | E84D2DC1C0119AB0E1D43BF1 /* LICENSE */, 213 | ); 214 | name = "Podspec Metadata"; 215 | sourceTree = ""; 216 | }; 217 | /* End PBXGroup section */ 218 | 219 | /* Begin PBXNativeTarget section */ 220 | 6003F589195388D20070C39A /* OAStackView_Example */ = { 221 | isa = PBXNativeTarget; 222 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "OAStackView_Example" */; 223 | buildPhases = ( 224 | F0D056238CAC9FFD1A5357A9 /* [CP] Check Pods Manifest.lock */, 225 | 6003F586195388D20070C39A /* Sources */, 226 | 6003F587195388D20070C39A /* Frameworks */, 227 | 6003F588195388D20070C39A /* Resources */, 228 | FB12E9DD5F5A20597F9FFA8E /* [CP] Embed Pods Frameworks */, 229 | D8CA61B1CD58F89FD32A8D95 /* [CP] Copy Pods Resources */, 230 | ); 231 | buildRules = ( 232 | ); 233 | dependencies = ( 234 | ); 235 | name = OAStackView_Example; 236 | productName = OAStackView; 237 | productReference = 6003F58A195388D20070C39A /* OAStackView_Example.app */; 238 | productType = "com.apple.product-type.application"; 239 | }; 240 | 6003F5AD195388D20070C39A /* OAStackView_Tests */ = { 241 | isa = PBXNativeTarget; 242 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "OAStackView_Tests" */; 243 | buildPhases = ( 244 | 9924532C4EE96CF45A93ED07 /* [CP] Check Pods Manifest.lock */, 245 | 6003F5AA195388D20070C39A /* Sources */, 246 | 6003F5AB195388D20070C39A /* Frameworks */, 247 | 6003F5AC195388D20070C39A /* Resources */, 248 | EB7C3C34672A93EA2CFD015C /* [CP] Embed Pods Frameworks */, 249 | CE8E2A7D466DE0968E379096 /* [CP] Copy Pods Resources */, 250 | ); 251 | buildRules = ( 252 | ); 253 | dependencies = ( 254 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 255 | ); 256 | name = OAStackView_Tests; 257 | productName = OAStackViewTests; 258 | productReference = 6003F5AE195388D20070C39A /* OAStackView_Tests.xctest */; 259 | productType = "com.apple.product-type.bundle.unit-test"; 260 | }; 261 | /* End PBXNativeTarget section */ 262 | 263 | /* Begin PBXProject section */ 264 | 6003F582195388D10070C39A /* Project object */ = { 265 | isa = PBXProject; 266 | attributes = { 267 | CLASSPREFIX = OA; 268 | LastUpgradeCheck = 0510; 269 | ORGANIZATIONNAME = "Omar Abdelhafith"; 270 | TargetAttributes = { 271 | 6003F5AD195388D20070C39A = { 272 | TestTargetID = 6003F589195388D20070C39A; 273 | }; 274 | }; 275 | }; 276 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "OAStackView" */; 277 | compatibilityVersion = "Xcode 3.2"; 278 | developmentRegion = English; 279 | hasScannedForEncodings = 0; 280 | knownRegions = ( 281 | en, 282 | Base, 283 | ); 284 | mainGroup = 6003F581195388D10070C39A; 285 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 286 | projectDirPath = ""; 287 | projectRoot = ""; 288 | targets = ( 289 | 6003F589195388D20070C39A /* OAStackView_Example */, 290 | 6003F5AD195388D20070C39A /* OAStackView_Tests */, 291 | ); 292 | }; 293 | /* End PBXProject section */ 294 | 295 | /* Begin PBXResourcesBuildPhase section */ 296 | 6003F588195388D20070C39A /* Resources */ = { 297 | isa = PBXResourcesBuildPhase; 298 | buildActionMask = 2147483647; 299 | files = ( 300 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, 301 | A7CC22B51B2E0BC7009C9911 /* Launch Screen.xib in Resources */, 302 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 303 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 304 | ); 305 | runOnlyForDeploymentPostprocessing = 0; 306 | }; 307 | 6003F5AC195388D20070C39A /* Resources */ = { 308 | isa = PBXResourcesBuildPhase; 309 | buildActionMask = 2147483647; 310 | files = ( 311 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | /* End PBXResourcesBuildPhase section */ 316 | 317 | /* Begin PBXShellScriptBuildPhase section */ 318 | 9924532C4EE96CF45A93ED07 /* [CP] Check Pods Manifest.lock */ = { 319 | isa = PBXShellScriptBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | ); 323 | inputPaths = ( 324 | ); 325 | name = "[CP] Check Pods Manifest.lock"; 326 | outputPaths = ( 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | shellPath = /bin/sh; 330 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 331 | showEnvVarsInLog = 0; 332 | }; 333 | CE8E2A7D466DE0968E379096 /* [CP] Copy Pods Resources */ = { 334 | isa = PBXShellScriptBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | ); 338 | inputPaths = ( 339 | ); 340 | name = "[CP] Copy Pods Resources"; 341 | outputPaths = ( 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | shellPath = /bin/sh; 345 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OAStackView_Tests/Pods-OAStackView_Tests-resources.sh\"\n"; 346 | showEnvVarsInLog = 0; 347 | }; 348 | D8CA61B1CD58F89FD32A8D95 /* [CP] Copy Pods Resources */ = { 349 | isa = PBXShellScriptBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | ); 353 | inputPaths = ( 354 | ); 355 | name = "[CP] Copy Pods Resources"; 356 | outputPaths = ( 357 | ); 358 | runOnlyForDeploymentPostprocessing = 0; 359 | shellPath = /bin/sh; 360 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OAStackView_Example/Pods-OAStackView_Example-resources.sh\"\n"; 361 | showEnvVarsInLog = 0; 362 | }; 363 | EB7C3C34672A93EA2CFD015C /* [CP] Embed Pods Frameworks */ = { 364 | isa = PBXShellScriptBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | ); 368 | inputPaths = ( 369 | ); 370 | name = "[CP] Embed Pods Frameworks"; 371 | outputPaths = ( 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | shellPath = /bin/sh; 375 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OAStackView_Tests/Pods-OAStackView_Tests-frameworks.sh\"\n"; 376 | showEnvVarsInLog = 0; 377 | }; 378 | F0D056238CAC9FFD1A5357A9 /* [CP] Check Pods Manifest.lock */ = { 379 | isa = PBXShellScriptBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | ); 383 | inputPaths = ( 384 | ); 385 | name = "[CP] Check Pods Manifest.lock"; 386 | outputPaths = ( 387 | ); 388 | runOnlyForDeploymentPostprocessing = 0; 389 | shellPath = /bin/sh; 390 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 391 | showEnvVarsInLog = 0; 392 | }; 393 | FB12E9DD5F5A20597F9FFA8E /* [CP] Embed Pods Frameworks */ = { 394 | isa = PBXShellScriptBuildPhase; 395 | buildActionMask = 2147483647; 396 | files = ( 397 | ); 398 | inputPaths = ( 399 | ); 400 | name = "[CP] Embed Pods Frameworks"; 401 | outputPaths = ( 402 | ); 403 | runOnlyForDeploymentPostprocessing = 0; 404 | shellPath = /bin/sh; 405 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-OAStackView_Example/Pods-OAStackView_Example-frameworks.sh\"\n"; 406 | showEnvVarsInLog = 0; 407 | }; 408 | /* End PBXShellScriptBuildPhase section */ 409 | 410 | /* Begin PBXSourcesBuildPhase section */ 411 | 6003F586195388D20070C39A /* Sources */ = { 412 | isa = PBXSourcesBuildPhase; 413 | buildActionMask = 2147483647; 414 | files = ( 415 | 6B5FCDD21E3139890010BCDB /* OAStackViewProxyViewController.m in Sources */, 416 | D21E827A1BCF97D400D8379B /* OAUIStackViewViewController.m in Sources */, 417 | 6003F59E195388D20070C39A /* OAAppDelegate.m in Sources */, 418 | 6003F5A7195388D20070C39A /* OAStackViewViewController.m in Sources */, 419 | 6003F59A195388D20070C39A /* main.m in Sources */, 420 | 535F82681B7681E200841D0D /* TestHelpers.m in Sources */, 421 | ); 422 | runOnlyForDeploymentPostprocessing = 0; 423 | }; 424 | 6003F5AA195388D20070C39A /* Sources */ = { 425 | isa = PBXSourcesBuildPhase; 426 | buildActionMask = 2147483647; 427 | files = ( 428 | 535F82651B7681B000841D0D /* OAStackViewAlignmentSpec.m in Sources */, 429 | A7CC22B31B2E0B8E009C9911 /* OAStackViewSpec.m in Sources */, 430 | ); 431 | runOnlyForDeploymentPostprocessing = 0; 432 | }; 433 | /* End PBXSourcesBuildPhase section */ 434 | 435 | /* Begin PBXTargetDependency section */ 436 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 437 | isa = PBXTargetDependency; 438 | target = 6003F589195388D20070C39A /* OAStackView_Example */; 439 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 440 | }; 441 | /* End PBXTargetDependency section */ 442 | 443 | /* Begin PBXVariantGroup section */ 444 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 445 | isa = PBXVariantGroup; 446 | children = ( 447 | 6003F597195388D20070C39A /* en */, 448 | ); 449 | name = InfoPlist.strings; 450 | sourceTree = ""; 451 | }; 452 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 453 | isa = PBXVariantGroup; 454 | children = ( 455 | 6003F5B9195388D20070C39A /* en */, 456 | ); 457 | name = InfoPlist.strings; 458 | sourceTree = ""; 459 | }; 460 | /* End PBXVariantGroup section */ 461 | 462 | /* Begin XCBuildConfiguration section */ 463 | 6003F5BD195388D20070C39A /* Debug */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ALWAYS_SEARCH_USER_PATHS = NO; 467 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 468 | CLANG_CXX_LIBRARY = "libc++"; 469 | CLANG_ENABLE_MODULES = YES; 470 | CLANG_ENABLE_OBJC_ARC = YES; 471 | CLANG_WARN_BOOL_CONVERSION = YES; 472 | CLANG_WARN_CONSTANT_CONVERSION = YES; 473 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 474 | CLANG_WARN_EMPTY_BODY = YES; 475 | CLANG_WARN_ENUM_CONVERSION = YES; 476 | CLANG_WARN_INT_CONVERSION = YES; 477 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 478 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 479 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 480 | COPY_PHASE_STRIP = NO; 481 | GCC_C_LANGUAGE_STANDARD = gnu99; 482 | GCC_DYNAMIC_NO_PIC = NO; 483 | GCC_OPTIMIZATION_LEVEL = 0; 484 | GCC_PREPROCESSOR_DEFINITIONS = ( 485 | "DEBUG=1", 486 | "$(inherited)", 487 | ); 488 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 489 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 490 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 491 | GCC_WARN_UNDECLARED_SELECTOR = YES; 492 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 493 | GCC_WARN_UNUSED_FUNCTION = YES; 494 | GCC_WARN_UNUSED_VARIABLE = YES; 495 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 496 | ONLY_ACTIVE_ARCH = YES; 497 | SDKROOT = iphoneos; 498 | TARGETED_DEVICE_FAMILY = "1,2"; 499 | }; 500 | name = Debug; 501 | }; 502 | 6003F5BE195388D20070C39A /* Release */ = { 503 | isa = XCBuildConfiguration; 504 | buildSettings = { 505 | ALWAYS_SEARCH_USER_PATHS = NO; 506 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 507 | CLANG_CXX_LIBRARY = "libc++"; 508 | CLANG_ENABLE_MODULES = YES; 509 | CLANG_ENABLE_OBJC_ARC = YES; 510 | CLANG_WARN_BOOL_CONVERSION = YES; 511 | CLANG_WARN_CONSTANT_CONVERSION = YES; 512 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 513 | CLANG_WARN_EMPTY_BODY = YES; 514 | CLANG_WARN_ENUM_CONVERSION = YES; 515 | CLANG_WARN_INT_CONVERSION = YES; 516 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 517 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 518 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 519 | COPY_PHASE_STRIP = YES; 520 | ENABLE_NS_ASSERTIONS = NO; 521 | GCC_C_LANGUAGE_STANDARD = gnu99; 522 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 523 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 524 | GCC_WARN_UNDECLARED_SELECTOR = YES; 525 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 526 | GCC_WARN_UNUSED_FUNCTION = YES; 527 | GCC_WARN_UNUSED_VARIABLE = YES; 528 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 529 | SDKROOT = iphoneos; 530 | TARGETED_DEVICE_FAMILY = "1,2"; 531 | VALIDATE_PRODUCT = YES; 532 | }; 533 | name = Release; 534 | }; 535 | 6003F5C0195388D20070C39A /* Debug */ = { 536 | isa = XCBuildConfiguration; 537 | baseConfigurationReference = 317D80D715D45DBE5FF06007 /* Pods-OAStackView_Example.debug.xcconfig */; 538 | buildSettings = { 539 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 540 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; 541 | DEVELOPMENT_TEAM = ""; 542 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 543 | GCC_PREFIX_HEADER = "OAStackView/OAStackView-Prefix.pch"; 544 | INFOPLIST_FILE = "OAStackView/OAStackView-Info.plist"; 545 | MODULE_NAME = ExampleApp; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | WRAPPER_EXTENSION = app; 548 | }; 549 | name = Debug; 550 | }; 551 | 6003F5C1195388D20070C39A /* Release */ = { 552 | isa = XCBuildConfiguration; 553 | baseConfigurationReference = 79754B7429578C082AA6B662 /* Pods-OAStackView_Example.release.xcconfig */; 554 | buildSettings = { 555 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 556 | CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES; 557 | DEVELOPMENT_TEAM = ""; 558 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 559 | GCC_PREFIX_HEADER = "OAStackView/OAStackView-Prefix.pch"; 560 | INFOPLIST_FILE = "OAStackView/OAStackView-Info.plist"; 561 | MODULE_NAME = ExampleApp; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | WRAPPER_EXTENSION = app; 564 | }; 565 | name = Release; 566 | }; 567 | 6003F5C3195388D20070C39A /* Debug */ = { 568 | isa = XCBuildConfiguration; 569 | baseConfigurationReference = C583B3EE309ADAB8181DEA28 /* Pods-OAStackView_Tests.debug.xcconfig */; 570 | buildSettings = { 571 | BUNDLE_LOADER = "$(TEST_HOST)"; 572 | FRAMEWORK_SEARCH_PATHS = ( 573 | "$(SDKROOT)/Developer/Library/Frameworks", 574 | "$(inherited)", 575 | "$(DEVELOPER_FRAMEWORKS_DIR)", 576 | ); 577 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 578 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 579 | GCC_PREPROCESSOR_DEFINITIONS = ( 580 | "DEBUG=1", 581 | "$(inherited)", 582 | ); 583 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 584 | PRODUCT_NAME = "$(TARGET_NAME)"; 585 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OAStackView_Example.app/OAStackView_Example"; 586 | WRAPPER_EXTENSION = xctest; 587 | }; 588 | name = Debug; 589 | }; 590 | 6003F5C4195388D20070C39A /* Release */ = { 591 | isa = XCBuildConfiguration; 592 | baseConfigurationReference = 5EB1D13FCC2F307EBA41CE00 /* Pods-OAStackView_Tests.release.xcconfig */; 593 | buildSettings = { 594 | BUNDLE_LOADER = "$(TEST_HOST)"; 595 | FRAMEWORK_SEARCH_PATHS = ( 596 | "$(SDKROOT)/Developer/Library/Frameworks", 597 | "$(inherited)", 598 | "$(DEVELOPER_FRAMEWORKS_DIR)", 599 | ); 600 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 601 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 602 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 603 | PRODUCT_NAME = "$(TARGET_NAME)"; 604 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OAStackView_Example.app/OAStackView_Example"; 605 | WRAPPER_EXTENSION = xctest; 606 | }; 607 | name = Release; 608 | }; 609 | /* End XCBuildConfiguration section */ 610 | 611 | /* Begin XCConfigurationList section */ 612 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "OAStackView" */ = { 613 | isa = XCConfigurationList; 614 | buildConfigurations = ( 615 | 6003F5BD195388D20070C39A /* Debug */, 616 | 6003F5BE195388D20070C39A /* Release */, 617 | ); 618 | defaultConfigurationIsVisible = 0; 619 | defaultConfigurationName = Release; 620 | }; 621 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "OAStackView_Example" */ = { 622 | isa = XCConfigurationList; 623 | buildConfigurations = ( 624 | 6003F5C0195388D20070C39A /* Debug */, 625 | 6003F5C1195388D20070C39A /* Release */, 626 | ); 627 | defaultConfigurationIsVisible = 0; 628 | defaultConfigurationName = Release; 629 | }; 630 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "OAStackView_Tests" */ = { 631 | isa = XCConfigurationList; 632 | buildConfigurations = ( 633 | 6003F5C3195388D20070C39A /* Debug */, 634 | 6003F5C4195388D20070C39A /* Release */, 635 | ); 636 | defaultConfigurationIsVisible = 0; 637 | defaultConfigurationName = Release; 638 | }; 639 | /* End XCConfigurationList section */ 640 | }; 641 | rootObject = 6003F582195388D10070C39A /* Project object */; 642 | } 643 | -------------------------------------------------------------------------------- /Example/OAStackView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/OAStackView.xcodeproj/xcshareddata/xcschemes/OAStackView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/OAStackView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/OAStackView/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "60x60", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "29x29", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "40x40", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "76x76", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Example/OAStackView/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/OAStackView/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Example/OAStackView/Launch Screen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Example/OAStackView/OAAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAAppDelegate.h 3 | // OAStackView 4 | // 5 | // Created by CocoaPods on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface OAAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/OAStackView/OAAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAAppDelegate.m 3 | // OAStackView 4 | // 5 | // Created by CocoaPods on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import "OAAppDelegate.h" 10 | 11 | //@interface OAStackViewDisableForwardToUIStackViewSentinel : NSObject @end 12 | //@implementation OAStackViewDisableForwardToUIStackViewSentinel @end 13 | 14 | @implementation OAAppDelegate 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 17 | { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | - (void)applicationWillResignActive:(UIApplication *)application 23 | { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application 29 | { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | - (void)applicationWillEnterForeground:(UIApplication *)application 35 | { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | - (void)applicationDidBecomeActive:(UIApplication *)application 40 | { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | - (void)applicationWillTerminate:(UIApplication *)application 45 | { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Example/OAStackView/OAStackView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | Main 31 | UIMainStoryboardFile~ipad 32 | Main_iPad 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UISupportedInterfaceOrientations 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Example/OAStackView/OAStackView-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/OAStackView/OAStackViewProxyViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAViewController.h 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface OAStackViewProxyViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/OAStackView/OAStackViewProxyViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewProxyViewController.m 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import "OAStackViewProxyViewController.h" 10 | #import 11 | 12 | @interface OAStackViewProxyViewController () 13 | @property (weak, nonatomic) OAStackViewProxy *stackView; 14 | @property (weak, nonatomic) IBOutlet UIView *viewToRemove; 15 | @end 16 | 17 | @implementation OAStackViewProxyViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | NSLog(@"%@: stackView implemented by %@ (%@)", 24 | NSStringFromClass([self class]), 25 | NSStringFromClass([self.stackView class]), 26 | NSStringFromClass([self.stackView superclass])); 27 | } 28 | 29 | - (void)didReceiveMemoryWarning 30 | { 31 | [super didReceiveMemoryWarning]; 32 | // Dispose of any resources that can be recreated. 33 | } 34 | - (IBAction)twoTapped:(UIButton*)sender { 35 | sender.hidden = YES; 36 | } 37 | 38 | - (IBAction)oneTapped:(UIButton*)sender { 39 | sender.hidden = YES; 40 | } 41 | 42 | - (IBAction)threeTapped:(UIButton*)sender { 43 | sender.hidden = YES; 44 | } 45 | 46 | 47 | - (IBAction)verticalTapped:(id)sender { 48 | self.stackView.axis = UILayoutConstraintAxisVertical; 49 | } 50 | 51 | - (IBAction)horizontalTapped:(id)sender { 52 | self.stackView.axis = UILayoutConstraintAxisHorizontal; 53 | } 54 | 55 | - (IBAction)spacingTapped:(UIButton*)sender { 56 | self.stackView.spacing = sender.tag; 57 | } 58 | 59 | - (IBAction)hideAll:(UIButton*)sender { 60 | [self.stackView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 61 | [obj setHidden:YES]; 62 | }]; 63 | } 64 | 65 | - (IBAction)showAll:(UIButton*)sender { 66 | // [self.stackView.subviews[1] setHidden:NO]; 67 | [self.stackView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 68 | [obj setHidden:NO]; 69 | }]; 70 | } 71 | 72 | - (IBAction)alignmentFill:(UIButton*)sender { 73 | self.stackView.alignment = OAStackViewAlignmentFill; 74 | } 75 | 76 | - (IBAction)alignmentCenter:(UIButton*)sender { 77 | self.stackView.alignment = OAStackViewAlignmentCenter; 78 | } 79 | 80 | - (IBAction)alignmentTrailing:(UIButton*)sender { 81 | self.stackView.alignment = OAStackViewAlignmentTrailing; 82 | } 83 | 84 | - (IBAction)alignmentLeading:(UIButton*)sender { 85 | self.stackView.alignment = OAStackViewAlignmentLeading; 86 | } 87 | 88 | - (IBAction)distributionFill:(UIButton *)sender { 89 | self.stackView.distribution = OAStackViewDistributionFill; 90 | } 91 | 92 | - (IBAction)distributionFillEqually:(UIButton *)sender { 93 | self.stackView.distribution = OAStackViewDistributionFillEqually; 94 | } 95 | 96 | - (IBAction)distributionFillProportionally:(UIButton *)sender { 97 | self.stackView.distribution = OAStackViewDistributionFillProportionally; 98 | } 99 | 100 | - (IBAction)distributionEqualSpacing:(UIButton *)sender { 101 | self.stackView.distribution = OAStackViewDistributionEqualSpacing; 102 | } 103 | 104 | - (IBAction)distributionEqualCentering:(UIButton *)sender { 105 | self.stackView.distribution = OAStackViewDistributionEqualCentering; 106 | } 107 | 108 | - (IBAction)marginsTapped:(UIButton *)sender { 109 | switch (sender.tag) { 110 | case 100: 111 | self.stackView.layoutMarginsRelativeArrangement = YES; 112 | self.stackView.layoutMargins = UIEdgeInsetsMake(10, 0, 10, 0); 113 | break; 114 | case 200: 115 | self.stackView.layoutMarginsRelativeArrangement = YES; 116 | self.stackView.layoutMargins = UIEdgeInsetsMake(10, 20, 30, 40); 117 | break; 118 | default: 119 | self.stackView.layoutMarginsRelativeArrangement = NO; 120 | self.stackView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); 121 | break; 122 | } 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /Example/OAStackView/OAStackViewViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAViewController.h 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface OAStackViewViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/OAStackView/OAStackViewViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAViewController.m 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import "OAStackViewViewController.h" 10 | #import 11 | 12 | @interface OAStackViewViewController () 13 | @property (weak, nonatomic) OAStackView *stackView; 14 | @property (weak, nonatomic) IBOutlet UIView *viewToRemove; 15 | @end 16 | 17 | @implementation OAStackViewViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | 23 | NSLog(@"%@: stackView implemented by %@ (%@)", 24 | NSStringFromClass([self class]), 25 | NSStringFromClass([self.stackView class]), 26 | NSStringFromClass([self.stackView superclass])); 27 | } 28 | 29 | - (void)didReceiveMemoryWarning 30 | { 31 | [super didReceiveMemoryWarning]; 32 | // Dispose of any resources that can be recreated. 33 | } 34 | - (IBAction)twoTapped:(UIButton*)sender { 35 | sender.hidden = YES; 36 | } 37 | 38 | - (IBAction)oneTapped:(UIButton*)sender { 39 | sender.hidden = YES; 40 | } 41 | 42 | - (IBAction)threeTapped:(UIButton*)sender { 43 | sender.hidden = YES; 44 | } 45 | 46 | 47 | - (IBAction)verticalTapped:(id)sender { 48 | self.stackView.axis = UILayoutConstraintAxisVertical; 49 | } 50 | 51 | - (IBAction)horizontalTapped:(id)sender { 52 | self.stackView.axis = UILayoutConstraintAxisHorizontal; 53 | } 54 | 55 | - (IBAction)spacingTapped:(UIButton*)sender { 56 | self.stackView.spacing = sender.tag; 57 | } 58 | 59 | - (IBAction)hideAll:(UIButton*)sender { 60 | [self.stackView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 61 | [obj setHidden:YES]; 62 | }]; 63 | } 64 | 65 | - (IBAction)showAll:(UIButton*)sender { 66 | // [self.stackView.subviews[1] setHidden:NO]; 67 | [self.stackView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 68 | [obj setHidden:NO]; 69 | }]; 70 | } 71 | 72 | - (IBAction)alignmentFill:(UIButton*)sender { 73 | self.stackView.alignment = OAStackViewAlignmentFill; 74 | } 75 | 76 | - (IBAction)alignmentCenter:(UIButton*)sender { 77 | self.stackView.alignment = OAStackViewAlignmentCenter; 78 | } 79 | 80 | - (IBAction)alignmentTrailing:(UIButton*)sender { 81 | self.stackView.alignment = OAStackViewAlignmentTrailing; 82 | } 83 | 84 | - (IBAction)alignmentLeading:(UIButton*)sender { 85 | self.stackView.alignment = OAStackViewAlignmentLeading; 86 | } 87 | 88 | - (IBAction)distributionFill:(UIButton *)sender { 89 | self.stackView.distribution = OAStackViewDistributionFill; 90 | } 91 | 92 | - (IBAction)distributionFillEqually:(UIButton *)sender { 93 | self.stackView.distribution = OAStackViewDistributionFillEqually; 94 | } 95 | 96 | - (IBAction)distributionFillProportionally:(UIButton *)sender { 97 | self.stackView.distribution = OAStackViewDistributionFillProportionally; 98 | } 99 | 100 | - (IBAction)distributionEqualSpacing:(UIButton *)sender { 101 | self.stackView.distribution = OAStackViewDistributionEqualSpacing; 102 | } 103 | 104 | - (IBAction)distributionEqualCentering:(UIButton *)sender { 105 | self.stackView.distribution = OAStackViewDistributionEqualCentering; 106 | } 107 | 108 | - (IBAction)marginsTapped:(UIButton *)sender { 109 | switch (sender.tag) { 110 | case 100: 111 | self.stackView.layoutMarginsRelativeArrangement = YES; 112 | self.stackView.layoutMargins = UIEdgeInsetsMake(10, 0, 10, 0); 113 | break; 114 | case 200: 115 | self.stackView.layoutMarginsRelativeArrangement = YES; 116 | self.stackView.layoutMargins = UIEdgeInsetsMake(10, 20, 30, 40); 117 | break; 118 | default: 119 | self.stackView.layoutMarginsRelativeArrangement = NO; 120 | self.stackView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); 121 | break; 122 | } 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /Example/OAStackView/OAUIStackViewViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAUIStackViewViewController.h 3 | // OAStackView 4 | // 5 | // Created by Michal Zaborowski on 15.10.2015. 6 | // Copyright © 2015 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OAUIStackViewViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/OAStackView/OAUIStackViewViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAUIStackViewViewController.m 3 | // OAStackView 4 | // 5 | // Created by Michal Zaborowski on 15.10.2015. 6 | // Copyright © 2015 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import "OAUIStackViewViewController.h" 10 | 11 | @interface OAUIStackViewViewController () 12 | @property (weak, nonatomic) IBOutlet UIStackView *stackView; 13 | @property (weak, nonatomic) IBOutlet UIView *viewToRemove; 14 | @end 15 | 16 | @implementation OAUIStackViewViewController 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | 22 | NSLog(@"%@: stackView implemented by %@ (%@)", 23 | NSStringFromClass([self class]), 24 | NSStringFromClass([self.stackView class]), 25 | NSStringFromClass([self.stackView superclass])); 26 | } 27 | 28 | - (void)didReceiveMemoryWarning 29 | { 30 | [super didReceiveMemoryWarning]; 31 | // Dispose of any resources that can be recreated. 32 | } 33 | - (IBAction)twoTapped:(UIButton*)sender { 34 | sender.hidden = YES; 35 | } 36 | 37 | - (IBAction)oneTapped:(UIButton*)sender { 38 | sender.hidden = YES; 39 | } 40 | 41 | - (IBAction)threeTapped:(UIButton*)sender { 42 | sender.hidden = YES; 43 | } 44 | 45 | 46 | - (IBAction)verticalTapped:(id)sender { 47 | self.stackView.axis = UILayoutConstraintAxisVertical; 48 | } 49 | 50 | - (IBAction)horizontalTapped:(id)sender { 51 | self.stackView.axis = UILayoutConstraintAxisHorizontal; 52 | } 53 | 54 | - (IBAction)spacingTapped:(UIButton*)sender { 55 | self.stackView.spacing = sender.tag; 56 | } 57 | 58 | - (IBAction)hideAll:(UIButton*)sender { 59 | [self.stackView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 60 | [obj setHidden:YES]; 61 | }]; 62 | } 63 | 64 | - (IBAction)showAll:(UIButton*)sender { 65 | // [self.stackView.subviews[1] setHidden:NO]; 66 | [self.stackView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 67 | [obj setHidden:NO]; 68 | }]; 69 | } 70 | 71 | - (IBAction)alignmentFill:(UIButton*)sender { 72 | self.stackView.alignment = UIStackViewAlignmentFill; 73 | } 74 | 75 | - (IBAction)alignmentCenter:(UIButton*)sender { 76 | self.stackView.alignment = UIStackViewAlignmentCenter; 77 | } 78 | 79 | - (IBAction)alignmentTrailing:(UIButton*)sender { 80 | self.stackView.alignment = UIStackViewAlignmentTrailing; 81 | } 82 | 83 | - (IBAction)alignmentLeading:(UIButton*)sender { 84 | self.stackView.alignment = UIStackViewAlignmentLeading; 85 | } 86 | 87 | - (IBAction)distributionFill:(UIButton *)sender { 88 | self.stackView.distribution = UIStackViewDistributionFill; 89 | } 90 | 91 | - (IBAction)distributionFillEqually:(UIButton *)sender { 92 | self.stackView.distribution = UIStackViewDistributionFillEqually; 93 | } 94 | 95 | - (IBAction)distributionFillProportionally:(UIButton *)sender { 96 | self.stackView.distribution = UIStackViewDistributionFillProportionally; 97 | } 98 | 99 | - (IBAction)distributionEqualSpacing:(UIButton *)sender { 100 | self.stackView.distribution = UIStackViewDistributionEqualSpacing; 101 | } 102 | 103 | - (IBAction)distributionEqualCentering:(UIButton *)sender { 104 | self.stackView.distribution = UIStackViewDistributionEqualCentering; 105 | } 106 | 107 | - (IBAction)marginsTapped:(UIButton *)sender { 108 | switch (sender.tag) { 109 | case 100: 110 | self.stackView.layoutMarginsRelativeArrangement = YES; 111 | self.stackView.layoutMargins = UIEdgeInsetsMake(10, 0, 10, 0); 112 | break; 113 | case 200: 114 | self.stackView.layoutMarginsRelativeArrangement = YES; 115 | self.stackView.layoutMargins = UIEdgeInsetsMake(10, 20, 30, 40); 116 | break; 117 | default: 118 | self.stackView.layoutMarginsRelativeArrangement = NO; 119 | self.stackView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); 120 | break; 121 | } 122 | } 123 | 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /Example/OAStackView/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/OAStackView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 06/14/2015. 6 | // Copyright (c) 2014 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "OAAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([OAAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | target 'OAStackView_Example' do 4 | pod "OAStackView", :path => "../" 5 | pod 'Reveal-iOS-SDK', '~> 1.6.2' 6 | end 7 | 8 | target 'OAStackView_Tests' do 9 | pod "OAStackView", :path => "../" 10 | pod 'Kiwi' 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kiwi (2.4.0) 3 | - OAStackView (1.0.1) 4 | - Reveal-iOS-SDK (1.6.2) 5 | 6 | DEPENDENCIES: 7 | - Kiwi 8 | - OAStackView (from `../`) 9 | - Reveal-iOS-SDK (~> 1.6.2) 10 | 11 | EXTERNAL SOURCES: 12 | OAStackView: 13 | :path: ../ 14 | 15 | SPEC CHECKSUMS: 16 | Kiwi: f49c9d54b28917df5928fe44968a39ed198cb8a8 17 | OAStackView: 752a0324a97b7e7defe5dd4fc435a906fe643522 18 | Reveal-iOS-SDK: e2250b3c155bcfac53ae223ddc1f76d08f206c33 19 | 20 | PODFILE CHECKSUM: a62bace25805ef248de941b6009169961f3b912e 21 | 22 | COCOAPODS: 1.1.1 23 | -------------------------------------------------------------------------------- /Example/Tests/OAStackViewAlignmentSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewAlignmentSpec.m 3 | // 4 | // 5 | // Created by Omar Abdelhafith on 08/08/2015. 6 | // Copyright 2015 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OAStackView.h" 11 | #import "TestHelpers.h" 12 | 13 | 14 | SPEC_BEGIN(OAStackViewAlignmentSpec) 15 | 16 | describe(@"OAStackViewAlignment", ^{ 17 | 18 | __block OAStackView *stackView; 19 | 20 | context(@"Alignemnt", ^{ 21 | 22 | __block UIView *view1, *view2, *view3; 23 | 24 | beforeEach(^{ 25 | view1 = createViewP(200, 300, 20, 250); 26 | view2 = createViewP(500, 300, 30, 250); 27 | view3 = createViewP(600, 330, 40, 250); 28 | 29 | NSArray *views = @[view1, view2, view3]; 30 | 31 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 32 | stackView.axis = UILayoutConstraintAxisVertical; 33 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 34 | }); 35 | 36 | it(@"Arrange the views to fill the whole axis if OAStackViewAlignmentFill is passed", ^{ 37 | stackView.alignment = OAStackViewAlignmentFill; 38 | layoutView(stackView); 39 | 40 | [[theValue(CGRectGetMinX(view3.frame)) should] equal:theValue(0)]; 41 | [[theValue(CGRectGetWidth(view3.frame)) should] equal:theValue(600)]; 42 | 43 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(0)]; 44 | [[theValue(CGRectGetWidth(view2.frame)) should] equal:theValue(600)]; 45 | }); 46 | 47 | it(@"Arrange the views on the begginning of the axis if OAStackViewAlignmentLeading is passed", ^{ 48 | stackView.alignment = OAStackViewAlignmentLeading; 49 | layoutView(stackView); 50 | 51 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(0)]; 52 | [[theValue(CGRectGetWidth(view1.frame)) should] equal:theValue(200)]; 53 | 54 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(0)]; 55 | [[theValue(CGRectGetWidth(view2.frame)) should] equal:theValue(500)]; 56 | }); 57 | 58 | it(@"Arrange the views on the End of the axis if OAStackViewAlignmentTrailing is passed", ^{ 59 | stackView.alignment = OAStackViewAlignmentTrailing;; 60 | layoutView(stackView); 61 | 62 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(400)]; 63 | [[theValue(CGRectGetWidth(view1.frame)) should] equal:theValue(200)]; 64 | 65 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(100)]; 66 | [[theValue(CGRectGetWidth(view2.frame)) should] equal:theValue(500)]; 67 | }); 68 | 69 | it(@"Arrange the views on the Center of the axis if OAStackViewAlignmentCenter is passed", ^{ 70 | stackView.alignment = OAStackViewAlignmentCenter; 71 | layoutView(stackView); 72 | 73 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(200)]; 74 | [[theValue(CGRectGetWidth(view1.frame)) should] equal:theValue(200)]; 75 | 76 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(50)]; 77 | [[theValue(CGRectGetWidth(view2.frame)) should] equal:theValue(500)]; 78 | }); 79 | 80 | }); 81 | 82 | 83 | context(@"Baseline alignment", ^{ 84 | __block UILabel *label; 85 | __block UIView *view; 86 | 87 | beforeEach(^{ 88 | label = createLabel(150, 1000); 89 | view = createViewP(100, 300, 30, 250); 90 | 91 | NSArray *views = @[label, view]; 92 | 93 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 94 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 95 | stackView.axis = UILayoutConstraintAxisHorizontal; 96 | stackView.distribution = OAStackViewDistributionFill; 97 | }); 98 | 99 | it(@"Arrange the view based on the first baseline", ^{ 100 | 101 | if (OA_SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) { 102 | stackView.alignment = OAStackViewAlignmentFirstBaseline; 103 | label.text = @"hello world hello world hello world hello world "; 104 | label.numberOfLines = 0; 105 | 106 | layoutView(stackView); 107 | 108 | [[theValue(CGRectGetHeight(view.frame)) shouldNot] equal:theValue(CGRectGetHeight(label.frame))]; 109 | [[theValue(CGRectGetMinY(view.frame)) should] equal:theValue(0)]; 110 | } 111 | }); 112 | 113 | it(@"Arrange the view based on the last baseline", ^{ 114 | stackView.alignment = OAStackViewAlignmentLastBaseline; 115 | label.text = @"hello world hello world hello world hello world "; 116 | label.numberOfLines = 0; 117 | 118 | layoutView(stackView); 119 | 120 | [[theValue(CGRectGetHeight(view.frame)) shouldNot] equal:theValue(CGRectGetHeight(label.frame))]; 121 | [[theValue(CGRectGetMinY(view.frame)) shouldNot] equal:theValue(0)]; 122 | 123 | BOOL isBigger = CGRectGetMaxY(view.frame) > CGRectGetMaxY(label.frame); 124 | [[theValue(isBigger) should] beYes]; 125 | }); 126 | 127 | it(@"can arrange multiple views", ^{ 128 | UIView *view2 = createViewP(100, 300, 30, 250); 129 | [stackView addArrangedSubview:view2]; 130 | 131 | stackView.alignment = OAStackViewAlignmentLastBaseline; 132 | label.text = @"hello world hello world hello world hello world "; 133 | label.numberOfLines = 0; 134 | 135 | layoutView(stackView); 136 | 137 | [[theValue(CGRectGetHeight(view.frame)) shouldNot] equal:theValue(CGRectGetHeight(label.frame))]; 138 | [[theValue(CGRectGetMinY(view.frame)) shouldNot] equal:theValue(0)]; 139 | 140 | BOOL isBigger = CGRectGetMaxY(view.frame) > CGRectGetMaxY(label.frame); 141 | [[theValue(isBigger) should] beYes]; 142 | 143 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(CGRectGetMinY(view.frame))]; 144 | [[theValue(CGRectGetMaxY(view2.frame)) should] equal:theValue(CGRectGetMaxY(view.frame))]; 145 | }); 146 | 147 | it(@"can arrange multiple views with different orders", ^{ 148 | UIView *view2 = createViewP(100, 300, 30, 250); 149 | [stackView insertArrangedSubview:view2 atIndex:0]; 150 | 151 | stackView.alignment = OAStackViewAlignmentLastBaseline; 152 | label.text = @"hello world hello world hello world hello world "; 153 | label.numberOfLines = 0; 154 | 155 | layoutView(stackView); 156 | 157 | [[theValue(CGRectGetHeight(view.frame)) shouldNot] equal:theValue(CGRectGetHeight(label.frame))]; 158 | [[theValue(CGRectGetMinY(view.frame)) shouldNot] equal:theValue(0)]; 159 | 160 | BOOL isBigger = CGRectGetMaxY(view.frame) > CGRectGetMaxY(label.frame); 161 | [[theValue(isBigger) should] beYes]; 162 | 163 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(CGRectGetMinY(view.frame))]; 164 | [[theValue(CGRectGetMaxY(view2.frame)) should] equal:theValue(CGRectGetMaxY(view.frame))]; 165 | }); 166 | 167 | }); 168 | 169 | }); 170 | 171 | SPEC_END 172 | -------------------------------------------------------------------------------- /Example/Tests/OAStackViewSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewSpec.m 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 14/06/2015. 6 | // Copyright 2015 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import "Kiwi.h" 10 | #import "OAStackView.h" 11 | #import "TestHelpers.h" 12 | 13 | 14 | SPEC_BEGIN(OAStackViewSpec) 15 | 16 | describe(@"OAStackView", ^{ 17 | __block OAStackView *stackView; 18 | 19 | context(@"Vertical", ^{ 20 | 21 | it(@"Can arrange views vertically", ^{ 22 | NSArray *views = @[createView(100, 20),createView(100, 40)]; 23 | 24 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 25 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 26 | stackView.axis = UILayoutConstraintAxisVertical; 27 | CGSize size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 28 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 60))]; 29 | }); 30 | 31 | it(@"Can change the spacing between views", ^{ 32 | NSArray *views = @[createView(100, 20),createView(100, 40)]; 33 | 34 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 35 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 36 | stackView.axis = UILayoutConstraintAxisVertical; 37 | CGSize size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 38 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 60))]; 39 | 40 | stackView.spacing = 20; 41 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 42 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 80))]; 43 | 44 | stackView.spacing = 50; 45 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 46 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 110))]; 47 | }); 48 | 49 | context(@"Removing views", ^{ 50 | __block UIView *view1, *view2, *view3; 51 | 52 | beforeEach(^{ 53 | view1 = createView(100, 20); 54 | view2 = createView(100, 30); 55 | view3 = createView(100, 40); 56 | 57 | NSArray *views = @[view1, view2, view3]; 58 | 59 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 60 | stackView.axis = UILayoutConstraintAxisVertical; 61 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 62 | }); 63 | 64 | it(@"Can remove the first view", ^{ 65 | 66 | CGSize size; 67 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 68 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 69 | 70 | [stackView removeArrangedSubview:view1]; 71 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 72 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 70))]; 73 | 74 | layoutView(stackView); 75 | 76 | [[theValue(CGRectGetMinX([view2 frame])) should] equal:theValue(0)]; 77 | }); 78 | 79 | it(@"Can remove a middle view", ^{ 80 | CGSize size; 81 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 82 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 83 | 84 | [stackView removeArrangedSubview:view2]; 85 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 86 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 60))]; 87 | 88 | layoutView(stackView); 89 | 90 | [[theValue(CGRectGetMinY([view3 frame])) should] equal:theValue(20)]; 91 | }); 92 | 93 | it(@"Can remove the last view", ^{ 94 | CGSize size; 95 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 96 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 97 | 98 | [stackView removeArrangedSubview:view3]; 99 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 100 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 50))]; 101 | }); 102 | 103 | }); 104 | 105 | context(@"Adding views", ^{ 106 | __block UIView *view1, *view2, *view3; 107 | 108 | beforeEach(^{ 109 | view1 = createView(100, 20); 110 | view2 = createView(100, 30); 111 | view3 = createView(100, 40); 112 | 113 | NSArray *views = @[view1, view2, view3]; 114 | 115 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 116 | stackView.axis = UILayoutConstraintAxisVertical; 117 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 118 | }); 119 | 120 | it(@"Can append a view", ^{ 121 | 122 | CGSize size; 123 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 124 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 125 | 126 | 127 | [stackView addArrangedSubview:createView(100, 40)]; 128 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 129 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 130))]; 130 | }); 131 | 132 | it(@"Can insert a view as first element", ^{ 133 | 134 | CGSize size; 135 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 136 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 137 | 138 | 139 | [stackView insertArrangedSubview:createView(100, 40) atIndex:0]; 140 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 141 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 130))]; 142 | 143 | layoutView(stackView); 144 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(40)]; 145 | 146 | }); 147 | 148 | it(@"Can insert a view in the middle", ^{ 149 | 150 | CGSize size; 151 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 152 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 153 | 154 | 155 | [stackView insertArrangedSubview:createView(100, 40) atIndex:1]; 156 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 157 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 130))]; 158 | 159 | layoutView(stackView); 160 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(60)]; 161 | 162 | }); 163 | 164 | it(@"Can insert a view at last index", ^{ 165 | 166 | CGSize size; 167 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 168 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 90))]; 169 | 170 | 171 | [stackView insertArrangedSubview:createView(100, 40) atIndex:3]; 172 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 173 | [[theValue(size) should] equal:theValue(CGSizeMake(100, 130))]; 174 | }); 175 | }); 176 | 177 | context(@"Hiding views", ^{ 178 | 179 | __block UIView *view1, *view2, *view3; 180 | 181 | beforeEach(^{ 182 | view1 = createViewP(200, 300, 20, 1000); 183 | view2 = createViewP(500, 300, 30, 1000); 184 | view3 = createViewP(600, 330, 40, 1000); 185 | 186 | NSArray *views = @[view1, view2, view3]; 187 | 188 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 189 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 190 | stackView.axis = UILayoutConstraintAxisVertical; 191 | }); 192 | 193 | it(@"Decreases the height when view is hidden", ^{ 194 | stackView.alignment = OAStackViewAlignmentFill; 195 | layoutView(stackView); 196 | 197 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(90)]; 198 | 199 | view1.hidden = YES; 200 | layoutView(stackView); 201 | 202 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(70)]; 203 | }); 204 | 205 | it(@"Adjustes frames when views are hidden", ^{ 206 | stackView.alignment = OAStackViewAlignmentFill; 207 | layoutView(stackView); 208 | 209 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(20)]; 210 | 211 | view1.hidden = YES; 212 | layoutView(stackView); 213 | 214 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(0)]; 215 | }); 216 | 217 | it(@"Adjustes frames when multiple views are hidden", ^{ 218 | stackView.alignment = OAStackViewAlignmentFill; 219 | layoutView(stackView); 220 | 221 | [[theValue(CGRectGetMinY(view3.frame)) should] equal:theValue(50)]; 222 | 223 | view1.hidden = YES; 224 | view2.hidden = YES; 225 | layoutView(stackView); 226 | 227 | [[theValue(CGRectGetMinY(view3.frame)) should] equal:theValue(0)]; 228 | }); 229 | 230 | it(@"Adjustes frames when the first view are hidden and unhidden", ^{ 231 | stackView.alignment = OAStackViewAlignmentFill; 232 | layoutView(stackView); 233 | 234 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(20)]; 235 | 236 | view1.hidden = YES; 237 | layoutView(stackView); 238 | 239 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(0)]; 240 | 241 | view1.hidden = NO; 242 | layoutView(stackView); 243 | 244 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(20)]; 245 | 246 | }); 247 | 248 | it(@"Adjustes frames when all the views are hidden and brought back", ^{ 249 | stackView.alignment = OAStackViewAlignmentFill; 250 | layoutView(stackView); 251 | 252 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(20)]; 253 | 254 | view1.hidden = YES; 255 | view2.hidden = YES; 256 | view3.hidden = YES; 257 | layoutView(stackView); 258 | 259 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(0)]; 260 | 261 | view1.hidden = NO; 262 | view2.hidden = NO; 263 | view3.hidden = NO; 264 | layoutView(stackView); 265 | 266 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(90)]; 267 | 268 | }); 269 | 270 | it(@"Adjustes frames when all the views are hidden and brought back in different orders", ^{ 271 | stackView.alignment = OAStackViewAlignmentFill; 272 | layoutView(stackView); 273 | 274 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(20)]; 275 | 276 | view1.hidden = YES; 277 | view2.hidden = YES; 278 | view3.hidden = YES; 279 | layoutView(stackView); 280 | 281 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(0)]; 282 | 283 | view1.hidden = NO; 284 | view3.hidden = NO; 285 | layoutView(stackView); 286 | 287 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(60)]; 288 | 289 | view2.hidden = NO; 290 | layoutView(stackView); 291 | 292 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(90)]; 293 | 294 | }); 295 | 296 | it(@"Can Unhide a non hidden view normally", ^{ 297 | stackView.alignment = OAStackViewAlignmentFill; 298 | layoutView(stackView); 299 | 300 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(20)]; 301 | 302 | view1.hidden = YES; 303 | view2.hidden = YES; 304 | layoutView(stackView); 305 | 306 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(40)]; 307 | 308 | view2.hidden = NO; 309 | layoutView(stackView); 310 | 311 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(70)]; 312 | 313 | view1.hidden = NO; 314 | layoutView(stackView); 315 | 316 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(90)]; 317 | 318 | }); 319 | 320 | 321 | }); 322 | 323 | 324 | context(@"Distribution", ^{ 325 | 326 | __block UIView *view1, *view2, *view3; 327 | 328 | beforeEach(^{ 329 | view1 = createViewP(200, 300, 50, 250); 330 | view2 = createViewP(500, 300, 60, 250); 331 | view3 = createViewP(600, 330, 100, 250); 332 | 333 | NSArray *views = @[view1, view2, view3]; 334 | 335 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 336 | stackView.axis = UILayoutConstraintAxisVertical; 337 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 338 | }); 339 | 340 | context(@"OAStackViewDistributionFill", ^{ 341 | 342 | it(@"Distributes the view based on their fill using OAStackViewDistributionFill", ^{ 343 | stackView.distribution = OAStackViewDistributionFill; 344 | addHightToView(stackView, 400, 1000); 345 | layoutView(stackView); 346 | 347 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(0)]; 348 | 349 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(240)]; 350 | 351 | [[theValue(CGRectGetMinY(view3.frame)) should] equal:theValue(300)]; 352 | }); 353 | 354 | }); 355 | 356 | context(@"OAStackViewDistributionFillEqually", ^{ 357 | 358 | it(@"Distributes the views equally using OAStackViewDistributionFillEqually", ^{ 359 | stackView.distribution = OAStackViewDistributionFillEqually; 360 | addHightToView(stackView, 400, 1000); 361 | layoutView(stackView); 362 | 363 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(0)]; 364 | 365 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:133 withDelta:1]; 366 | 367 | [[theValue(CGRectGetMinY(view3.frame)) should] equal:266 withDelta:1]; 368 | }); 369 | 370 | it(@"Adds the correct spacing between views", ^{ 371 | stackView.distribution = OAStackViewDistributionFillEqually; 372 | stackView.spacing = 50; 373 | addHightToView(stackView, 400, 1000); 374 | layoutView(stackView); 375 | 376 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(0)]; 377 | 378 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(150)]; 379 | 380 | [[theValue(CGRectGetMinY(view3.frame)) should] equal:theValue(300)]; 381 | }); 382 | 383 | }); 384 | 385 | context(@"OAStackViewDistributionFillProportionally", ^{ 386 | it(@"Distributes the views proportionally based on their intrinsicContentSize", ^{ 387 | // Views 1, 2 and 3 are UIButtons. Changing the title affects their intrinsicContentSize. 388 | // Since we're testing the vertical layout here, we add newlines to affect their intrinsicContentSize height 389 | 390 | [(UIButton *)view1 setTitle:@"the title" forState:UIControlStateNormal]; 391 | [(UIButton *)view2 setTitle:@"the title" forState:UIControlStateNormal]; 392 | [(UIButton *)view3 setTitle:@"the\ntitle" forState:UIControlStateNormal]; 393 | 394 | stackView.distribution = OAStackViewDistributionFillProportionally; 395 | 396 | layoutView(stackView); 397 | CGFloat proportion = view2.intrinsicContentSize.height / view1.intrinsicContentSize.height; 398 | [[theValue(CGRectGetHeight(view2.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetHeight(view1.frame) * proportion)]; 399 | 400 | proportion = view3.intrinsicContentSize.height / view2.intrinsicContentSize.height; 401 | [[theValue(CGRectGetHeight(view3.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetHeight(view2.frame) * proportion)]; 402 | }); 403 | }); 404 | 405 | context(@"OAStackViewDistributionEqualSpacing", ^{ 406 | it(@"Distributes the views with equal spacing between each view", ^{ 407 | // Views 1, 2 and 3 are UIButtons. Changing the title affects their intrinsicContentSize. 408 | 409 | [(UIButton *)view1 setTitle:@"A short title" forState:UIControlStateNormal]; 410 | [(UIButton *)view2 setTitle:@"A bit longer title" forState:UIControlStateNormal]; 411 | [(UIButton *)view3 setTitle:@"A really really really really long title" 412 | forState:UIControlStateNormal]; 413 | 414 | stackView.distribution = OAStackViewDistributionEqualSpacing; 415 | stackView.spacing = 40; 416 | 417 | layoutView(stackView); 418 | 419 | CGFloat firstGap = CGRectGetMinY(view2.frame) - CGRectGetMaxY(view1.frame); 420 | CGFloat secondGap = CGRectGetMinY(view3.frame) - CGRectGetMaxY(view2.frame); 421 | [[theValue(firstGap) should] equal:theValue(secondGap)]; 422 | [[theValue(firstGap) should] beBetween:theValue(stackView.spacing) and:theValue(CGRectGetHeight(stackView.bounds))]; 423 | }); 424 | }); 425 | }); 426 | 427 | context(@"Margins", ^{ 428 | 429 | __block UIView *view1, *view2, *view3; 430 | 431 | beforeEach(^{ 432 | view1 = createView(100, 100); 433 | view2 = createView(100, 100); 434 | view3 = createView(100, 100); 435 | 436 | NSArray *views = @[view1, view2, view3]; 437 | 438 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 439 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 440 | stackView.layoutMarginsRelativeArrangement = YES; 441 | stackView.axis = UILayoutConstraintAxisVertical; 442 | stackView.layoutMargins = UIEdgeInsetsMake(10, 20, 30, 40); 443 | }); 444 | 445 | it(@"Arranges the views relative to margins if set", ^{ 446 | layoutView(stackView); 447 | 448 | [[theValue(view1.frame) should] equal:theValue(CGRectMake(20, 10, 100, 100))]; 449 | [[theValue(view2.frame) should] equal:theValue(CGRectMake(20, 110, 100, 100))]; 450 | [[theValue(view3.frame) should] equal:theValue(CGRectMake(20, 210, 100, 100))]; 451 | 452 | //Frame origin does not return 0,0 for iOS7, This is not a major issue as the stackview has not been added to a view stack. 453 | //Matching on the size would be enough to verify this fix 454 | [[theValue(stackView.frame.size) should] equal:theValue(CGSizeMake(160, 340))]; 455 | }); 456 | }); 457 | 458 | }); 459 | 460 | 461 | context(@"Horizontal", ^{ 462 | 463 | it(@"Can arrange views vertically", ^{ 464 | NSArray *views = @[createView(100, 40),createView(100, 40)]; 465 | 466 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 467 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 468 | stackView.axis = UILayoutConstraintAxisHorizontal; 469 | 470 | CGSize size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 471 | [[theValue(size) should] equal:theValue(CGSizeMake(200, 40))]; 472 | }); 473 | 474 | it(@"Can change the spacing between views", ^{ 475 | NSArray *views = @[createView(100, 40),createView(100, 40)]; 476 | 477 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 478 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 479 | stackView.axis = UILayoutConstraintAxisHorizontal; 480 | 481 | CGSize size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 482 | [[theValue(size) should] equal:theValue(CGSizeMake(200, 40))]; 483 | 484 | stackView.spacing = 20; 485 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 486 | [[theValue(size) should] equal:theValue(CGSizeMake(220, 40))]; 487 | 488 | stackView.spacing = 50; 489 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 490 | [[theValue(size) should] equal:theValue(CGSizeMake(250, 40))]; 491 | }); 492 | 493 | context(@"Removing views", ^{ 494 | __block UIView *view1, *view2, *view3; 495 | 496 | beforeEach(^{ 497 | view1 = createView(20, 100); 498 | view2 = createView(30, 100); 499 | view3 = createView(40, 100); 500 | 501 | NSArray *views = @[view1, view2, view3]; 502 | 503 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 504 | stackView.axis = UILayoutConstraintAxisHorizontal; 505 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 506 | }); 507 | 508 | it(@"Can remove the first view", ^{ 509 | 510 | CGSize size; 511 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 512 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 513 | 514 | [stackView removeArrangedSubview:view1]; 515 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 516 | [[theValue(size) should] equal:theValue(CGSizeMake(70, 100))]; 517 | 518 | layoutView(stackView); 519 | 520 | [[theValue(CGRectGetMinX([view2 frame])) should] equal:theValue(0)]; 521 | }); 522 | 523 | it(@"Can remove a middle view", ^{ 524 | CGSize size; 525 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 526 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 527 | 528 | [stackView removeArrangedSubview:view2]; 529 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 530 | [[theValue(size) should] equal:theValue(CGSizeMake(60, 100))]; 531 | 532 | layoutView(stackView); 533 | 534 | [[theValue(CGRectGetMinX([view3 frame])) should] equal:theValue(20)]; 535 | }); 536 | 537 | it(@"Can remove the last view", ^{ 538 | CGSize size; 539 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 540 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 541 | 542 | [stackView removeArrangedSubview:view3]; 543 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 544 | [[theValue(size) should] equal:theValue(CGSizeMake(50, 100))]; 545 | }); 546 | 547 | }); 548 | 549 | 550 | context(@"Adding views", ^{ 551 | __block UIView *view1, *view2, *view3; 552 | 553 | beforeEach(^{ 554 | view1 = createView(20, 100 ); 555 | view2 = createView(30, 100); 556 | view3 = createView(40, 100); 557 | 558 | NSArray *views = @[view1, view2, view3]; 559 | 560 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 561 | stackView.axis = UILayoutConstraintAxisHorizontal; 562 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 563 | }); 564 | 565 | it(@"Can append a view", ^{ 566 | 567 | CGSize size; 568 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 569 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 570 | 571 | 572 | [stackView addArrangedSubview:createView(40, 100)]; 573 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 574 | [[theValue(size) should] equal:theValue(CGSizeMake(130, 100))]; 575 | }); 576 | 577 | it(@"Can insert a view as first element", ^{ 578 | 579 | CGSize size; 580 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 581 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 582 | 583 | 584 | [stackView insertArrangedSubview:createView(40, 100) atIndex:0]; 585 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 586 | [[theValue(size) should] equal:theValue(CGSizeMake(130, 100))]; 587 | 588 | layoutView(stackView); 589 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(40)]; 590 | 591 | }); 592 | 593 | it(@"Can insert a view in the middle", ^{ 594 | 595 | CGSize size; 596 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 597 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 598 | 599 | 600 | [stackView insertArrangedSubview:createView(40, 100) atIndex:1]; 601 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 602 | [[theValue(size) should] equal:theValue(CGSizeMake(130, 100))]; 603 | 604 | layoutView(stackView); 605 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(60)]; 606 | 607 | }); 608 | 609 | it(@"Can insert a view at last index", ^{ 610 | 611 | CGSize size; 612 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 613 | [[theValue(size) should] equal:theValue(CGSizeMake(90, 100))]; 614 | 615 | [stackView insertArrangedSubview:createView(40, 100) atIndex:3]; 616 | size = [stackView systemLayoutSizeFittingSize:CGSizeZero]; 617 | [[theValue(size) should] equal:theValue(CGSizeMake(130, 100))]; 618 | }); 619 | 620 | }); 621 | 622 | 623 | context(@"Alignemnt", ^{ 624 | 625 | __block UIView *view1, *view2, *view3; 626 | 627 | beforeEach(^{ 628 | view1 = createViewP(20, 250, 200, 300); 629 | view2 = createViewP(30, 250, 500, 300); 630 | view3 = createViewP(40, 250, 600, 330); 631 | 632 | NSArray *views = @[view1, view2, view3]; 633 | 634 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 635 | stackView.axis = UILayoutConstraintAxisHorizontal; 636 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 637 | }); 638 | 639 | it(@"Arrange the views to fill the whole axis if OAStackViewAlignmentFill is passed", ^{ 640 | stackView.alignment = OAStackViewAlignmentFill; 641 | layoutView(stackView); 642 | 643 | [[theValue(CGRectGetMinY(view3.frame)) should] equal:theValue(0)]; 644 | [[theValue(CGRectGetHeight(view3.frame)) should] equal:theValue(600)]; 645 | 646 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(0)]; 647 | [[theValue(CGRectGetHeight(view2.frame)) should] equal:theValue(600)]; 648 | }); 649 | 650 | it(@"Arrange the views on the begginning of the axis if OAStackViewAlignmentLeading is passed", ^{ 651 | stackView.alignment = OAStackViewAlignmentLeading; 652 | layoutView(stackView); 653 | 654 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(0)]; 655 | [[theValue(CGRectGetHeight(view1.frame)) should] equal:theValue(200)]; 656 | 657 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(0)]; 658 | [[theValue(CGRectGetHeight(view2.frame)) should] equal:theValue(500)]; 659 | }); 660 | 661 | it(@"Arrange the views on the End of the axis if OAStackViewAlignmentTrailing is passed", ^{ 662 | stackView.alignment = OAStackViewAlignmentTrailing;; 663 | layoutView(stackView); 664 | 665 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(400)]; 666 | [[theValue(CGRectGetHeight(view1.frame)) should] equal:theValue(200)]; 667 | 668 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(100)]; 669 | [[theValue(CGRectGetHeight(view2.frame)) should] equal:theValue(500)]; 670 | }); 671 | 672 | it(@"Arrange the views on the Center of the axis if OAStackViewAlignmentCenter is passed", ^{ 673 | stackView.alignment = OAStackViewAlignmentCenter; 674 | layoutView(stackView); 675 | 676 | [[theValue(CGRectGetMinY(view1.frame)) should] equal:theValue(200)]; 677 | [[theValue(CGRectGetHeight(view1.frame)) should] equal:theValue(200)]; 678 | 679 | [[theValue(CGRectGetMinY(view2.frame)) should] equal:theValue(50)]; 680 | [[theValue(CGRectGetHeight(view2.frame)) should] equal:theValue(500)]; 681 | }); 682 | 683 | }); 684 | 685 | 686 | context(@"Hiding views", ^{ 687 | 688 | __block UIView *view1, *view2, *view3; 689 | 690 | beforeEach(^{ 691 | view1 = createViewP(20, 1000, 200, 300); 692 | view2 = createViewP(30, 1000, 500, 300); 693 | view3 = createViewP(40, 1000, 600, 330); 694 | 695 | NSArray *views = @[view1, view2, view3]; 696 | 697 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 698 | stackView.axis = UILayoutConstraintAxisHorizontal; 699 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 700 | }); 701 | 702 | it(@"Decreases the height when view is hidden", ^{ 703 | stackView.alignment = OAStackViewAlignmentFill; 704 | layoutView(stackView); 705 | 706 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(90)]; 707 | 708 | view1.hidden = YES; 709 | layoutView(stackView); 710 | 711 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(70)]; 712 | }); 713 | 714 | it(@"Adjustes frames when views are hidden", ^{ 715 | stackView.alignment = OAStackViewAlignmentFill; 716 | layoutView(stackView); 717 | 718 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(20)]; 719 | 720 | view1.hidden = YES; 721 | layoutView(stackView); 722 | 723 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(0)]; 724 | }); 725 | 726 | it(@"Adjustes frames when multiple views are hidden", ^{ 727 | stackView.alignment = OAStackViewAlignmentFill; 728 | layoutView(stackView); 729 | 730 | [[theValue(CGRectGetMinX(view3.frame)) should] equal:theValue(50)]; 731 | 732 | view1.hidden = YES; 733 | view2.hidden = YES; 734 | layoutView(stackView); 735 | 736 | [[theValue(CGRectGetMinX(view3.frame)) should] equal:theValue(0)]; 737 | }); 738 | 739 | it(@"Adjustes frames when the first view are hidden and unhidden", ^{ 740 | stackView.alignment = OAStackViewAlignmentFill; 741 | layoutView(stackView); 742 | 743 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(20)]; 744 | 745 | view1.hidden = YES; 746 | layoutView(stackView); 747 | 748 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(0)]; 749 | 750 | view1.hidden = NO; 751 | layoutView(stackView); 752 | 753 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(20)]; 754 | 755 | }); 756 | 757 | it(@"Adjustes frames when all the views are hidden and brought back", ^{ 758 | stackView.alignment = OAStackViewAlignmentFill; 759 | layoutView(stackView); 760 | 761 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(20)]; 762 | 763 | view1.hidden = YES; 764 | view2.hidden = YES; 765 | view3.hidden = YES; 766 | layoutView(stackView); 767 | 768 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(0)]; 769 | 770 | view1.hidden = NO; 771 | view2.hidden = NO; 772 | view3.hidden = NO; 773 | layoutView(stackView); 774 | 775 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(90)]; 776 | 777 | }); 778 | 779 | it(@"Adjustes frames when all the views are hidden and brought back in different orders", ^{ 780 | stackView.alignment = OAStackViewAlignmentFill; 781 | layoutView(stackView); 782 | 783 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(20)]; 784 | 785 | view1.hidden = YES; 786 | view2.hidden = YES; 787 | view3.hidden = YES; 788 | layoutView(stackView); 789 | 790 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(0)]; 791 | 792 | view1.hidden = NO; 793 | view3.hidden = NO; 794 | layoutView(stackView); 795 | 796 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(60)]; 797 | 798 | view2.hidden = NO; 799 | layoutView(stackView); 800 | 801 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(90)]; 802 | 803 | }); 804 | 805 | }); 806 | 807 | 808 | context(@"Distribution", ^{ 809 | 810 | __block UIView *view1, *view2, *view3; 811 | 812 | beforeEach(^{ 813 | view1 = createViewP(50, 300, 200, 300); 814 | view2 = createViewP(60, 300, 500, 300); 815 | view3 = createViewP(100, 300, 600, 330); 816 | 817 | NSArray *views = @[view1, view2, view3]; 818 | 819 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 820 | stackView.axis = UILayoutConstraintAxisHorizontal; 821 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 822 | }); 823 | 824 | context(@"OAStackViewDistributionFill", ^{ 825 | 826 | it(@"Distributes the view based on their fill using OAStackViewDistributionFill", ^{ 827 | stackView.distribution = OAStackViewDistributionFill; 828 | addWidthToView(stackView, 400, 1000); 829 | layoutView(stackView); 830 | 831 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(0)]; 832 | 833 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:240 withDelta:10]; 834 | 835 | [[theValue(CGRectGetMinX(view3.frame)) should] equal:theValue(300)]; 836 | }); 837 | 838 | }); 839 | 840 | context(@"OAStackViewDistributionFillEqually", ^{ 841 | 842 | it(@"Distributes the views equally using OAStackViewDistributionFillEqually", ^{ 843 | stackView.distribution = OAStackViewDistributionFillEqually; 844 | addWidthToView(stackView, 400, 1000); 845 | layoutView(stackView); 846 | 847 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(0)]; 848 | 849 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:133 withDelta:1]; 850 | 851 | [[theValue(CGRectGetMinX(view3.frame)) should] equal:266 withDelta:1]; 852 | }); 853 | 854 | it(@"Adds the correct spacing between views", ^{ 855 | stackView.distribution = OAStackViewDistributionFillEqually; 856 | stackView.spacing = 50; 857 | addWidthToView(stackView, 400, 1000); 858 | layoutView(stackView); 859 | 860 | [[theValue(CGRectGetMinX(view1.frame)) should] equal:theValue(0)]; 861 | 862 | [[theValue(CGRectGetMinX(view2.frame)) should] equal:theValue(150)]; 863 | 864 | [[theValue(CGRectGetMinX(view3.frame)) should] equal:theValue(300)]; 865 | }); 866 | 867 | }); 868 | 869 | context(@"OAStackViewDistributionFillProportionally", ^{ 870 | it(@"Distributes the views proportionally based on their intrinsicContentSize", ^{ 871 | // Views 1, 2 and 3 are UIButtons. Changing the title affects their intrinsicContentSize. 872 | 873 | [(UIButton *)view1 setTitle:@"the title" forState:UIControlStateNormal]; 874 | [(UIButton *)view2 setTitle:@"the title" forState:UIControlStateNormal]; 875 | [(UIButton *)view3 setTitle:@"the title the title" forState:UIControlStateNormal]; 876 | 877 | stackView.distribution = OAStackViewDistributionFillProportionally; 878 | 879 | layoutView(stackView); 880 | CGFloat proportion = view2.intrinsicContentSize.width / view1.intrinsicContentSize.width; 881 | [[theValue(CGRectGetWidth(view2.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetWidth(view1.frame) * proportion)]; 882 | 883 | proportion = view3.intrinsicContentSize.width / view2.intrinsicContentSize.width; 884 | [[theValue(CGRectGetWidth(view3.frame)) should] beWithin:theValue(1) of:theValue(CGRectGetWidth(view2.frame) * proportion)]; 885 | }); 886 | }); 887 | 888 | context(@"OAStackViewDistributionEqualSpacing", ^{ 889 | it(@"Distributes the views with equal spacing between each view", ^{ 890 | // Views 1, 2 and 3 are UIButtons. Changing the title affects their intrinsicContentSize. 891 | 892 | [(UIButton *)view1 setTitle:@"A short title" forState:UIControlStateNormal]; 893 | [(UIButton *)view2 setTitle:@"A bit longer title" forState:UIControlStateNormal]; 894 | [(UIButton *)view3 setTitle:@"A really really really really long title" 895 | forState:UIControlStateNormal]; 896 | 897 | stackView.distribution = OAStackViewDistributionEqualSpacing; 898 | stackView.spacing = 40; 899 | 900 | layoutView(stackView); 901 | 902 | CGFloat firstGap = CGRectGetMinX(view2.frame) - CGRectGetMaxX(view1.frame); 903 | CGFloat secondGap = CGRectGetMinX(view3.frame) - CGRectGetMaxX(view2.frame); 904 | [[theValue(firstGap) should] equal:theValue(secondGap)]; 905 | [[theValue(firstGap) should] beBetween:theValue(stackView.spacing) 906 | and:theValue(CGRectGetWidth(stackView.bounds))]; 907 | }); 908 | }); 909 | 910 | }); 911 | 912 | }); 913 | 914 | 915 | context(@"Margins", ^{ 916 | 917 | __block UIView *view1, *view2, *view3; 918 | 919 | beforeEach(^{ 920 | view1 = createView(100, 100); 921 | view2 = createView(100, 100); 922 | view3 = createView(100, 100); 923 | 924 | NSArray *views = @[view1, view2, view3]; 925 | 926 | stackView = [[OAStackView alloc] initWithArrangedSubviews:views]; 927 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 928 | stackView.axis = UILayoutConstraintAxisHorizontal; 929 | stackView.layoutMarginsRelativeArrangement = YES; 930 | stackView.layoutMargins = UIEdgeInsetsMake(10, 20, 30, 40); 931 | }); 932 | 933 | it(@"Arranges the views relative to margins if set", ^{ 934 | layoutView(stackView); 935 | 936 | [[theValue(view1.frame) should] equal:theValue(CGRectMake(20, 10, 100, 100))]; 937 | [[theValue(view2.frame) should] equal:theValue(CGRectMake(120, 10, 100, 100))]; 938 | [[theValue(view3.frame) should] equal:theValue(CGRectMake(220, 10, 100, 100))]; 939 | 940 | //Frame origin does not return 0,0 for iOS7, This is not a major issue as the stackview has not been added to a view stack. 941 | //Matching on the size would be enough to verify this fix 942 | [[theValue(stackView.frame.size) should] equal:theValue(CGSizeMake(360, 140))]; 943 | }); 944 | }); 945 | 946 | 947 | context(@"Arranged Subviews", ^{ 948 | 949 | __block UIView *view1, *view2, *view3; 950 | 951 | beforeEach(^{ 952 | view1 = createView(100, 100); 953 | view2 = createView(100, 100); 954 | view3 = createView(100, 100); 955 | 956 | stackView = [[OAStackView alloc] init]; 957 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 958 | }); 959 | 960 | it(@"Initializes arrangedSubviews to an empty array", ^{ 961 | [[stackView.arrangedSubviews should] beEmpty]; 962 | }); 963 | 964 | it(@"Initalizes arrangedSubviews to the given views when initialized with arranged subviess", ^{ 965 | stackView = [[OAStackView alloc] initWithArrangedSubviews:@[view1, view2, view3]]; 966 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view1, view2, view3]]; 967 | }); 968 | 969 | it(@"Maintains the correct array when adding views", ^{ 970 | [stackView addArrangedSubview:view1]; 971 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view1]]; 972 | 973 | [stackView addArrangedSubview:view2]; 974 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view2]]; 975 | 976 | [stackView addArrangedSubview:view3]; 977 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view3]]; 978 | }); 979 | 980 | it(@"Maintains the correct array when removing views", ^{ 981 | [stackView addArrangedSubview:view1]; 982 | [stackView addArrangedSubview:view2]; 983 | [stackView addArrangedSubview:view3]; 984 | 985 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view1, view2, view3]]; 986 | 987 | [stackView removeArrangedSubview:view2]; 988 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view1, view3]]; 989 | 990 | [stackView removeArrangedSubview:view1]; 991 | [[stackView.arrangedSubviews should] containObjectsInArray:@[view3]]; 992 | 993 | [stackView removeArrangedSubview:view3]; 994 | [[stackView.arrangedSubviews should] containObjectsInArray:@[]]; 995 | }); 996 | }); 997 | 998 | 999 | context(@"bug fixes", ^{ 1000 | 1001 | __block UIView *view1, *view2, *view3; 1002 | 1003 | beforeEach(^{ 1004 | view1 = createView(20, 100); 1005 | view2 = createView(30, 100); 1006 | view3 = createView(40, 100); 1007 | 1008 | stackView = [[OAStackView alloc] initWithArrangedSubviews:@[]]; 1009 | stackView.axis = UILayoutConstraintAxisHorizontal; 1010 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 1011 | }); 1012 | 1013 | it(@"should add the trailing and leading superview constraint in case insert the first view", ^{ 1014 | [stackView insertArrangedSubview:view1 atIndex:0]; 1015 | [stackView insertArrangedSubview:view2 atIndex:1]; 1016 | layoutView(stackView); 1017 | [[theValue(CGRectGetHeight(stackView.frame)) should] equal:theValue(100)]; 1018 | [[theValue(CGRectGetWidth(stackView.frame)) should] equal:theValue(50)]; 1019 | }); 1020 | 1021 | }); 1022 | }); 1023 | 1024 | SPEC_END -------------------------------------------------------------------------------- /Example/Tests/TestHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestHelpers.h 3 | // 4 | // 5 | // Created by Omar Abdelhafith on 08/08/2015. 6 | // 7 | // 8 | 9 | #import 10 | 11 | UIView *createView(float width, float height); 12 | UIView *createViewP(float width, float widthPriority, float height, float heightPriority); 13 | 14 | void addHightToView(UIView *view, float height, float heightPriority); 15 | void addWidthToView(UIView *view, float width, float widthPriority); 16 | 17 | void layoutView(UIView* view); 18 | 19 | UILabel *createLabel(float width, float widthPriority); 20 | 21 | #define OA_SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) 22 | -------------------------------------------------------------------------------- /Example/Tests/TestHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestHelpers.m 3 | // 4 | // 5 | // Created by Omar Abdelhafith on 08/08/2015. 6 | // 7 | // 8 | 9 | #import "TestHelpers.h" 10 | 11 | 12 | UIColor *randomColor() { 13 | CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 14 | CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white 15 | CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black 16 | UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; 17 | return color; 18 | } 19 | 20 | UIView *createView(float width, float height) { 21 | return createViewP(width, 1000, height, 1000); 22 | } 23 | 24 | UIView *createViewP(float width, float widthPriority, float height, float heightPriority) { 25 | UIColor *color = randomColor(); 26 | UIButton *view = [UIButton new]; 27 | view.translatesAutoresizingMaskIntoConstraints = NO; 28 | [view setTitle:@"the title" forState:UIControlStateNormal]; 29 | NSString *constraintStr = [NSString stringWithFormat:@"V:[view(%f@%f)]", height, heightPriority]; 30 | [view addConstraints: 31 | [NSLayoutConstraint constraintsWithVisualFormat:constraintStr 32 | options:0 33 | metrics:0 34 | views:NSDictionaryOfVariableBindings(view)]]; 35 | 36 | constraintStr = [NSString stringWithFormat:@"H:[view(%f@%f)]", width, widthPriority]; 37 | [view addConstraints: 38 | [NSLayoutConstraint constraintsWithVisualFormat:constraintStr 39 | options:0 40 | metrics:0 41 | views:NSDictionaryOfVariableBindings(view)]]; 42 | 43 | view.backgroundColor = color; 44 | return view; 45 | } 46 | 47 | UILabel *createLabel(float width, float widthPriority) { 48 | UIColor *color = randomColor(); 49 | UILabel *label = [UILabel new]; 50 | label.translatesAutoresizingMaskIntoConstraints = NO; 51 | 52 | NSString *constraintStr = [NSString stringWithFormat:@"H:[label(%f@%f)]", width, widthPriority]; 53 | [label addConstraints: 54 | [NSLayoutConstraint constraintsWithVisualFormat:constraintStr 55 | options:0 56 | metrics:0 57 | views:NSDictionaryOfVariableBindings(label)]]; 58 | 59 | label.backgroundColor = color; 60 | return label; 61 | } 62 | 63 | void addWidthToView(UIView *view, float width, float widthPriority) { 64 | NSString *constraintStr = [NSString stringWithFormat:@"H:[view(%f@%f)]", width, widthPriority]; 65 | [view addConstraints: 66 | [NSLayoutConstraint constraintsWithVisualFormat:constraintStr 67 | options:0 68 | metrics:0 69 | views:NSDictionaryOfVariableBindings(view)]]; 70 | } 71 | 72 | void addHightToView(UIView *view, float height, float heightPriority) { 73 | NSString *constraintStr = [NSString stringWithFormat:@"V:[view(%f@%f)]", height, heightPriority]; 74 | [view addConstraints: 75 | [NSLayoutConstraint constraintsWithVisualFormat:constraintStr 76 | options:0 77 | metrics:0 78 | views:NSDictionaryOfVariableBindings(view)]]; 79 | } 80 | 81 | void layoutView(UIView* view) { 82 | UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 83 | [window addSubview:view]; 84 | 85 | [window setNeedsLayout]; 86 | [window layoutIfNeeded]; 87 | } -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | #endif 6 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Tests/tests.transcript.txt: -------------------------------------------------------------------------------- 1 | Spec: OAStackViewSpec 2 | 3 | -------------------------------------------------- 4 | Describe: OAStackView 5 | -------------------------------------------------- 6 | 7 | When: Vertical 8 | It: Can arrange views vertically 9 | It: Can change the spacing between views 10 | 11 | When: Removing views 12 | It: Can remove the first view 13 | It: Can remove a middle view 14 | It: Can remove the last view 15 | 16 | When: Adding views 17 | It: Can append a view 18 | It: Can insert a view as first element 19 | It: Can insert a view in the middle 20 | It: Can insert a view at last index 21 | 22 | When: Alignemnt 23 | It: Arrange the views to fill the whole axis if OAStackViewAlignmentFill is passed 24 | It: Arrange the views on the begginning of the axis if OAStackViewAlignmentLeading is passed 25 | It: Arrange the views on the End of the axis if OAStackViewAlignmentTrailing is passed 26 | It: Arrange the views on the Center of the axis if OAStackViewAlignmentCenter is passed 27 | 28 | When: Hiding views 29 | It: Decreases the height when view is hidden 30 | It: Adjustes frames when views are hidden 31 | It: Adjustes frames when multiple views are hidden 32 | It: Adjustes frames when the first view are hidden and unhidden 33 | It: Adjustes frames when all the views are hidden and brought back 34 | It: Adjustes frames when all the views are hidden and brought back in different orders 35 | It: Can Unhide a non hidden view normally 36 | 37 | When: Distribution 38 | 39 | When: OAStackViewDistributionFill 40 | It: Distributes the view based on their fill using OAStackViewDistributionFill 41 | 42 | When: OAStackViewDistributionFillEqually 43 | It: Distributes the views equally using OAStackViewDistributionFillEqually 44 | It: Adds the correct spacing between views 45 | 46 | When: Horizontal 47 | It: Can arrange views vertically 48 | It: Can change the spacing between views 49 | 50 | When: Removing views 51 | It: Can remove the first view 52 | It: Can remove a middle view 53 | It: Can remove the last view 54 | 55 | When: Adding views 56 | It: Can append a view 57 | It: Can insert a view as first element 58 | It: Can insert a view in the middle 59 | It: Can insert a view at last index 60 | 61 | When: Alignemnt 62 | It: Arrange the views to fill the whole axis if OAStackViewAlignmentFill is passed 63 | It: Arrange the views on the begginning of the axis if OAStackViewAlignmentLeading is passed 64 | It: Arrange the views on the End of the axis if OAStackViewAlignmentTrailing is passed 65 | It: Arrange the views on the Center of the axis if OAStackViewAlignmentCenter is passed 66 | 67 | When: Hiding views 68 | It: Decreases the height when view is hidden 69 | It: Adjustes frames when views are hidden 70 | It: Adjustes frames when multiple views are hidden 71 | It: Adjustes frames when the first view are hidden and unhidden 72 | It: Adjustes frames when all the views are hidden and brought back 73 | It: Adjustes frames when all the views are hidden and brought back in different orders 74 | 75 | When: Distribution 76 | 77 | When: OAStackViewDistributionFill 78 | It: Distributes the view based on their fill using OAStackViewDistributionFill 79 | 80 | When: OAStackViewDistributionFillEqually 81 | It: Distributes the views equally using OAStackViewDistributionFillEqually 82 | It: Adds the correct spacing between views 83 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Omar Abdelhafith 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /OAStackView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "OAStackView" 3 | s.version = "1.0.1" 4 | s.summary = "Porting UIStackView to iOS 7+." 5 | s.description = <<-DESC 6 | iOS 9 introduced the very cool [UIStackView](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/), UIStackView can be used to easily create simple and complex layouts. 7 | 8 | As expected `UIStackView` can only be used for iOS9 and up. This project tries to port back the stackview to iOS 6+. 9 | DESC 10 | s.homepage = "https://github.com/oarrabi/OAStackView" 11 | s.license = 'MIT' 12 | s.author = { "Omar Abdelhafith" => "o.arrabi@me.com" } 13 | s.source = { :git => "https://github.com/oarrabi/OAStackView.git", :tag => s.version.to_s } 14 | 15 | s.platform = :ios, '7.0' 16 | s.requires_arc = true 17 | 18 | s.source_files = 'Pod/Classes/**/*' 19 | 20 | end 21 | -------------------------------------------------------------------------------- /OAStackView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3B1224581B4DF25200C02D3A /* _OALayoutGuide.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B12244A1B4DF25200C02D3A /* _OALayoutGuide.h */; }; 11 | 3B1224591B4DF25200C02D3A /* _OALayoutGuide.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B12244B1B4DF25200C02D3A /* _OALayoutGuide.m */; }; 12 | 3B12245A1B4DF25200C02D3A /* OAStackView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B12244C1B4DF25200C02D3A /* OAStackView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 3B12245B1B4DF25200C02D3A /* OAStackView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B12244D1B4DF25200C02D3A /* OAStackView.m */; }; 14 | 3B12245C1B4DF25200C02D3A /* OAStackView+Constraint.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B12244E1B4DF25200C02D3A /* OAStackView+Constraint.h */; }; 15 | 3B12245D1B4DF25200C02D3A /* OAStackView+Constraint.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B12244F1B4DF25200C02D3A /* OAStackView+Constraint.m */; }; 16 | 3B12245E1B4DF25200C02D3A /* OAStackView+Hiding.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B1224501B4DF25200C02D3A /* OAStackView+Hiding.h */; }; 17 | 3B12245F1B4DF25200C02D3A /* OAStackView+Hiding.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B1224511B4DF25200C02D3A /* OAStackView+Hiding.m */; }; 18 | 3B1224601B4DF25200C02D3A /* OAStackView+Traversal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B1224521B4DF25200C02D3A /* OAStackView+Traversal.h */; }; 19 | 3B1224611B4DF25200C02D3A /* OAStackView+Traversal.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B1224531B4DF25200C02D3A /* OAStackView+Traversal.m */; }; 20 | 3B1224621B4DF25200C02D3A /* OAStackViewAlignmentStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B1224541B4DF25200C02D3A /* OAStackViewAlignmentStrategy.h */; }; 21 | 3B1224631B4DF25200C02D3A /* OAStackViewAlignmentStrategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B1224551B4DF25200C02D3A /* OAStackViewAlignmentStrategy.m */; }; 22 | 3B1224641B4DF25200C02D3A /* OAStackViewDistributionStrategy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B1224561B4DF25200C02D3A /* OAStackViewDistributionStrategy.h */; }; 23 | 3B1224651B4DF25200C02D3A /* OAStackViewDistributionStrategy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B1224571B4DF25200C02D3A /* OAStackViewDistributionStrategy.m */; }; 24 | 80BE14951B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.h in Headers */ = {isa = PBXBuildFile; fileRef = 80BE14931B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.h */; }; 25 | 80BE14961B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.m in Sources */ = {isa = PBXBuildFile; fileRef = 80BE14941B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.m */; }; 26 | D871CEF71B685D8500FD5F11 /* OATransformLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D871CEF51B685D8500FD5F11 /* OATransformLayer.h */; }; 27 | D871CEF81B685D8500FD5F11 /* OATransformLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D871CEF61B685D8500FD5F11 /* OATransformLayer.m */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 3B12244A1B4DF25200C02D3A /* _OALayoutGuide.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = _OALayoutGuide.h; path = Pod/Classes/_OALayoutGuide.h; sourceTree = SOURCE_ROOT; }; 32 | 3B12244B1B4DF25200C02D3A /* _OALayoutGuide.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = _OALayoutGuide.m; path = Pod/Classes/_OALayoutGuide.m; sourceTree = SOURCE_ROOT; }; 33 | 3B12244C1B4DF25200C02D3A /* OAStackView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OAStackView.h; path = Pod/Classes/OAStackView.h; sourceTree = SOURCE_ROOT; }; 34 | 3B12244D1B4DF25200C02D3A /* OAStackView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OAStackView.m; path = Pod/Classes/OAStackView.m; sourceTree = SOURCE_ROOT; }; 35 | 3B12244E1B4DF25200C02D3A /* OAStackView+Constraint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OAStackView+Constraint.h"; path = "Pod/Classes/OAStackView+Constraint.h"; sourceTree = SOURCE_ROOT; }; 36 | 3B12244F1B4DF25200C02D3A /* OAStackView+Constraint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OAStackView+Constraint.m"; path = "Pod/Classes/OAStackView+Constraint.m"; sourceTree = SOURCE_ROOT; }; 37 | 3B1224501B4DF25200C02D3A /* OAStackView+Hiding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OAStackView+Hiding.h"; path = "Pod/Classes/OAStackView+Hiding.h"; sourceTree = SOURCE_ROOT; }; 38 | 3B1224511B4DF25200C02D3A /* OAStackView+Hiding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OAStackView+Hiding.m"; path = "Pod/Classes/OAStackView+Hiding.m"; sourceTree = SOURCE_ROOT; }; 39 | 3B1224521B4DF25200C02D3A /* OAStackView+Traversal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OAStackView+Traversal.h"; path = "Pod/Classes/OAStackView+Traversal.h"; sourceTree = SOURCE_ROOT; }; 40 | 3B1224531B4DF25200C02D3A /* OAStackView+Traversal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OAStackView+Traversal.m"; path = "Pod/Classes/OAStackView+Traversal.m"; sourceTree = SOURCE_ROOT; }; 41 | 3B1224541B4DF25200C02D3A /* OAStackViewAlignmentStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OAStackViewAlignmentStrategy.h; path = Pod/Classes/OAStackViewAlignmentStrategy.h; sourceTree = SOURCE_ROOT; }; 42 | 3B1224551B4DF25200C02D3A /* OAStackViewAlignmentStrategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OAStackViewAlignmentStrategy.m; path = Pod/Classes/OAStackViewAlignmentStrategy.m; sourceTree = SOURCE_ROOT; }; 43 | 3B1224561B4DF25200C02D3A /* OAStackViewDistributionStrategy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OAStackViewDistributionStrategy.h; path = Pod/Classes/OAStackViewDistributionStrategy.h; sourceTree = SOURCE_ROOT; }; 44 | 3B1224571B4DF25200C02D3A /* OAStackViewDistributionStrategy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OAStackViewDistributionStrategy.m; path = Pod/Classes/OAStackViewDistributionStrategy.m; sourceTree = SOURCE_ROOT; }; 45 | 80BE14931B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OAStackViewAlignmentStrategyBaseline.h; path = Pod/Classes/OAStackViewAlignmentStrategyBaseline.h; sourceTree = SOURCE_ROOT; }; 46 | 80BE14941B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OAStackViewAlignmentStrategyBaseline.m; path = Pod/Classes/OAStackViewAlignmentStrategyBaseline.m; sourceTree = SOURCE_ROOT; }; 47 | 8298C9231B4188700006C53E /* OAStackView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OAStackView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 8298C93F1B4188F50006C53E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 49 | D871CEF51B685D8500FD5F11 /* OATransformLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OATransformLayer.h; path = Pod/Classes/OATransformLayer.h; sourceTree = SOURCE_ROOT; }; 50 | D871CEF61B685D8500FD5F11 /* OATransformLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OATransformLayer.m; path = Pod/Classes/OATransformLayer.m; sourceTree = SOURCE_ROOT; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 8298C91F1B4188700006C53E /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 8298C9191B4188700006C53E = { 65 | isa = PBXGroup; 66 | children = ( 67 | 8298C9251B4188700006C53E /* OAStackView */, 68 | 8298C9241B4188700006C53E /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 8298C9241B4188700006C53E /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 8298C9231B4188700006C53E /* OAStackView.framework */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | 8298C9251B4188700006C53E /* OAStackView */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 80BE14931B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.h */, 84 | 80BE14941B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.m */, 85 | 3B12244A1B4DF25200C02D3A /* _OALayoutGuide.h */, 86 | 3B12244B1B4DF25200C02D3A /* _OALayoutGuide.m */, 87 | 3B12244C1B4DF25200C02D3A /* OAStackView.h */, 88 | 3B12244D1B4DF25200C02D3A /* OAStackView.m */, 89 | 3B12244E1B4DF25200C02D3A /* OAStackView+Constraint.h */, 90 | 3B12244F1B4DF25200C02D3A /* OAStackView+Constraint.m */, 91 | 3B1224501B4DF25200C02D3A /* OAStackView+Hiding.h */, 92 | 3B1224511B4DF25200C02D3A /* OAStackView+Hiding.m */, 93 | 3B1224521B4DF25200C02D3A /* OAStackView+Traversal.h */, 94 | 3B1224531B4DF25200C02D3A /* OAStackView+Traversal.m */, 95 | 3B1224541B4DF25200C02D3A /* OAStackViewAlignmentStrategy.h */, 96 | 3B1224551B4DF25200C02D3A /* OAStackViewAlignmentStrategy.m */, 97 | 3B1224561B4DF25200C02D3A /* OAStackViewDistributionStrategy.h */, 98 | 3B1224571B4DF25200C02D3A /* OAStackViewDistributionStrategy.m */, 99 | D871CEF51B685D8500FD5F11 /* OATransformLayer.h */, 100 | D871CEF61B685D8500FD5F11 /* OATransformLayer.m */, 101 | 8298C93F1B4188F50006C53E /* Info.plist */, 102 | ); 103 | path = OAStackView; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXHeadersBuildPhase section */ 109 | 8298C9201B4188700006C53E /* Headers */ = { 110 | isa = PBXHeadersBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | 3B12245A1B4DF25200C02D3A /* OAStackView.h in Headers */, 114 | 80BE14951B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.h in Headers */, 115 | D871CEF71B685D8500FD5F11 /* OATransformLayer.h in Headers */, 116 | 3B1224621B4DF25200C02D3A /* OAStackViewAlignmentStrategy.h in Headers */, 117 | 3B12245E1B4DF25200C02D3A /* OAStackView+Hiding.h in Headers */, 118 | 3B1224601B4DF25200C02D3A /* OAStackView+Traversal.h in Headers */, 119 | 3B12245C1B4DF25200C02D3A /* OAStackView+Constraint.h in Headers */, 120 | 3B1224581B4DF25200C02D3A /* _OALayoutGuide.h in Headers */, 121 | 3B1224641B4DF25200C02D3A /* OAStackViewDistributionStrategy.h in Headers */, 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXHeadersBuildPhase section */ 126 | 127 | /* Begin PBXNativeTarget section */ 128 | 8298C9221B4188700006C53E /* OAStackView */ = { 129 | isa = PBXNativeTarget; 130 | buildConfigurationList = 8298C9391B4188700006C53E /* Build configuration list for PBXNativeTarget "OAStackView" */; 131 | buildPhases = ( 132 | 8298C91E1B4188700006C53E /* Sources */, 133 | 8298C91F1B4188700006C53E /* Frameworks */, 134 | 8298C9201B4188700006C53E /* Headers */, 135 | 8298C9211B4188700006C53E /* Resources */, 136 | ); 137 | buildRules = ( 138 | ); 139 | dependencies = ( 140 | ); 141 | name = OAStackView; 142 | productName = OAStackView; 143 | productReference = 8298C9231B4188700006C53E /* OAStackView.framework */; 144 | productType = "com.apple.product-type.framework"; 145 | }; 146 | /* End PBXNativeTarget section */ 147 | 148 | /* Begin PBXProject section */ 149 | 8298C91A1B4188700006C53E /* Project object */ = { 150 | isa = PBXProject; 151 | attributes = { 152 | LastSwiftUpdateCheck = 0730; 153 | LastUpgradeCheck = 0630; 154 | ORGANIZATIONNAME = OAStackView; 155 | TargetAttributes = { 156 | 8298C9221B4188700006C53E = { 157 | CreatedOnToolsVersion = 6.3.2; 158 | }; 159 | }; 160 | }; 161 | buildConfigurationList = 8298C91D1B4188700006C53E /* Build configuration list for PBXProject "OAStackView" */; 162 | compatibilityVersion = "Xcode 3.2"; 163 | developmentRegion = English; 164 | hasScannedForEncodings = 0; 165 | knownRegions = ( 166 | en, 167 | ); 168 | mainGroup = 8298C9191B4188700006C53E; 169 | productRefGroup = 8298C9241B4188700006C53E /* Products */; 170 | projectDirPath = ""; 171 | projectRoot = ""; 172 | targets = ( 173 | 8298C9221B4188700006C53E /* OAStackView */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXResourcesBuildPhase section */ 179 | 8298C9211B4188700006C53E /* Resources */ = { 180 | isa = PBXResourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | 8298C91E1B4188700006C53E /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 3B1224651B4DF25200C02D3A /* OAStackViewDistributionStrategy.m in Sources */, 194 | 3B1224591B4DF25200C02D3A /* _OALayoutGuide.m in Sources */, 195 | 80BE14961B79041F00809203 /* OAStackViewAlignmentStrategyBaseline.m in Sources */, 196 | 3B12245B1B4DF25200C02D3A /* OAStackView.m in Sources */, 197 | 3B1224631B4DF25200C02D3A /* OAStackViewAlignmentStrategy.m in Sources */, 198 | 3B1224611B4DF25200C02D3A /* OAStackView+Traversal.m in Sources */, 199 | 3B12245F1B4DF25200C02D3A /* OAStackView+Hiding.m in Sources */, 200 | D871CEF81B685D8500FD5F11 /* OATransformLayer.m in Sources */, 201 | 3B12245D1B4DF25200C02D3A /* OAStackView+Constraint.m in Sources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXSourcesBuildPhase section */ 206 | 207 | /* Begin XCBuildConfiguration section */ 208 | 8298C9371B4188700006C53E /* Debug */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | ALWAYS_SEARCH_USER_PATHS = NO; 212 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 213 | CLANG_CXX_LIBRARY = "libc++"; 214 | CLANG_ENABLE_MODULES = YES; 215 | CLANG_ENABLE_OBJC_ARC = YES; 216 | CLANG_WARN_BOOL_CONVERSION = YES; 217 | CLANG_WARN_CONSTANT_CONVERSION = YES; 218 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 219 | CLANG_WARN_EMPTY_BODY = YES; 220 | CLANG_WARN_ENUM_CONVERSION = YES; 221 | CLANG_WARN_INT_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_UNREACHABLE_CODE = YES; 224 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 225 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 226 | COPY_PHASE_STRIP = NO; 227 | CURRENT_PROJECT_VERSION = 1; 228 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 229 | ENABLE_STRICT_OBJC_MSGSEND = YES; 230 | GCC_C_LANGUAGE_STANDARD = gnu99; 231 | GCC_DYNAMIC_NO_PIC = NO; 232 | GCC_NO_COMMON_BLOCKS = YES; 233 | GCC_OPTIMIZATION_LEVEL = 0; 234 | GCC_PREPROCESSOR_DEFINITIONS = ( 235 | "DEBUG=1", 236 | "$(inherited)", 237 | ); 238 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 246 | MTL_ENABLE_DEBUG_INFO = YES; 247 | ONLY_ACTIVE_ARCH = YES; 248 | SDKROOT = iphoneos; 249 | TARGETED_DEVICE_FAMILY = "1,2"; 250 | VERSIONING_SYSTEM = "apple-generic"; 251 | VERSION_INFO_PREFIX = ""; 252 | }; 253 | name = Debug; 254 | }; 255 | 8298C9381B4188700006C53E /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | CURRENT_PROJECT_VERSION = 1; 275 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 276 | ENABLE_NS_ASSERTIONS = NO; 277 | ENABLE_STRICT_OBJC_MSGSEND = YES; 278 | GCC_C_LANGUAGE_STANDARD = gnu99; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 281 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 282 | GCC_WARN_UNDECLARED_SELECTOR = YES; 283 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 284 | GCC_WARN_UNUSED_FUNCTION = YES; 285 | GCC_WARN_UNUSED_VARIABLE = YES; 286 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 287 | MTL_ENABLE_DEBUG_INFO = NO; 288 | SDKROOT = iphoneos; 289 | TARGETED_DEVICE_FAMILY = "1,2"; 290 | VALIDATE_PRODUCT = YES; 291 | VERSIONING_SYSTEM = "apple-generic"; 292 | VERSION_INFO_PREFIX = ""; 293 | }; 294 | name = Release; 295 | }; 296 | 8298C93A1B4188700006C53E /* Debug */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | APPLICATION_EXTENSION_API_ONLY = YES; 300 | CLANG_ENABLE_MODULES = YES; 301 | DEFINES_MODULE = YES; 302 | DYLIB_COMPATIBILITY_VERSION = 1; 303 | DYLIB_CURRENT_VERSION = 1; 304 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 305 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 306 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 307 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | SKIP_INSTALL = YES; 312 | SWIFT_OBJC_INTERFACE_HEADER_NAME = ""; 313 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 314 | TARGETED_DEVICE_FAMILY = "1,2"; 315 | }; 316 | name = Debug; 317 | }; 318 | 8298C93B1B4188700006C53E /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | APPLICATION_EXTENSION_API_ONLY = YES; 322 | CLANG_ENABLE_MODULES = YES; 323 | DEFINES_MODULE = YES; 324 | DYLIB_COMPATIBILITY_VERSION = 1; 325 | DYLIB_CURRENT_VERSION = 1; 326 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 327 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 328 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 329 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 330 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 331 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | SKIP_INSTALL = YES; 334 | SWIFT_OBJC_INTERFACE_HEADER_NAME = ""; 335 | TARGETED_DEVICE_FAMILY = "1,2"; 336 | }; 337 | name = Release; 338 | }; 339 | /* End XCBuildConfiguration section */ 340 | 341 | /* Begin XCConfigurationList section */ 342 | 8298C91D1B4188700006C53E /* Build configuration list for PBXProject "OAStackView" */ = { 343 | isa = XCConfigurationList; 344 | buildConfigurations = ( 345 | 8298C9371B4188700006C53E /* Debug */, 346 | 8298C9381B4188700006C53E /* Release */, 347 | ); 348 | defaultConfigurationIsVisible = 0; 349 | defaultConfigurationName = Release; 350 | }; 351 | 8298C9391B4188700006C53E /* Build configuration list for PBXNativeTarget "OAStackView" */ = { 352 | isa = XCConfigurationList; 353 | buildConfigurations = ( 354 | 8298C93A1B4188700006C53E /* Debug */, 355 | 8298C93B1B4188700006C53E /* Release */, 356 | ); 357 | defaultConfigurationIsVisible = 0; 358 | defaultConfigurationName = Release; 359 | }; 360 | /* End XCConfigurationList section */ 361 | }; 362 | rootObject = 8298C91A1B4188700006C53E /* Project object */; 363 | } 364 | -------------------------------------------------------------------------------- /OAStackView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /OAStackView.xcodeproj/xcshareddata/xcschemes/OAStackView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsomar/OAStackView/c886ae815e8dc71ac650f62b002d8c836bb85e6e/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsomar/OAStackView/c886ae815e8dc71ac650f62b002d8c836bb85e6e/Pod/Classes/.gitkeep -------------------------------------------------------------------------------- /Pod/Classes/OAStackView+Constraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView+Constraint.h 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 14/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackView.h" 10 | 11 | @interface OAStackView (Constraint) 12 | 13 | - (NSArray*)constraintsAffectingView:(UIView*)view; 14 | - (NSArray*)constraintsAffectingView:(UIView*)view inAxis:(UILayoutConstraintAxis)axis; 15 | 16 | - (NSArray*)constraintsBetweenView:(UIView*)firstView andView:(UIView*)otherView inAxis:(UILayoutConstraintAxis)axis; 17 | - (NSArray*)constraintsBetweenView:(UIView*)firstView andView:(UIView*)otherView 18 | inAxis:(UILayoutConstraintAxis)axis includeReversed:(BOOL)includeReversed; 19 | 20 | - (NSArray*)firstConstraintAffectingView:(UIView*)superView andView:(UIView*)childView inAxis:(UILayoutConstraintAxis)axis; 21 | - (NSArray*)lastConstraintAffectingView:(UIView*)superView andView:(UIView*)childView inAxis:(UILayoutConstraintAxis)axis; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView+Constraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView+Constraint.m 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 14/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackView+Constraint.h" 10 | 11 | @implementation OAStackView (Constraint) 12 | 13 | - (NSArray*)constraintsBetweenView:(UIView*)firstView andView:(UIView*)otherView 14 | inAxis:(UILayoutConstraintAxis)axis includeReversed:(BOOL)includeReversed { 15 | NSMutableArray *arr = [@[] mutableCopy]; 16 | 17 | for (NSLayoutConstraint *constraint in self.constraints) { 18 | BOOL viewMatches = (firstView == constraint.firstItem && otherView == constraint.secondItem); 19 | 20 | if (includeReversed) { 21 | viewMatches = viewMatches || (firstView == constraint.secondItem && otherView == constraint.firstItem); 22 | } 23 | 24 | BOOL isCorrectAxis = [self isConstraintAttribute:constraint.firstAttribute affectingAxis:axis] || 25 | [self isConstraintAttribute:constraint.secondAttribute affectingAxis:axis]; 26 | 27 | if (viewMatches && isCorrectAxis) { 28 | [arr addObject:constraint]; 29 | } 30 | } 31 | 32 | return arr; 33 | } 34 | 35 | - (NSArray*)firstConstraintAffectingView:(UIView*)superView andView:(UIView*)childView inAxis:(UILayoutConstraintAxis)axis { 36 | return [self constraintAffectingView:superView andView:childView inAxis:axis isLast:NO]; 37 | } 38 | 39 | - (NSArray*)lastConstraintAffectingView:(UIView*)superView andView:(UIView*)childView inAxis:(UILayoutConstraintAxis)axis { 40 | return [self constraintAffectingView:superView andView:childView inAxis:axis isLast:YES]; 41 | } 42 | 43 | - (NSArray*)constraintAffectingView:(UIView*)superView andView:(UIView*)childView inAxis:(UILayoutConstraintAxis)axis isLast:(BOOL)isLast { 44 | if (childView == nil || superView == nil) { 45 | return nil; 46 | } 47 | 48 | NSMutableArray *arr = [@[] mutableCopy]; 49 | 50 | for (NSLayoutConstraint *constraint in self.constraints) { 51 | BOOL isBetweenViews = [self isConstraint:constraint betweenView:superView andView:childView]; 52 | if (!isBetweenViews) { continue; } 53 | 54 | BOOL isOnAxis = [self isConstraint:constraint affectingAxis:axis]; 55 | if (!isOnAxis) { continue; } 56 | 57 | BOOL matches = NO; 58 | 59 | if (axis == UILayoutConstraintAxisHorizontal) { 60 | 61 | if (isLast) { 62 | matches = 63 | (constraint.firstAttribute == NSLayoutAttributeRight || constraint.firstAttribute == NSLayoutAttributeTrailing) && 64 | (constraint.secondAttribute == NSLayoutAttributeRight || constraint.secondAttribute == NSLayoutAttributeTrailing); 65 | } else { 66 | matches = 67 | (constraint.firstAttribute == NSLayoutAttributeLeft || constraint.firstAttribute == NSLayoutAttributeLeading) && 68 | (constraint.secondAttribute == NSLayoutAttributeLeft || constraint.secondAttribute == NSLayoutAttributeLeading); 69 | } 70 | 71 | } else { 72 | 73 | if (isLast) { 74 | matches = 75 | (constraint.firstAttribute == NSLayoutAttributeBottom) && 76 | (constraint.secondAttribute == NSLayoutAttributeBottom); 77 | } else { 78 | matches = 79 | (constraint.firstAttribute == NSLayoutAttributeTop) && 80 | (constraint.secondAttribute == NSLayoutAttributeTop); 81 | } 82 | 83 | } 84 | 85 | if (matches) { 86 | [arr addObject:constraint]; 87 | } 88 | } 89 | 90 | return arr; 91 | } 92 | 93 | - (BOOL)isConstraint:(NSLayoutConstraint*)constraint betweenView:(UIView*)firstView andView:(UIView*)secondView { 94 | return (firstView == constraint.firstItem && secondView == constraint.secondItem) || 95 | (firstView == constraint.secondItem && secondView == constraint.firstItem); 96 | } 97 | 98 | - (NSArray*)constraintsBetweenView:(UIView*)firstView andView:(UIView*)otherView inAxis:(UILayoutConstraintAxis)axis { 99 | return [self constraintsBetweenView:firstView andView:otherView inAxis:axis includeReversed:YES]; 100 | } 101 | 102 | - (NSArray*)constraintsAffectingView:(UIView*)view { 103 | NSMutableArray *arr = [@[] mutableCopy]; 104 | [arr addObjectsFromArray:[self constraintsAffectingView:view inAxis:UILayoutConstraintAxisVertical]]; 105 | [arr addObjectsFromArray:[self constraintsAffectingView:view inAxis:UILayoutConstraintAxisHorizontal]]; 106 | return arr; 107 | } 108 | 109 | - (NSArray*)constraintsAffectingView:(UIView*)view inAxis:(UILayoutConstraintAxis)axis { 110 | NSMutableArray *arr = [@[] mutableCopy]; 111 | 112 | for (NSLayoutConstraint *constraint in self.constraints) { 113 | BOOL viewMatches = (view == constraint.firstItem || view == constraint.secondItem); 114 | BOOL constraintAffectingAxis = [self isConstraint:constraint affectingAxis:axis]; 115 | 116 | if (viewMatches && constraintAffectingAxis) { 117 | [arr addObject:constraint]; 118 | } 119 | } 120 | 121 | return arr; 122 | } 123 | 124 | - (BOOL)isConstraint:(NSLayoutConstraint*)constraint affectingAxis:(UILayoutConstraintAxis)axis { 125 | return [self isConstraintAttribute:constraint.firstAttribute affectingAxis:axis] || 126 | [self isConstraintAttribute:constraint.secondAttribute affectingAxis:axis]; 127 | } 128 | 129 | - (BOOL)isConstraintAttribute:(NSLayoutAttribute)attribute affectingAxis:(UILayoutConstraintAxis)axis { 130 | switch (axis) { 131 | case UILayoutConstraintAxisHorizontal: 132 | return attribute == NSLayoutAttributeLeft || attribute == NSLayoutAttributeRight || 133 | attribute == NSLayoutAttributeLeading || attribute == NSLayoutAttributeTrailing || 134 | attribute == NSLayoutAttributeCenterX || attribute == NSLayoutAttributeWidth; 135 | break; 136 | 137 | case UILayoutConstraintAxisVertical: 138 | return attribute == NSLayoutAttributeTop || attribute == NSLayoutAttributeBottom || 139 | attribute == NSLayoutAttributeCenterY || attribute == NSLayoutAttributeHeight; 140 | break; 141 | 142 | default: 143 | break; 144 | } 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView+Hiding.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView+Hiding.h 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackView.h" 10 | 11 | @interface OAStackView (Hiding) 12 | 13 | - (void)addObserverForView:(UIView*)view; 14 | - (void)addObserverForViews:(NSArray<__kindof UIView *> *)views; 15 | 16 | - (void)removeObserverForView:(UIView*)view; 17 | - (void)removeObserverForViews:(NSArray<__kindof UIView *> *)views; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView+Hiding.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView+Hiding.m 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackView+Hiding.h" 10 | 11 | @interface OAStackView () 12 | - (void)hideView:(UIView*)view; 13 | - (void)unHideView:(UIView*)view; 14 | @end 15 | 16 | @implementation OAStackView (Hiding) 17 | 18 | - (void)addObserverForView:(UIView*)view { 19 | [view addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; 20 | } 21 | 22 | - (void)removeObserverForView:(UIView*)view { 23 | [view removeObserver:self forKeyPath:@"hidden"]; 24 | } 25 | 26 | - (void)addObserverForViews:(NSArray*)views { 27 | for (UIView *view in views) { 28 | [self addObserverForView:view]; 29 | } 30 | } 31 | 32 | - (void)removeObserverForViews:(NSArray<__kindof UIView *> *)views { 33 | for (UIView *view in views) { 34 | [self removeObserverForView:view]; 35 | } 36 | } 37 | 38 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 39 | BOOL isHidden = [change[NSKeyValueChangeNewKey] boolValue]; 40 | BOOL wasHidden = [change[NSKeyValueChangeOldKey] boolValue]; 41 | 42 | if (isHidden == wasHidden) { 43 | return; 44 | } 45 | 46 | if (isHidden) { 47 | [self hideView:object]; 48 | } else { 49 | [self unHideView:object]; 50 | } 51 | 52 | } 53 | 54 | #pragma mark subviews 55 | 56 | - (void)didAddSubview:(UIView *)subview { 57 | [super didAddSubview:subview]; 58 | [self addObserverForView:subview]; 59 | } 60 | 61 | - (void)willRemoveSubview:(UIView *)subview { 62 | [super willRemoveSubview:subview]; 63 | [self removeObserverForView:subview]; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView+Traversal.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView+Traversal.h 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackView.h" 10 | 11 | @interface OAStackView (Traversal) 12 | 13 | - (UIView*)visibleViewBeforeIndex:(NSInteger)index; 14 | - (UIView*)visibleViewBeforeView:(UIView*)view; 15 | 16 | - (UIView*)visibleViewAfterIndex:(NSInteger)index; 17 | - (UIView*)visibleViewAfterView:(UIView*)view; 18 | 19 | - (void)iterateVisibleViews:(void (^) (UIView *view, UIView *previousView))block; 20 | 21 | - (NSArray*)currentVisibleViews; 22 | 23 | - (UIView*)lastVisibleItem; 24 | 25 | - (NSLayoutConstraint*)firstViewConstraint; 26 | - (NSLayoutConstraint*)lastViewConstraint; 27 | 28 | - (BOOL)isViewLastItem:(UIView*)view excludingItem:(UIView*)excludingItem; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView+Traversal.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView+Traversal.m 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackView+Traversal.h" 10 | 11 | @implementation OAStackView (Traversal) 12 | 13 | - (UIView*)visibleViewBeforeView:(UIView*)view { 14 | NSInteger index = [self.subviews indexOfObject:view]; 15 | if (index == NSNotFound) { return nil; } 16 | 17 | return [self visibleViewBeforeIndex:index]; 18 | } 19 | 20 | - (UIView*)visibleViewAfterView:(UIView*)view { 21 | NSInteger index = [self.subviews indexOfObject:view]; 22 | if (index == NSNotFound) { return nil; } 23 | 24 | return [self visibleViewAfterIndex:index]; 25 | } 26 | 27 | - (UIView*)visibleViewAfterIndex:(NSInteger)index { 28 | for (NSInteger i = index + 1; i < self.subviews.count; i++) { 29 | UIView *theView = self.subviews[i]; 30 | if (!theView.hidden) { 31 | return theView; 32 | } 33 | } 34 | 35 | return nil; 36 | } 37 | 38 | - (UIView*)visibleViewBeforeIndex:(NSInteger)index { 39 | for (NSInteger i = index - 1; i >= 0; i--) { 40 | UIView *theView = self.subviews[i]; 41 | if (!theView.hidden) { 42 | return theView; 43 | } 44 | } 45 | 46 | return nil; 47 | } 48 | 49 | - (UIView*)lastVisibleItem { 50 | return [self visibleViewBeforeIndex:self.subviews.count]; 51 | } 52 | 53 | - (void)iterateVisibleViews:(void (^) (UIView *view, UIView *previousView))block { 54 | 55 | id previousView; 56 | for (UIView *view in self.subviews) { 57 | if (view.isHidden) { continue; } 58 | 59 | block(view, previousView); 60 | previousView = view; 61 | } 62 | } 63 | 64 | - (NSArray*)currentVisibleViews { 65 | NSMutableArray *arr = [@[] mutableCopy]; 66 | [self iterateVisibleViews:^(UIView *view, UIView *previousView) { 67 | [arr addObject:view]; 68 | }]; 69 | return arr; 70 | } 71 | 72 | - (BOOL)isLastVisibleItem:(UIView*)view { 73 | return view == [self lastVisibleItem]; 74 | } 75 | 76 | - (NSLayoutConstraint*)lastViewConstraint { 77 | for (NSLayoutConstraint *constraint in self.constraints) { 78 | 79 | if (self.axis == UILayoutConstraintAxisVertical) { 80 | if ( (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeBottom) || 81 | (constraint.secondItem == self && constraint.secondAttribute == NSLayoutAttributeBottom)) { 82 | return constraint; 83 | } 84 | } else { 85 | if ( (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeTrailing) || 86 | (constraint.secondItem == self && constraint.secondAttribute == NSLayoutAttributeTrailing)) { 87 | return constraint; 88 | } 89 | } 90 | 91 | } 92 | return nil; 93 | } 94 | 95 | 96 | - (NSLayoutConstraint*)firstViewConstraint { 97 | for (NSLayoutConstraint *constraint in self.constraints) { 98 | 99 | if (self.axis == UILayoutConstraintAxisVertical) { 100 | if ( (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeTop) || 101 | (constraint.secondItem == self && constraint.secondAttribute == NSLayoutAttributeTop)) { 102 | return constraint; 103 | } 104 | } else { 105 | if ( (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeLeading) || 106 | (constraint.secondItem == self && constraint.secondAttribute == NSLayoutAttributeLeading)) { 107 | return constraint; 108 | } 109 | } 110 | 111 | } 112 | return nil; 113 | } 114 | 115 | - (BOOL)isViewLastItem:(UIView*)view excludingItem:(UIView*)excludingItem { 116 | NSArray<__kindof UIView *> *visible = [self currentVisibleViews]; 117 | NSInteger index = [visible indexOfObject:view]; 118 | NSInteger exclutedIndex = [visible indexOfObject:excludingItem]; 119 | 120 | 121 | return index == visible.count - 1 || 122 | (exclutedIndex == visible.count - 1 && index == visible.count - 2); 123 | } 124 | 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView.h 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 14/06/2015. 6 | // Copyright © 2015 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, OAStackViewDistribution) { 12 | 13 | /* When items do not fit (overflow) or fill (underflow) the space available 14 | adjustments occur according to compressionResistance or hugging 15 | priorities of items, or when that is ambiguous, according to arrangement 16 | order. 17 | */ 18 | OAStackViewDistributionFill = 0, 19 | 20 | /* Items are all the same size. 21 | When space allows, this will be the size of the item with the largest 22 | intrinsicContentSize (along the axis of the stack). 23 | Overflow or underflow adjustments are distributed equally among the items. 24 | */ 25 | OAStackViewDistributionFillEqually, 26 | 27 | /* Overflow or underflow adjustments are distributed among the items proportional 28 | to their intrinsicContentSizes. 29 | */ 30 | OAStackViewDistributionFillProportionally, 31 | 32 | /* Additional underflow spacing is divided equally in the spaces between the items. 33 | Overflow squeezing is controlled by compressionResistance priorities followed by 34 | arrangement order. 35 | */ 36 | OAStackViewDistributionEqualSpacing, 37 | 38 | /* Equal center-to-center spacing of the items is maintained as much 39 | as possible while still maintaining a minimum edge-to-edge spacing within the 40 | allowed area. 41 | Additional underflow spacing is divided equally in the spacing. Overflow 42 | squeezing is distributed first according to compressionResistance priorities 43 | of items, then according to subview order while maintaining the configured 44 | (edge-to-edge) spacing as a minimum. 45 | */ 46 | OAStackViewDistributionEqualCentering, 47 | }; 48 | 49 | typedef NS_ENUM(NSInteger, OAStackViewAlignment) { 50 | OAStackViewAlignmentFill, 51 | 52 | /* Align the leading edges of vertically stacked items 53 | or the top edges of horizontally stacked items tightly to the relevant edge 54 | of the container 55 | */ 56 | OAStackViewAlignmentLeading, 57 | OAStackViewAlignmentTop = OAStackViewAlignmentLeading, 58 | OAStackViewAlignmentFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0), // Valid for horizontal axis only 59 | 60 | /* Center the items in a vertical stack horizontally 61 | or the items in a horizontal stack vertically 62 | */ 63 | OAStackViewAlignmentCenter, 64 | 65 | /* Align the trailing edges of vertically stacked items 66 | or the bottom edges of horizontally stacked items tightly to the relevant 67 | edge of the container 68 | */ 69 | OAStackViewAlignmentTrailing, 70 | OAStackViewAlignmentBottom = OAStackViewAlignmentTrailing, 71 | OAStackViewAlignmentBaseline, 72 | OAStackViewAlignmentLastBaseline = OAStackViewAlignmentBaseline, // Valid for horizontal axis only 73 | }; 74 | 75 | // Keep older versions of the compiler happy 76 | #ifndef NS_ASSUME_NONNULL_BEGIN 77 | #define NS_ASSUME_NONNULL_BEGIN 78 | #define NS_ASSUME_NONNULL_END 79 | #define nullable 80 | #define nonnullable 81 | #define __nullable 82 | #endif 83 | 84 | NS_ASSUME_NONNULL_BEGIN 85 | 86 | // Define the `OAStackViewDisableForwardToUIStackViewSentinel` to disable the automatic forwarding to OAStackView on iOS 7+. 87 | // Copy below line into your AppDelegate.m: 88 | // @interface OAStackViewDisableForwardToUIStackViewSentinel : NSObject @end 89 | // @implementation OAStackViewDisableForwardToUIStackViewSentinel @end 90 | 91 | @interface OAStackView : UIView 92 | 93 | @property(nonatomic,readonly,copy) NSArray<__kindof UIView *> *arrangedSubviews; 94 | 95 | /* A stack with a horizontal axis is a row of arrangedSubviews, 96 | and a stack with a vertical axis is a column of arrangedSubviews. 97 | Default value is `UILayoutConstraintAxisHorizontal`. 98 | */ 99 | @property(nonatomic) UILayoutConstraintAxis axis; 100 | @property(nonatomic) IBInspectable NSInteger axisValue; 101 | 102 | @property(nonatomic) IBInspectable CGFloat spacing; 103 | 104 | //Default is Fill 105 | @property(nonatomic) OAStackViewAlignment alignment; 106 | @property(nonatomic) IBInspectable NSInteger alignmentValue; 107 | 108 | 109 | @property(nonatomic) OAStackViewDistribution distribution; 110 | @property(nonatomic) IBInspectable NSInteger distributionValue; 111 | 112 | //layoutMargins has been added to OAStackView since iOS 7 does not include a layout margin 113 | @property(nonatomic) UIEdgeInsets layoutMargins; 114 | 115 | @property(nonatomic, getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement; 116 | 117 | - (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views NS_DESIGNATED_INITIALIZER; 118 | - (instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER; 119 | 120 | - (void)addArrangedSubview:(UIView *)view; 121 | - (void)removeArrangedSubview:(UIView *)view; 122 | - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex; 123 | 124 | @end 125 | 126 | @interface OAStackViewProxy : UIStackView 127 | @end 128 | 129 | NS_ASSUME_NONNULL_END 130 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackView.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackView.m 3 | // OAStackView 4 | // 5 | // Created by Omar Abdelhafith on 14/06/2015. 6 | // Copyright © 2015 Omar Abdelhafith. All rights reserved. 7 | // 8 | 9 | #import "OAStackView.h" 10 | #import "OAStackView+Constraint.h" 11 | #import "OAStackView+Hiding.h" 12 | #import "OAStackView+Traversal.h" 13 | #import "OAStackViewAlignmentStrategy.h" 14 | #import "OAStackViewDistributionStrategy.h" 15 | #import "OATransformLayer.h" 16 | #import 17 | 18 | @interface OAStackView () 19 | @property(nonatomic, strong) NSMutableArray *mutableArrangedSubviews; 20 | @property(nonatomic) OAStackViewAlignmentStrategy *alignmentStrategy; 21 | @property(nonatomic) OAStackViewDistributionStrategy *distributionStrategy; 22 | 23 | // Not implemented but needed for backward compatibility with UIStackView 24 | @property(nonatomic,getter=isBaselineRelativeArrangement) BOOL baselineRelativeArrangement; 25 | 26 | @property(nonatomic, getter=areNativeLayoutMarginsSupported, readonly) BOOL nativeLayoutMarginsSupported; 27 | 28 | @end 29 | 30 | @implementation OAStackView 31 | 32 | + (Class)layerClass { 33 | return [OATransformLayer class]; 34 | } 35 | 36 | #pragma mark - Initialization 37 | 38 | - (instancetype)initWithCoder:(NSCoder *)decoder { 39 | self = [super initWithCoder:decoder]; 40 | 41 | if (self) { 42 | [self commonInitWithInitalSubviews:@[]]; 43 | 44 | if ([NSStringFromClass([self class]) isEqualToString:@"UIStackView"]) { 45 | self.axis = [decoder decodeIntegerForKey:@"UIStackViewAxis"]; 46 | self.distribution = [decoder decodeIntegerForKey:@"UIStackViewDistribution"]; 47 | self.alignment = [decoder decodeIntegerForKey:@"UIStackViewAlignment"]; 48 | self.spacing = [decoder decodeDoubleForKey:@"UIStackViewSpacing"]; 49 | self.baselineRelativeArrangement = [decoder decodeBoolForKey:@"UIStackViewBaselineRelative"]; 50 | self.layoutMarginsRelativeArrangement = [decoder decodeBoolForKey:@"UIStackViewLayoutMarginsRelative"]; 51 | } 52 | 53 | [self layoutArrangedViews]; 54 | } 55 | 56 | return self; 57 | } 58 | 59 | - (instancetype)initWithArrangedSubviews:(NSArray<__kindof UIView *> *)views { 60 | self = [super initWithFrame:CGRectZero]; 61 | 62 | if (self) { 63 | [self commonInitWithInitalSubviews:views]; 64 | [self layoutArrangedViews]; 65 | } 66 | 67 | return self; 68 | } 69 | 70 | - (instancetype)initWithFrame:(CGRect)frame { 71 | return [self initWithArrangedSubviews:@[]]; 72 | } 73 | 74 | - (void)commonInitWithInitalSubviews:(NSArray *)initialSubviews { 75 | _nativeLayoutMarginsSupported = [UIView instancesRespondToSelector:@selector(layoutMargins)]; 76 | 77 | _mutableArrangedSubviews = [initialSubviews mutableCopy]; 78 | [self addViewsAsSubviews:initialSubviews]; 79 | 80 | _axis = UILayoutConstraintAxisHorizontal; 81 | _alignment = OAStackViewAlignmentFill; 82 | _distribution = OAStackViewDistributionFill; 83 | 84 | _compatibilityLayoutMargins = UIEdgeInsetsMake(0, 8, 0, 8); 85 | _layoutMarginsRelativeArrangement = NO; 86 | 87 | self.alignmentStrategy = [OAStackViewAlignmentStrategy strategyWithStackView:self]; 88 | self.distributionStrategy = [OAStackViewDistributionStrategy strategyWithStackView:self]; 89 | } 90 | 91 | #pragma mark - Properties 92 | 93 | - (NSArray *)arrangedSubviews { 94 | return self.mutableArrangedSubviews.copy; 95 | } 96 | 97 | - (void)setBackgroundColor:(UIColor *)backgroundColor { 98 | // Does not have any effect because `CATransformLayer` is not rendered. 99 | } 100 | 101 | - (void)setOpaque:(BOOL)opaque { 102 | // Does not have any effect because `CATransformLayer` is not rendered. 103 | } 104 | 105 | - (void)setClipsToBounds:(BOOL)clipsToBounds { 106 | // Does not have any effect because `CATransformLayer` is not rendered. 107 | } 108 | 109 | - (void)setSpacing:(CGFloat)spacing { 110 | if (_spacing == spacing) { return; } 111 | 112 | _spacing = spacing; 113 | 114 | for (NSLayoutConstraint *constraint in self.constraints) { 115 | BOOL isWidthOrHeight = 116 | (constraint.firstAttribute == NSLayoutAttributeWidth) || 117 | (constraint.firstAttribute == NSLayoutAttributeHeight); 118 | 119 | if ([self.subviews containsObject:constraint.firstItem] && 120 | [self.subviews containsObject:constraint.secondItem] && 121 | !isWidthOrHeight) { 122 | constraint.constant = spacing; 123 | } 124 | } 125 | } 126 | 127 | - (void)setAxis:(UILayoutConstraintAxis)axis { 128 | if (_axis == axis) { return; } 129 | _axis = axis; 130 | [self layoutArrangedViews]; 131 | } 132 | 133 | - (void)setAxisValue:(NSInteger)axisValue { 134 | _axisValue = axisValue; 135 | self.axis = self.axisValue; 136 | } 137 | 138 | - (void)setAlignment:(OAStackViewAlignment)alignment { 139 | if (_alignment == alignment) { return; } 140 | 141 | _alignment = alignment; 142 | [self setAlignmentConstraints]; 143 | } 144 | 145 | - (void)setAlignmentConstraints { 146 | self.alignmentStrategy = [OAStackViewAlignmentStrategy strategyWithStackView:self]; 147 | 148 | [self.alignmentStrategy alignFirstView:self.subviews.firstObject]; 149 | 150 | [self iterateVisibleViews:^(UIView *view, UIView *previousView) { 151 | [self.alignmentStrategy addConstraintsOnOtherAxis:view]; 152 | [self.alignmentStrategy alignView:view withPreviousView:previousView]; 153 | }]; 154 | 155 | [self.alignmentStrategy alignLastView:self.subviews.lastObject]; 156 | } 157 | 158 | - (void)setAlignmentStrategy:(OAStackViewAlignmentStrategy *)alignmentStrategy { 159 | if ([_alignmentStrategy isEqual:alignmentStrategy]) { 160 | return; 161 | } 162 | 163 | [_alignmentStrategy removeAddedConstraints]; 164 | _alignmentStrategy = alignmentStrategy; 165 | } 166 | 167 | - (void)setDistributionStrategy:(OAStackViewDistributionStrategy *)distributionStrategy { 168 | if ([_distributionStrategy isEqual:distributionStrategy]) { 169 | return; 170 | } 171 | 172 | [_distributionStrategy removeAddedConstraints]; 173 | _distributionStrategy = distributionStrategy; 174 | } 175 | 176 | - (void)removeConstraint:(NSLayoutConstraint *)constraint { 177 | [super removeConstraint:constraint]; 178 | } 179 | 180 | - (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints { 181 | [super removeConstraints:constraints]; 182 | } 183 | 184 | - (void)updateConstraints { 185 | [super updateConstraints]; 186 | } 187 | 188 | - (void)layoutSubviews { 189 | [super layoutSubviews]; 190 | } 191 | 192 | - (void)setAlignmentValue:(NSInteger)alignmentValue { 193 | _alignmentValue = alignmentValue; 194 | self.alignment = alignmentValue; 195 | } 196 | 197 | - (void)setDistribution:(OAStackViewDistribution)distribution { 198 | if (_distribution == distribution) { return; } 199 | 200 | _distribution = distribution; 201 | [self layoutArrangedViews]; 202 | } 203 | 204 | - (void)setDistributionConstraints { 205 | self.distributionStrategy = [OAStackViewDistributionStrategy strategyWithStackView:self]; 206 | 207 | [self iterateVisibleViews:^(UIView *view, UIView *previousView) { 208 | [self.distributionStrategy alignView:view afterView:previousView]; 209 | }]; 210 | 211 | [self.distributionStrategy alignView:nil afterView:[self lastVisibleItem]]; 212 | } 213 | 214 | - (void)setDistributionValue:(NSInteger)distributionValue { 215 | _distributionValue = distributionValue; 216 | self.distribution = distributionValue; 217 | } 218 | 219 | @synthesize layoutMargins = _compatibilityLayoutMargins; 220 | 221 | - (UIEdgeInsets)layoutMargins { 222 | if (self.nativeLayoutMarginsSupported) { 223 | return super.layoutMargins; 224 | } 225 | 226 | return _compatibilityLayoutMargins; 227 | } 228 | 229 | - (void)setLayoutMargins:(UIEdgeInsets)layoutMargins { 230 | if (self.nativeLayoutMarginsSupported) { 231 | [super setLayoutMargins:layoutMargins]; 232 | } else { 233 | _compatibilityLayoutMargins = layoutMargins; 234 | [self layoutArrangedViews]; 235 | } 236 | } 237 | 238 | - (void)setLayoutMarginsRelativeArrangement:(BOOL)layoutMarginsRelativeArrangement { 239 | _layoutMarginsRelativeArrangement = layoutMarginsRelativeArrangement; 240 | [self layoutArrangedViews]; 241 | } 242 | 243 | #pragma mark - Overriden methods 244 | 245 | - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { 246 | [self layoutArrangedViews]; 247 | } 248 | 249 | - (void)layoutMarginsDidChange { 250 | [super layoutMarginsDidChange]; 251 | [self layoutArrangedViews]; 252 | } 253 | 254 | #pragma mark - Adding and removing 255 | 256 | - (void)addArrangedSubview:(UIView *)view { 257 | [self insertArrangedSubview:view atIndex:self.subviews.count]; 258 | } 259 | 260 | - (void)removeArrangedSubview:(UIView *)view { 261 | 262 | if (self.subviews.count == 1) { 263 | [self.mutableArrangedSubviews removeObject:view]; 264 | [view removeFromSuperview]; 265 | return; 266 | } 267 | 268 | [self removeViewFromArrangedViews:view permanently:YES]; 269 | } 270 | 271 | - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex { 272 | [self insertArrangedSubview:view atIndex:stackIndex newItem:YES]; 273 | } 274 | 275 | - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)stackIndex newItem:(BOOL)newItem { 276 | 277 | id previousView, nextView; 278 | view.translatesAutoresizingMaskIntoConstraints = NO; 279 | BOOL isAppending = stackIndex == self.subviews.count; 280 | 281 | if (isAppending) { 282 | //Appending a new item 283 | 284 | previousView = [self lastVisibleItem]; 285 | nextView = nil; 286 | 287 | NSArray<__kindof NSLayoutConstraint *> *constraints = [self lastConstraintAffectingView:self andView:previousView inAxis:self.axis]; 288 | if (constraints) { 289 | [self removeConstraints:constraints]; 290 | } 291 | 292 | if (newItem) { 293 | [self.mutableArrangedSubviews addObject:view]; 294 | [self addSubview:view]; 295 | } 296 | 297 | } else { 298 | //Item insertion 299 | 300 | previousView = [self visibleViewBeforeIndex:stackIndex]; 301 | nextView = [self visibleViewAfterIndex:newItem ? stackIndex - 1: stackIndex]; 302 | 303 | NSArray<__kindof NSLayoutConstraint *> *constraints; 304 | BOOL isLastVisibleItem = [self isViewLastItem:previousView excludingItem:view]; 305 | BOOL isFirstVisibleView = previousView == nil; 306 | BOOL isOnlyItem = previousView == nil && nextView == nil; 307 | 308 | if (isLastVisibleItem) { 309 | constraints = @[[self lastViewConstraint]]; 310 | } else if(isOnlyItem) { 311 | constraints = [self constraintsBetweenView:previousView ?: self andView:nextView ?: self inAxis:self.axis]; 312 | } else if(isFirstVisibleView) { 313 | constraints = @[[self firstViewConstraint]]; 314 | } else { 315 | constraints = [self constraintsBetweenView:previousView ?: self andView:nextView ?: self inAxis:self.axis]; 316 | } 317 | 318 | [self removeConstraints:constraints]; 319 | 320 | if (newItem) { 321 | [self.mutableArrangedSubviews insertObject:view atIndex:stackIndex]; 322 | [self insertSubview:view atIndex:stackIndex]; 323 | } 324 | } 325 | 326 | [self.distributionStrategy alignView:view afterView:previousView]; 327 | [self.alignmentStrategy alignView:view withPreviousView:previousView]; 328 | [self.alignmentStrategy addConstraintsOnOtherAxis:view]; 329 | [self.distributionStrategy alignView:nextView afterView:view]; 330 | [self.alignmentStrategy alignView:nextView withPreviousView:view]; 331 | } 332 | 333 | - (void)removeViewFromArrangedViews:(UIView*)view permanently:(BOOL)permanently { 334 | NSInteger index = [self.subviews indexOfObject:view]; 335 | if (index == NSNotFound) { return; } 336 | 337 | id previousView = [self visibleViewBeforeView:view]; 338 | id nextView = [self visibleViewAfterView:view]; 339 | 340 | if (permanently) { 341 | [self.mutableArrangedSubviews removeObject:view]; 342 | [view removeFromSuperview]; 343 | } else { 344 | NSArray <__kindof NSLayoutConstraint *> *constraint = [self constraintsAffectingView:view]; 345 | [self removeConstraints:constraint]; 346 | } 347 | 348 | if (nextView) { 349 | [self.distributionStrategy alignView:nextView afterView:previousView]; 350 | } else if(previousView) { 351 | [self.distributionStrategy alignView:nil afterView:[self lastVisibleItem]]; 352 | } 353 | } 354 | 355 | #pragma mark - Hide and Unhide 356 | 357 | - (void)hideView:(UIView*)view { 358 | [self removeViewFromArrangedViews:view permanently:NO]; 359 | } 360 | 361 | - (void)unHideView:(UIView*)view { 362 | NSInteger index = [self.subviews indexOfObject:view]; 363 | [self insertArrangedSubview:view atIndex:index newItem:NO]; 364 | } 365 | 366 | #pragma mark - Align View 367 | 368 | - (void)layoutArrangedViews { 369 | NSMutableArray *constraints = [NSMutableArray array]; 370 | [constraints addObjectsFromArray:self.alignmentStrategy.addedConstraints]; 371 | [constraints addObjectsFromArray:self.distributionStrategy.addedConstraints]; 372 | [self removeConstraints:constraints]; 373 | 374 | [self setAlignmentConstraints]; 375 | [self setDistributionConstraints]; 376 | } 377 | 378 | - (void)addViewsAsSubviews:(NSArray*)views { 379 | for (UIView *view in views) { 380 | view.translatesAutoresizingMaskIntoConstraints = NO; 381 | [self addSubview:view]; 382 | } 383 | } 384 | 385 | @end 386 | 387 | @implementation OAStackViewProxy 388 | @end 389 | 390 | #pragma mark - Runtime Injection 391 | 392 | // Constructors are called after all classes have been loaded. 393 | __attribute__((constructor)) static void OAStackViewPatchEntry(void) { 394 | 395 | if (objc_getClass("UIStackView")) { 396 | return; 397 | } 398 | 399 | if (objc_getClass("OAStackViewDisableForwardToUIStackViewSentinel")) { 400 | return; 401 | } 402 | 403 | Class class = objc_allocateClassPair(OAStackView.class, "UIStackView", 0); 404 | if (class) { 405 | objc_registerClassPair(class); 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackViewAlignmentStrategy.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewAlignmentStrategy.h 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import 10 | #import "OAStackView.h" 11 | 12 | 13 | @interface OAStackViewAlignmentStrategy : NSObject 14 | 15 | @property(nonatomic, readonly) NSArray *addedConstraints; 16 | 17 | + (OAStackViewAlignmentStrategy*)strategyWithStackView:(OAStackView *)stackView; 18 | 19 | - (void)addConstraintsOnOtherAxis:(UIView*)view; 20 | - (void)alignView:(UIView*)view withPreviousView:(UIView*)previousView; 21 | 22 | - (void)alignFirstView:(UIView*)view; 23 | - (void)alignLastView:(UIView*)view; 24 | 25 | - (void)removeAddedConstraints; 26 | 27 | @end 28 | 29 | // Method for subclasses to use 30 | @interface OAStackViewAlignmentStrategy (Subclassing) 31 | - (NSString*)otherAxisString; 32 | @end -------------------------------------------------------------------------------- /Pod/Classes/OAStackViewAlignmentStrategy.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewAlignmentStrategy.m 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackViewAlignmentStrategy.h" 10 | #import "OAStackViewAlignmentStrategyBaseline.h" 11 | 12 | 13 | @interface OAStackViewAlignmentStrategyFill : OAStackViewAlignmentStrategy 14 | @end 15 | 16 | @interface OAStackViewAlignmentStrategyTrailing : OAStackViewAlignmentStrategy 17 | @end 18 | 19 | @interface OAStackViewAlignmentStrategyLeading : OAStackViewAlignmentStrategy 20 | @end 21 | 22 | @interface OAStackViewAlignmentStrategyCenter: OAStackViewAlignmentStrategy 23 | @end 24 | 25 | @interface OAStackViewAlignmentStrategy () 26 | @property(nonatomic, weak) OAStackView *stackView; 27 | @property(nonatomic) NSMutableArray *constraints; 28 | @end 29 | 30 | @implementation OAStackViewAlignmentStrategy 31 | 32 | + (OAStackViewAlignmentStrategy*)strategyWithStackView:(OAStackView *)stackView { 33 | 34 | Class cls; 35 | switch (stackView.alignment) { 36 | 37 | case OAStackViewAlignmentFill: 38 | cls = [OAStackViewAlignmentStrategyFill class]; 39 | break; 40 | 41 | case OAStackViewAlignmentLeading: 42 | cls = [OAStackViewAlignmentStrategyLeading class]; 43 | break; 44 | 45 | case OAStackViewAlignmentTrailing: 46 | cls = [OAStackViewAlignmentStrategyTrailing class]; 47 | break; 48 | 49 | case OAStackViewAlignmentCenter: 50 | cls = [OAStackViewAlignmentStrategyCenter class]; 51 | break; 52 | 53 | case OAStackViewAlignmentBaseline: 54 | cls = [OAStackViewAlignmentStrategyLastBaseline class]; 55 | break; 56 | 57 | case OAStackViewAlignmentFirstBaseline: 58 | cls = [OAStackViewAlignmentStrategyFirstBaseline class]; 59 | break; 60 | 61 | default: 62 | break; 63 | } 64 | 65 | return [[cls alloc] initWithWithStackView:stackView]; 66 | } 67 | 68 | - (instancetype)initWithWithStackView:(OAStackView *)stackView { 69 | self = [super init]; 70 | if (self) { 71 | _stackView = stackView; 72 | _constraints = [NSMutableArray array]; 73 | } 74 | return self; 75 | } 76 | 77 | - (NSString*)otherAxisString { 78 | return self.stackView.axis == UILayoutConstraintAxisHorizontal ? @"V" : @"H"; 79 | } 80 | 81 | - (CGFloat)firstMargin { 82 | if (self.stackView.axis == UILayoutConstraintAxisHorizontal) { 83 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.top : 0.0f; 84 | } else { 85 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.left : 0.0f; 86 | } 87 | } 88 | 89 | - (CGFloat)lastMargin { 90 | if (self.stackView.axis == UILayoutConstraintAxisHorizontal) { 91 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.bottom : 0.0f; 92 | } else { 93 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.right : 0.0f; 94 | } 95 | } 96 | 97 | - (NSLayoutAttribute)centerAttribute { 98 | return self.stackView.axis == UILayoutConstraintAxisHorizontal ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX; 99 | } 100 | 101 | - (void)addConstraintsOnOtherAxis:(UIView*)view { 102 | NSArray *arr = [self constraintsalignViewOnOtherAxis:view]; 103 | [self.constraints addObjectsFromArray:arr]; 104 | 105 | if (arr) { [self.stackView addConstraints:arr]; } 106 | } 107 | 108 | - (void)alignView:(UIView*)view withPreviousView:(UIView*)previousView { 109 | NSArray *arr = [self constraintsAlignView:view afterPreviousView:previousView]; 110 | [self.constraints addObjectsFromArray:arr]; 111 | 112 | if (arr) { [self.stackView addConstraints:arr]; } 113 | } 114 | 115 | - (NSArray *)addedConstraints { 116 | return [self.constraints copy]; 117 | } 118 | 119 | - (void)removeAddedConstraints { 120 | [self.stackView removeConstraints:self.constraints]; 121 | [self.constraints removeAllObjects]; 122 | } 123 | 124 | - (NSArray*)constraintsalignViewOnOtherAxis:(UIView*)view { 125 | id constraintString = [NSString stringWithFormat:@"%@:|-(%@firstMargin)-[view]-(%@lastMargin)-|", [self otherAxisString], [self firstMarginRelation], [self lastMarginRelation]]; 126 | 127 | NSNumber *firstMargin = @([self firstMargin]); 128 | NSNumber *lastMargin = @([self lastMargin]); 129 | return [NSLayoutConstraint constraintsWithVisualFormat:constraintString 130 | options:0 131 | metrics:NSDictionaryOfVariableBindings(firstMargin, lastMargin) 132 | views:NSDictionaryOfVariableBindings(view)]; 133 | } 134 | 135 | - (NSArray*)constraintsAlignView:(UIView *)view afterPreviousView:(UIView*)afterView { /* subclassing */ return nil; } 136 | 137 | - (NSString *)firstMarginRelation { 138 | return @"=="; 139 | } 140 | 141 | - (NSString *)lastMarginRelation { 142 | return @"=="; 143 | } 144 | 145 | - (void)alignFirstView:(UIView*)view { 146 | if(!view) { return; } 147 | NSArray *arr = [self firstViewConstraints:view withParentView:self.stackView]; 148 | if(!arr) { return; } 149 | 150 | [self.constraints addObjectsFromArray:arr]; 151 | [self.stackView addConstraints:arr]; 152 | } 153 | 154 | - (void)alignLastView:(UIView*)view { 155 | if(!view) { return; } 156 | NSArray *arr = [self lastViewConstraints:view withParentView:self.stackView]; 157 | if(!arr) { return; } 158 | 159 | [self.constraints addObjectsFromArray:arr]; 160 | [self.stackView addConstraints:arr]; 161 | } 162 | 163 | - (NSArray*)firstViewConstraints:(UIView*)view withParentView:(UIView*)parentView { 164 | return nil; 165 | } 166 | 167 | - (NSArray*)lastViewConstraints:(UIView*)view withParentView:(UIView*)parentView { 168 | return nil; 169 | } 170 | 171 | @end 172 | 173 | @implementation OAStackViewAlignmentStrategyFill 174 | @end 175 | 176 | @implementation OAStackViewAlignmentStrategyLeading 177 | - (NSString *)firstMarginRelation { 178 | return @"=="; 179 | } 180 | 181 | - (NSString *)lastMarginRelation { 182 | return @">="; 183 | } 184 | @end 185 | 186 | @implementation OAStackViewAlignmentStrategyTrailing 187 | - (NSString *)firstMarginRelation { 188 | return @">="; 189 | } 190 | 191 | - (NSString *)lastMarginRelation { 192 | return @"=="; 193 | } 194 | @end 195 | 196 | @implementation OAStackViewAlignmentStrategyCenter 197 | - (NSString *)firstMarginRelation { 198 | return @">="; 199 | } 200 | 201 | - (NSString *)lastMarginRelation { 202 | return @">="; 203 | } 204 | 205 | - (NSArray*)constraintsalignViewOnOtherAxis:(UIView*)view { 206 | NSArray<__kindof NSLayoutConstraint *> *constraints = [super constraintsalignViewOnOtherAxis:view]; 207 | CGFloat centerAdjustment = ([self firstMargin] - [self lastMargin]) / 2; 208 | return [constraints arrayByAddingObject:[NSLayoutConstraint constraintWithItem:view 209 | attribute:[self centerAttribute] 210 | relatedBy:NSLayoutRelationEqual 211 | toItem:view.superview 212 | attribute:[self centerAttribute] 213 | multiplier:1 214 | constant:centerAdjustment]]; 215 | } 216 | 217 | @end 218 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackViewAlignmentStrategyBaseline.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewAlignmentStrategyBaseline.h 3 | // 4 | // 5 | // Created by Omar Abdelhafith on 08/08/2015. 6 | // 7 | // 8 | 9 | #import 10 | #import "OAStackViewAlignmentStrategy.h" 11 | 12 | 13 | @interface OAStackViewAlignmentStrategyBaseline: OAStackViewAlignmentStrategy 14 | - (NSLayoutAttribute)baselineAttribute; 15 | @end 16 | 17 | @interface OAStackViewAlignmentStrategyLastBaseline: OAStackViewAlignmentStrategyBaseline 18 | @end 19 | 20 | @interface OAStackViewAlignmentStrategyFirstBaseline: OAStackViewAlignmentStrategyBaseline 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackViewAlignmentStrategyBaseline.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewAlignmentStrategyBaseline.m 3 | // 4 | // 5 | // Created by Omar Abdelhafith on 08/08/2015. 6 | // 7 | // 8 | 9 | #import "OAStackViewAlignmentStrategyBaseline.h" 10 | 11 | 12 | @implementation OAStackViewAlignmentStrategyBaseline 13 | 14 | - (NSArray*)constraintsalignViewOnOtherAxis:(UIView*)view { 15 | NSString *constraintString = [NSString stringWithFormat:@"%@:|-(>=0@750)-[view]-(>=0@750)-|", [self otherAxisString]]; 16 | 17 | return [NSLayoutConstraint constraintsWithVisualFormat:constraintString 18 | options:0 19 | metrics:nil 20 | views:NSDictionaryOfVariableBindings(view)]; 21 | } 22 | 23 | - (NSArray*)constraintsAlignView:(UIView *)view afterPreviousView:(UIView*)afterView { 24 | if (!view || !afterView) { return nil; } 25 | 26 | return @[[NSLayoutConstraint constraintWithItem:view 27 | attribute:[self baselineAttribute] 28 | relatedBy:NSLayoutRelationEqual toItem:afterView 29 | attribute:[self baselineAttribute] multiplier:1.0f 30 | constant:0.0f]]; 31 | } 32 | 33 | - (NSArray*)firstViewConstraints:(UIView*)view withParentView:(UIView*)parentView { 34 | return nil; 35 | //Not used for now 36 | // id constraintString = [NSString stringWithFormat:@"%@:|-(>=0@750)-[view]", [self otherAxisString]]; 37 | // 38 | // return [NSLayoutConstraint constraintsWithVisualFormat:constraintString 39 | // options:0 40 | // metrics:nil 41 | // views:NSDictionaryOfVariableBindings(view)]; 42 | // 43 | // return @[[NSLayoutConstraint constraintWithItem:parentView 44 | // attribute:NSLayoutAttributeTop 45 | // relatedBy:NSLayoutRelationGreaterThanOrEqual 46 | // toItem:view 47 | // attribute:NSLayoutAttributeTop 48 | // multiplier:1 constant:0]]; 49 | } 50 | 51 | - (NSArray*)lastViewConstraints:(UIView*)view withParentView:(UIView*)parentView { 52 | return nil; 53 | //Not used for now 54 | // id constraintString = [NSString stringWithFormat:@"%@:[view]-(>=0@750)-|", [self otherAxisString]]; 55 | // 56 | // return [NSLayoutConstraint constraintsWithVisualFormat:constraintString 57 | // options:0 58 | // metrics:nil 59 | // views:NSDictionaryOfVariableBindings(view)]; 60 | // 61 | // return @[[NSLayoutConstraint constraintWithItem:parentView 62 | // attribute:NSLayoutAttributeBottom 63 | // relatedBy:NSLayoutRelationGreaterThanOrEqual 64 | // toItem:view 65 | // attribute:NSLayoutAttributeBottom 66 | // multiplier:1 constant:0]]; 67 | } 68 | 69 | - (NSLayoutAttribute)baselineAttribute { 70 | return NSLayoutAttributeBaseline; 71 | } 72 | 73 | @end 74 | 75 | 76 | @implementation OAStackViewAlignmentStrategyFirstBaseline 77 | 78 | - (NSLayoutAttribute)baselineAttribute { 79 | return NSLayoutAttributeFirstBaseline; 80 | } 81 | 82 | @end 83 | 84 | 85 | @implementation OAStackViewAlignmentStrategyLastBaseline 86 | 87 | - (NSLayoutAttribute)baselineAttribute { 88 | return NSLayoutAttributeLastBaseline; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackViewDistributionStrategy.h: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewDistributionStrategy.h 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import 10 | #import "OAStackView.h" 11 | 12 | 13 | @interface OAStackViewDistributionStrategy : NSObject 14 | 15 | @property(nonatomic, readonly) NSArray *addedConstraints; 16 | 17 | + (OAStackViewDistributionStrategy*)strategyWithStackView:(OAStackView *)stackView; 18 | 19 | - (void)alignView:(UIView*)view afterView:(UIView*)previousView; 20 | - (void)removeAddedConstraints; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Pod/Classes/OAStackViewDistributionStrategy.m: -------------------------------------------------------------------------------- 1 | // 2 | // OAStackViewDistributionStrategy.m 3 | // Pods 4 | // 5 | // Created by Omar Abdelhafith on 15/06/2015. 6 | // 7 | // 8 | 9 | #import "OAStackViewDistributionStrategy.h" 10 | 11 | #import "_OALayoutGuide.h" 12 | 13 | @interface OAStackViewDistributionStrategyFill : OAStackViewDistributionStrategy 14 | @end 15 | 16 | @interface OAStackViewDistributionStrategyFillEqually : OAStackViewDistributionStrategy 17 | @end 18 | 19 | @interface OAStackViewDistributionStrategyFillProportionally : OAStackViewDistributionStrategy 20 | @end 21 | 22 | @interface OAStackViewDistributionStrategyEqualSpacing : OAStackViewDistributionStrategy 23 | @end 24 | 25 | @interface OAStackViewDistributionStrategyEqualCentering : OAStackViewDistributionStrategy 26 | @end 27 | 28 | @interface OAStackViewDistributionStrategy () 29 | @property(nonatomic, weak) OAStackView *stackView; 30 | @property(nonatomic) NSMutableArray *constraints; 31 | @end 32 | 33 | @implementation OAStackViewDistributionStrategy 34 | 35 | + (OAStackViewDistributionStrategy*)strategyWithStackView:(OAStackView *)stackView { 36 | Class cls; 37 | 38 | switch (stackView.distribution) { 39 | case OAStackViewDistributionFill: 40 | cls = [OAStackViewDistributionStrategyFill class]; 41 | break; 42 | 43 | case OAStackViewDistributionFillEqually: 44 | cls = [OAStackViewDistributionStrategyFillEqually class]; 45 | break; 46 | 47 | case OAStackViewDistributionFillProportionally: 48 | cls = [OAStackViewDistributionStrategyFillProportionally class]; 49 | break; 50 | 51 | case OAStackViewDistributionEqualSpacing: 52 | cls = [OAStackViewDistributionStrategyEqualSpacing class]; 53 | break; 54 | 55 | case OAStackViewDistributionEqualCentering: 56 | cls = [OAStackViewDistributionStrategyEqualCentering class]; 57 | break; 58 | 59 | default: 60 | break; 61 | } 62 | 63 | return [[cls alloc] initStackView:stackView]; 64 | } 65 | 66 | - (instancetype)initStackView:(OAStackView*)stackView { 67 | self = [super init]; 68 | if (self) { 69 | _stackView = stackView; 70 | _constraints = [NSMutableArray array]; 71 | } 72 | return self; 73 | } 74 | 75 | - (void)alignView:(UIView*)view afterView:(UIView*)previousView { 76 | if (!previousView && !view) { return; } 77 | 78 | if (!previousView) { 79 | return [self alignFirstView:view]; 80 | } 81 | 82 | if(!view) { 83 | return [self alignLastView:previousView]; 84 | } 85 | 86 | if (previousView && view) { 87 | [self alignMiddleView:view afterView:previousView]; 88 | } 89 | } 90 | 91 | - (void)alignLastView:(UIView*)view { 92 | NSString *constraintString = [NSString stringWithFormat:@"%@:[view]-(lastMargin)-|", [self currentAxisString]]; 93 | NSNumber *lastMargin = @([self lastMargin]); 94 | NSArray *arr = [NSLayoutConstraint constraintsWithVisualFormat:constraintString 95 | options:0 96 | metrics:NSDictionaryOfVariableBindings(lastMargin) 97 | views:NSDictionaryOfVariableBindings(view)]; 98 | 99 | [self.constraints addObjectsFromArray:arr]; 100 | [self.stackView addConstraints:arr]; 101 | } 102 | 103 | - (void)alignFirstView:(UIView*)view { 104 | NSString *str = [NSString stringWithFormat:@"%@:|-(firstMargin)-[view]", [self currentAxisString]]; 105 | NSNumber *firstMargin = @([self firstMargin]); 106 | NSArray *arr = [NSLayoutConstraint constraintsWithVisualFormat:str 107 | options:0 108 | metrics:NSDictionaryOfVariableBindings(firstMargin) 109 | views:NSDictionaryOfVariableBindings(view)]; 110 | 111 | [self.constraints addObjectsFromArray:arr]; 112 | [self.stackView addConstraints:arr]; 113 | } 114 | 115 | 116 | - (void)alignMiddleView:(UIView*)view afterView:(UIView*)previousView { 117 | NSString *str = [NSString stringWithFormat:@"%@:[previousView]-(%@%f)-[view]", 118 | [self currentAxisString], 119 | [self symbolicSpacingRelation], 120 | self.stackView.spacing]; 121 | 122 | NSArray *arr = [NSLayoutConstraint constraintsWithVisualFormat:str 123 | options:0 124 | metrics:nil 125 | views:NSDictionaryOfVariableBindings(view, previousView)]; 126 | 127 | [self.constraints addObjectsFromArray:arr]; 128 | [self.stackView addConstraints:arr]; 129 | } 130 | 131 | 132 | - (NSString*)currentAxisString { 133 | return self.stackView.axis == UILayoutConstraintAxisHorizontal ? @"H" : @"V"; 134 | } 135 | 136 | - (NSLayoutAttribute)equalityAxis { 137 | return self.stackView.axis == UILayoutConstraintAxisVertical ? NSLayoutAttributeHeight : NSLayoutAttributeWidth; 138 | } 139 | 140 | - (CGFloat)firstMargin { 141 | if (self.stackView.axis == UILayoutConstraintAxisHorizontal) { 142 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.left : 0.0f; 143 | } else { 144 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.top : 0.0f; 145 | } 146 | } 147 | 148 | - (CGFloat)lastMargin { 149 | if (self.stackView.axis == UILayoutConstraintAxisHorizontal) { 150 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.right : 0.0f; 151 | } else { 152 | return self.stackView.layoutMarginsRelativeArrangement ? self.stackView.layoutMargins.bottom : 0.0f; 153 | } 154 | } 155 | 156 | - (NSArray *)addedConstraints { 157 | return [self.constraints copy]; 158 | } 159 | 160 | - (void)removeAddedConstraints { 161 | [self.stackView removeConstraints:self.constraints]; 162 | [self.constraints removeAllObjects]; 163 | } 164 | 165 | - (NSString *)symbolicSpacingRelation 166 | { 167 | return @"=="; 168 | } 169 | 170 | @end 171 | 172 | @implementation OAStackViewDistributionStrategyFill 173 | @end 174 | 175 | @implementation OAStackViewDistributionStrategyFillEqually 176 | 177 | - (void)alignMiddleView:(UIView*)view afterView:(UIView*)previousView { 178 | [super alignMiddleView:view afterView:previousView]; 179 | [self addEqualityConstraintsBetween:view otherView:previousView]; 180 | } 181 | 182 | - (void)addEqualityConstraintsBetween:(UIView*)view otherView:(UIView*)otherView { 183 | if (otherView == nil || view == nil) { 184 | return; 185 | } 186 | 187 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view 188 | attribute:[self equalityAxis] 189 | relatedBy:NSLayoutRelationEqual 190 | toItem:otherView 191 | attribute:[self equalityAxis] 192 | multiplier:1 193 | constant:0]; 194 | 195 | [self.constraints addObject:constraint]; 196 | [self.stackView addConstraint:constraint]; 197 | } 198 | 199 | @end 200 | 201 | @implementation OAStackViewDistributionStrategyFillProportionally 202 | 203 | - (void)alignMiddleView:(UIView*)view afterView:(UIView*)previousView { 204 | [super alignMiddleView:view afterView:previousView]; 205 | [self addProportionalityConstraintsBetween:view otherView:previousView]; 206 | } 207 | 208 | - (void)addProportionalityConstraintsBetween:(UIView *)view otherView:(UIView *)otherView { 209 | if (view == nil || otherView == nil) { 210 | return; 211 | } 212 | 213 | CGFloat multiplier = 1; 214 | if (self.stackView.axis == UILayoutConstraintAxisHorizontal) { 215 | multiplier = view.intrinsicContentSize.width / otherView.intrinsicContentSize.width; 216 | } else { 217 | multiplier = view.intrinsicContentSize.height / otherView.intrinsicContentSize.height; 218 | } 219 | 220 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view 221 | attribute:[self equalityAxis] 222 | relatedBy:NSLayoutRelationEqual 223 | toItem:otherView 224 | attribute:[self equalityAxis] 225 | multiplier:multiplier 226 | constant:0]; 227 | 228 | [self.constraints addObject:constraint]; 229 | [self.stackView addConstraint:constraint]; 230 | } 231 | 232 | @end 233 | 234 | @interface OAStackViewDistributionStrategyEqualSpacing () 235 | 236 | @property (nonatomic, strong) NSMutableArray *equalSpacingLayoutGuides; 237 | 238 | @end 239 | 240 | @implementation OAStackViewDistributionStrategyEqualSpacing 241 | 242 | - (NSLayoutAttribute)spanningAttributeForAxis:(UILayoutConstraintAxis)axis 243 | isInitialEdge:(BOOL)isInitialConstraint 244 | { 245 | switch (axis) { 246 | case UILayoutConstraintAxisHorizontal: 247 | return isInitialConstraint ? NSLayoutAttributeLeading : NSLayoutAttributeTrailing; 248 | 249 | case UILayoutConstraintAxisVertical: 250 | return isInitialConstraint ? NSLayoutAttributeTop : NSLayoutAttributeBottom; 251 | } 252 | } 253 | 254 | - (NSMutableArray *)equalSpacingLayoutGuides 255 | { 256 | if (!_equalSpacingLayoutGuides) { 257 | _equalSpacingLayoutGuides = [NSMutableArray array]; 258 | } 259 | 260 | return _equalSpacingLayoutGuides; 261 | } 262 | 263 | - (NSString *)symbolicSpacingRelation 264 | { 265 | return @">="; 266 | } 267 | 268 | - (void)alignMiddleView:(UIView *)view afterView:(UIView *)previousView 269 | { 270 | [super alignMiddleView:view afterView:previousView]; 271 | 272 | _OALayoutGuide *guide = [_OALayoutGuide new]; 273 | [self.equalSpacingLayoutGuides addObject:guide]; 274 | [self.stackView addSubview:guide]; 275 | 276 | NSMutableArray *newConstraints = [NSMutableArray array]; 277 | 278 | UILayoutConstraintAxis axis = self.stackView.axis; 279 | 280 | NSLayoutConstraint *firstEdgeConstraint = 281 | [NSLayoutConstraint constraintWithItem:guide 282 | attribute:[self spanningAttributeForAxis:axis 283 | isInitialEdge:YES] 284 | relatedBy:NSLayoutRelationEqual 285 | toItem:previousView 286 | attribute:[self spanningAttributeForAxis:axis 287 | isInitialEdge:NO] 288 | multiplier:1 289 | constant:0]; 290 | 291 | NSLayoutConstraint *secondEdgeConstraint = 292 | [NSLayoutConstraint constraintWithItem:view 293 | attribute:[self spanningAttributeForAxis:axis 294 | isInitialEdge:YES] 295 | relatedBy:NSLayoutRelationEqual 296 | toItem:guide 297 | attribute:[self spanningAttributeForAxis:axis 298 | isInitialEdge:NO] 299 | multiplier:1 300 | constant:0]; 301 | 302 | [newConstraints addObjectsFromArray:@[firstEdgeConstraint, secondEdgeConstraint]]; 303 | 304 | id firstGuide = self.equalSpacingLayoutGuides.firstObject; 305 | if (firstGuide != guide) { 306 | NSLayoutConstraint *equalWidth = 307 | [NSLayoutConstraint constraintWithItem:firstGuide 308 | attribute:[self equalityAxis] 309 | relatedBy:NSLayoutRelationEqual 310 | toItem:guide 311 | attribute:[self equalityAxis] 312 | multiplier:1 313 | constant:0]; 314 | 315 | equalWidth.identifier = @"OA-fill-equally"; 316 | 317 | [newConstraints addObject:equalWidth]; 318 | } 319 | 320 | [self.constraints addObjectsFromArray:newConstraints]; 321 | [self.stackView addConstraints:newConstraints]; 322 | } 323 | 324 | - (void)removeAddedConstraints 325 | { 326 | [super removeAddedConstraints]; 327 | 328 | [self.equalSpacingLayoutGuides makeObjectsPerformSelector:@selector(removeFromSuperview)]; 329 | [self.equalSpacingLayoutGuides removeAllObjects]; 330 | } 331 | 332 | @end 333 | 334 | @implementation OAStackViewDistributionStrategyEqualCentering 335 | 336 | @end 337 | -------------------------------------------------------------------------------- /Pod/Classes/OATransformLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // OATransformLayer.h 3 | // OAStackView 4 | // 5 | // Created by Miguel Cabeça on 14/07/15. 6 | // Copyright (c) 2015 OAStackView. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OATransformLayer : CATransformLayer 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Pod/Classes/OATransformLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // OATransformLayer.m 3 | // OAStackView 4 | // 5 | // Created by Miguel Cabeça on 14/07/15. 6 | // Copyright (c) 2015 OAStackView. All rights reserved. 7 | // 8 | 9 | #import "OATransformLayer.h" 10 | 11 | @implementation OATransformLayer 12 | 13 | - (void)setOpaque:(BOOL)opaque { 14 | // Does not have any effect because `CATransformLayer` is not rendered. 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Pod/Classes/_OALayoutGuide.h: -------------------------------------------------------------------------------- 1 | // 2 | // _OALayoutGuide.h 3 | // Pods 4 | // 5 | 6 | #import 7 | 8 | @interface _OALayoutGuide : UIView 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /Pod/Classes/_OALayoutGuide.m: -------------------------------------------------------------------------------- 1 | // 2 | // _OALayoutGuide.m 3 | // Pods 4 | // 5 | 6 | #import "_OALayoutGuide.h" 7 | #import "OATransformLayer.h" 8 | 9 | @interface _OALayoutGuide () 10 | 11 | @property (nonatomic) BOOL propertiesAreLocked; 12 | 13 | @end 14 | 15 | @implementation _OALayoutGuide 16 | 17 | + (Class)layerClass 18 | { 19 | return [OATransformLayer class]; 20 | } 21 | 22 | + (BOOL)requiresConstraintBasedLayout 23 | { 24 | return YES; 25 | } 26 | 27 | - (id)initWithCoder:(NSCoder *)aDecoder 28 | { 29 | self = [super initWithCoder:aDecoder]; 30 | if (!self) { return nil; } 31 | 32 | [self commonInit]; 33 | 34 | return self; 35 | } 36 | 37 | - (instancetype)initWithFrame:(CGRect)frame 38 | { 39 | self = [super initWithFrame:frame]; 40 | if (!self) { return nil; } 41 | 42 | [self commonInit]; 43 | 44 | return self; 45 | } 46 | 47 | - (void)commonInit 48 | { 49 | self.translatesAutoresizingMaskIntoConstraints = NO; 50 | self.userInteractionEnabled = NO; 51 | self.hidden = YES; 52 | 53 | self.propertiesAreLocked = YES; 54 | } 55 | 56 | - (void)setHidden:(BOOL)hidden 57 | { 58 | NSAssert(!self.propertiesAreLocked, @"Properties are no longer mutable"); 59 | [super setHidden:hidden]; 60 | } 61 | 62 | - (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled 63 | { 64 | NSAssert(!self.propertiesAreLocked, @"Properties are no longer mutable"); 65 | [super setUserInteractionEnabled:userInteractionEnabled]; 66 | } 67 | 68 | - (void)setBackgroundColor:(UIColor *)backgroundColor {} 69 | 70 | - (void)setOpaque:(BOOL)opaque {} 71 | 72 | - (void)setClipsToBounds:(BOOL)clipsToBounds {} 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OAStackView 2 | 3 | [![Build Status](https://travis-ci.org/oarrabi/OAStackView.svg)](https://travis-ci.org/oarrabi/OAStackView) 4 | [![Version](https://img.shields.io/cocoapods/v/OAStackView.svg?style=flat)](http://cocoapods.org/pods/OAStackView) 5 | [![License](https://img.shields.io/cocoapods/l/OAStackView.svg?style=flat)](http://cocoapods.org/pods/OAStackView) 6 | [![Platform](https://img.shields.io/cocoapods/p/OAStackView.svg?style=flat)](http://cocoapods.org/pods/OAStackView) 7 | 8 | iOS 9 introduced the very cool [UIStackView](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/), UIStackView can be used to easily create simple and complex layouts. 9 | 10 | As expected `UIStackView` can only be used for iOS 9 and up. This project tries to port back the stackview to iOS 7+. 11 | 12 | `OAStackView` aims at replicating all the features in `UIStackView` 13 | 14 |

15 | 16 | ## Usage 17 | 18 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 19 | 20 | Since `OAStackView` mimics the interface of `UIStackView`, the usage of `OAStackView` is similar to `UIStackView`. 21 | 22 | `OAStackView` Can be either used from the Interface builder, or from code. 23 | 24 | ### Interface Builder 25 | 26 | Drag a a UIView into your view controller, and add some views to it. 27 | 28 | 29 |

 photo step1_zps2xxl75vw.png

30 | 31 | Change the class to `OAStackView` 32 | 33 |

 photo step2_zpsfgwirklz.png

34 | 35 | (Optional) Change the stack Axis (set Axis Value to 0 for Horizontal or 1 for Vertical), Spacing, Alignment or distribution. 36 | 37 |

 photo step3_zpsmk8xw3hz.png

38 | 39 | Run the project! 40 | 41 |

 photo step4_zpsgl92oeoc.png

42 | 43 | 44 | ### From Code 45 | 46 | To use `OAStackView` from code, Please refer to [UIStackView](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/) for proper documentation. 47 | 48 | As a quick example on its usage do the following: 49 | 50 | Create a couple of views to be stacked: 51 | 52 | ```objc 53 | UILabel *l1 = [[UILabel alloc] init]; 54 | l1.text = @"Label 1"; 55 | UILabel *l2 = [[UILabel alloc] init]; 56 | l2.text = @"Label 2"; 57 | ``` 58 | 59 | Create the stack view passing the array of views: 60 | 61 | ```objc 62 | OAStackView *stackView = [[OAStackView alloc] initWithArrangedSubviews:@[l1, l2]]; 63 | stackView.translatesAutoresizingMaskIntoConstraints = NO; 64 | ``` 65 | 66 | Add the stack view to `self.view` 67 | 68 | ```objc 69 | [self.view addSubview:stackView]; 70 | 71 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-30-[stackView]" 72 | options:0 73 | metrics:0 74 | views:NSDictionaryOfVariableBindings(stackView)]]; 75 | 76 | [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[stackView]" 77 | options:0 78 | metrics:0 79 | views:NSDictionaryOfVariableBindings(stackView)]]; 80 | ``` 81 | 82 | ## Installation 83 | 84 | OAStackView is available through [CocoaPods](http://cocoapods.org). To install 85 | it, simply add the following line to your Podfile: 86 | 87 | ```ruby 88 | pod "OAStackView" 89 | ``` 90 | 91 | ## Tests 92 | 93 | Since `OAStackView` has been built from reverse engineering `UIStackView`, and since I intend to keep updating and refactoring `OAStackView`, tests was one of the requirements going forward. 94 | 95 | The following a [human readable](https://raw.githubusercontent.com/oarrabi/OAStackView/master/Example/Tests/tests.transcript.txt?token=ABZLPOoXoCREu-BpaaIEVcTY5i1icbkrks5ViJ_9wA%3D%3D) text subscript (generated with [specipy](https://github.com/oarrabi/specipy)). 96 | 97 | ## Contribution 98 | 99 | All contributions in any form are welcomed, if you find the project helpful, and you want to contribute then please do. 100 | 101 | ## Known Issues, and future improvements 102 | 103 | ### Missing functionality 104 | `OAStackView` implements most of the features from `UIStackView` except the following: 105 | 106 | - [ ] `baselineRelativeArrangement` 107 | 108 | @property(nonatomic,getter=isBaselineRelativeArrangement) BOOL baselineRelativeArrangement; 109 | 110 | - [x] `layoutMarginsRelativeArrangement` 111 | 112 | 113 | @property(nonatomic,getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement; 114 | 115 | `UIStackViewDistribution` is also partially implemented (2 elements out of 5 are still not implemented) 116 | 117 | - [x] `UIStackViewDistributionFill` 118 | - [x] `UIStackViewDistributionFillEqually` 119 | - [x] `UIStackViewDistributionFillProportionally` 120 | - [ ] `UIStackViewDistributionEqualSpacing` 121 | - [ ] `UIStackViewDistributionEqualCentering` 122 | 123 | Please refer to [UIStackView](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStackView_Class_Reference/) for proper documentation. 124 | 125 | ### Future improvements 126 | The following would be nice to have for future versions 127 | 128 | - [ ] Covering the remaining functionality from `UIStackView` 129 | - [ ] Better Documentation 130 | - [ ] Better test coverage for some edge cases 131 | - [ ] Rewrite in swift, or more swift friendly 132 | 133 | ## Author 134 | 135 | Omar Abdelhafith, o.arrabi@me.com 136 | 137 | ## License 138 | 139 | OAStackView is available under the MIT license. See the LICENSE file for more info. 140 | --------------------------------------------------------------------------------