├── .gitignore ├── .travis.yml ├── Example ├── Podfile ├── StackLayout.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── StackLayout-Example.xcscheme ├── StackLayout.xcworkspace │ └── contents.xcworkspacedata ├── StackLayout │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Main.storyboard │ ├── SLAppDelegate.h │ ├── SLAppDelegate.m │ ├── SLViewController.h │ ├── SLViewController.m │ ├── StackLayout-Info.plist │ ├── StackLayout-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── Tests │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── Tests.m │ └── en.lproj │ └── InfoPlist.strings ├── Images ├── ToolbarLayout.png └── WelcomeTipLayout.png ├── LICENSE ├── Pod ├── Assets │ └── .gitkeep └── StackLayout │ ├── .gitkeep │ ├── SLStackLayout.h │ ├── SLStackLayout.m │ ├── UIView+StackLayout.h │ └── UIView+StackLayout.m ├── README.md ├── StackLayout.podspec ├── StackLayout.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── _Pods.xcodeproj /.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 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | Pods/ 34 | Example/Podfile.lock -------------------------------------------------------------------------------- /.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 | # cache: cocoapods 7 | # podfile: Example/Podfile 8 | # before_install: 9 | # - gem install cocoapods # Since Travis is not always on latest version 10 | # - pod install --project-directory=Example 11 | install: 12 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 13 | script: 14 | - set -o pipefail && xcodebuild test -workspace Example/StackLayout.xcworkspace -scheme StackLayout-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c 15 | - pod lib lint --quick 16 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | use_frameworks! 3 | 4 | target 'StackLayout_Example' do 5 | pod "StackLayout", :path => "../" 6 | end 7 | 8 | target 'StackLayout_Tests' do 9 | pod "StackLayout", :path => "../" 10 | end 11 | -------------------------------------------------------------------------------- /Example/StackLayout.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 11 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 12 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 13 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 14 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 15 | 6003F59E195388D20070C39A /* SLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* SLAppDelegate.m */; }; 16 | 6003F5A7195388D20070C39A /* SLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* SLViewController.m */; }; 17 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 18 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 19 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 20 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 21 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 22 | 6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; }; 23 | 6C8BA695225467057CC22CAA /* Pods_StackLayout_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FC09CF3D7A6560559277CDE /* Pods_StackLayout_Tests.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 24 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 25 | 95D031FE7C11987A8EFDC125 /* Pods_StackLayout_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F545DACB768DF5DBE377DFDC /* Pods_StackLayout_Example.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXContainerItemProxy section */ 29 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 6003F582195388D10070C39A /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 6003F589195388D20070C39A; 34 | remoteInfo = StackLayout; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 17202CAD40FD122F4C81492B /* Pods-StackLayout_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StackLayout_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-StackLayout_Example/Pods-StackLayout_Example.release.xcconfig"; sourceTree = ""; }; 40 | 497B11FAC9F7D1FA195461D5 /* Pods-StackLayout_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StackLayout_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-StackLayout_Tests/Pods-StackLayout_Tests.release.xcconfig"; sourceTree = ""; }; 41 | 4FC09CF3D7A6560559277CDE /* Pods_StackLayout_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_StackLayout_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 5EF07AE193D6D0B8F50FE729 /* Pods-StackLayout_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StackLayout_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StackLayout_Tests/Pods-StackLayout_Tests.debug.xcconfig"; sourceTree = ""; }; 43 | 6003F58A195388D20070C39A /* StackLayout_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StackLayout_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 45 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 46 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 47 | 6003F595195388D20070C39A /* StackLayout-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "StackLayout-Info.plist"; sourceTree = ""; }; 48 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 49 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 50 | 6003F59B195388D20070C39A /* StackLayout-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StackLayout-Prefix.pch"; sourceTree = ""; }; 51 | 6003F59C195388D20070C39A /* SLAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SLAppDelegate.h; sourceTree = ""; }; 52 | 6003F59D195388D20070C39A /* SLAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SLAppDelegate.m; sourceTree = ""; }; 53 | 6003F5A5195388D20070C39A /* SLViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SLViewController.h; sourceTree = ""; }; 54 | 6003F5A6195388D20070C39A /* SLViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SLViewController.m; sourceTree = ""; }; 55 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 56 | 6003F5AE195388D20070C39A /* StackLayout_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StackLayout_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 58 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 59 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 60 | 6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 61 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 62 | 6A88BD3611A0F6A1767CEF47 /* StackLayout.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = StackLayout.podspec; path = ../StackLayout.podspec; sourceTree = ""; }; 63 | 72A5D4DCA9430FA374F8953C /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 64 | 7C32760BB0E849D6DFF96F6F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 65 | 7F1BD4D7C2A14FDAD7C08CA3 /* Pods-StackLayout_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StackLayout_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StackLayout_Example/Pods-StackLayout_Example.debug.xcconfig"; sourceTree = ""; }; 66 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 67 | F545DACB768DF5DBE377DFDC /* Pods_StackLayout_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_StackLayout_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | /* End PBXFileReference section */ 69 | 70 | /* Begin PBXFrameworksBuildPhase section */ 71 | 6003F587195388D20070C39A /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 76 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 77 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 78 | 95D031FE7C11987A8EFDC125 /* Pods_StackLayout_Example.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 6003F5AB195388D20070C39A /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 87 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 88 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 89 | 6C8BA695225467057CC22CAA /* Pods_StackLayout_Tests.framework in Frameworks */, 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXFrameworksBuildPhase section */ 94 | 95 | /* Begin PBXGroup section */ 96 | 6003F581195388D10070C39A = { 97 | isa = PBXGroup; 98 | children = ( 99 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 100 | 6003F593195388D20070C39A /* Example for StackLayout */, 101 | 6003F5B5195388D20070C39A /* Tests */, 102 | 6003F58C195388D20070C39A /* Frameworks */, 103 | 6003F58B195388D20070C39A /* Products */, 104 | BFA311722368E667F2F3A79E /* Pods */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | 6003F58B195388D20070C39A /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 6003F58A195388D20070C39A /* StackLayout_Example.app */, 112 | 6003F5AE195388D20070C39A /* StackLayout_Tests.xctest */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 6003F58C195388D20070C39A /* Frameworks */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 6003F58D195388D20070C39A /* Foundation.framework */, 121 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 122 | 6003F591195388D20070C39A /* UIKit.framework */, 123 | 6003F5AF195388D20070C39A /* XCTest.framework */, 124 | F545DACB768DF5DBE377DFDC /* Pods_StackLayout_Example.framework */, 125 | 4FC09CF3D7A6560559277CDE /* Pods_StackLayout_Tests.framework */, 126 | ); 127 | name = Frameworks; 128 | sourceTree = ""; 129 | }; 130 | 6003F593195388D20070C39A /* Example for StackLayout */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 6003F59C195388D20070C39A /* SLAppDelegate.h */, 134 | 6003F59D195388D20070C39A /* SLAppDelegate.m */, 135 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 136 | 6003F5A5195388D20070C39A /* SLViewController.h */, 137 | 6003F5A6195388D20070C39A /* SLViewController.m */, 138 | 6003F5A8195388D20070C39A /* Images.xcassets */, 139 | 6003F594195388D20070C39A /* Supporting Files */, 140 | ); 141 | name = "Example for StackLayout"; 142 | path = StackLayout; 143 | sourceTree = ""; 144 | }; 145 | 6003F594195388D20070C39A /* Supporting Files */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 6003F595195388D20070C39A /* StackLayout-Info.plist */, 149 | 6003F596195388D20070C39A /* InfoPlist.strings */, 150 | 6003F599195388D20070C39A /* main.m */, 151 | 6003F59B195388D20070C39A /* StackLayout-Prefix.pch */, 152 | ); 153 | name = "Supporting Files"; 154 | sourceTree = ""; 155 | }; 156 | 6003F5B5195388D20070C39A /* Tests */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 6003F5BB195388D20070C39A /* Tests.m */, 160 | 6003F5B6195388D20070C39A /* Supporting Files */, 161 | ); 162 | path = Tests; 163 | sourceTree = ""; 164 | }; 165 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 169 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 170 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 171 | ); 172 | name = "Supporting Files"; 173 | sourceTree = ""; 174 | }; 175 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 6A88BD3611A0F6A1767CEF47 /* StackLayout.podspec */, 179 | 72A5D4DCA9430FA374F8953C /* README.md */, 180 | 7C32760BB0E849D6DFF96F6F /* LICENSE */, 181 | ); 182 | name = "Podspec Metadata"; 183 | sourceTree = ""; 184 | }; 185 | BFA311722368E667F2F3A79E /* Pods */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 7F1BD4D7C2A14FDAD7C08CA3 /* Pods-StackLayout_Example.debug.xcconfig */, 189 | 17202CAD40FD122F4C81492B /* Pods-StackLayout_Example.release.xcconfig */, 190 | 5EF07AE193D6D0B8F50FE729 /* Pods-StackLayout_Tests.debug.xcconfig */, 191 | 497B11FAC9F7D1FA195461D5 /* Pods-StackLayout_Tests.release.xcconfig */, 192 | ); 193 | name = Pods; 194 | sourceTree = ""; 195 | }; 196 | /* End PBXGroup section */ 197 | 198 | /* Begin PBXNativeTarget section */ 199 | 6003F589195388D20070C39A /* StackLayout_Example */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "StackLayout_Example" */; 202 | buildPhases = ( 203 | FFA2BCD04229F7C2A74F45D8 /* [CP] Check Pods Manifest.lock */, 204 | 6003F586195388D20070C39A /* Sources */, 205 | 6003F587195388D20070C39A /* Frameworks */, 206 | 6003F588195388D20070C39A /* Resources */, 207 | 9010B163D0499C1A8992AAF2 /* [CP] Embed Pods Frameworks */, 208 | 24992C42D97012921C6FCE85 /* [CP] Copy Pods Resources */, 209 | ); 210 | buildRules = ( 211 | ); 212 | dependencies = ( 213 | ); 214 | name = StackLayout_Example; 215 | productName = StackLayout; 216 | productReference = 6003F58A195388D20070C39A /* StackLayout_Example.app */; 217 | productType = "com.apple.product-type.application"; 218 | }; 219 | 6003F5AD195388D20070C39A /* StackLayout_Tests */ = { 220 | isa = PBXNativeTarget; 221 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "StackLayout_Tests" */; 222 | buildPhases = ( 223 | A0558CCE02F3A7D76E1888A3 /* [CP] Check Pods Manifest.lock */, 224 | 6003F5AA195388D20070C39A /* Sources */, 225 | 6003F5AB195388D20070C39A /* Frameworks */, 226 | 6003F5AC195388D20070C39A /* Resources */, 227 | BD6169E73463C259282B2EDF /* [CP] Embed Pods Frameworks */, 228 | 602E9C3FCF91326D8AF62299 /* [CP] Copy Pods Resources */, 229 | ); 230 | buildRules = ( 231 | ); 232 | dependencies = ( 233 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 234 | ); 235 | name = StackLayout_Tests; 236 | productName = StackLayoutTests; 237 | productReference = 6003F5AE195388D20070C39A /* StackLayout_Tests.xctest */; 238 | productType = "com.apple.product-type.bundle.unit-test"; 239 | }; 240 | /* End PBXNativeTarget section */ 241 | 242 | /* Begin PBXProject section */ 243 | 6003F582195388D10070C39A /* Project object */ = { 244 | isa = PBXProject; 245 | attributes = { 246 | CLASSPREFIX = SL; 247 | LastSwiftUpdateCheck = 0730; 248 | LastUpgradeCheck = 0510; 249 | ORGANIZATIONNAME = "Bridger Maxwell"; 250 | TargetAttributes = { 251 | 6003F5AD195388D20070C39A = { 252 | TestTargetID = 6003F589195388D20070C39A; 253 | }; 254 | }; 255 | }; 256 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "StackLayout" */; 257 | compatibilityVersion = "Xcode 3.2"; 258 | developmentRegion = English; 259 | hasScannedForEncodings = 0; 260 | knownRegions = ( 261 | en, 262 | Base, 263 | ); 264 | mainGroup = 6003F581195388D10070C39A; 265 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 266 | projectDirPath = ""; 267 | projectRoot = ""; 268 | targets = ( 269 | 6003F589195388D20070C39A /* StackLayout_Example */, 270 | 6003F5AD195388D20070C39A /* StackLayout_Tests */, 271 | ); 272 | }; 273 | /* End PBXProject section */ 274 | 275 | /* Begin PBXResourcesBuildPhase section */ 276 | 6003F588195388D20070C39A /* Resources */ = { 277 | isa = PBXResourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, 281 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 282 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | 6003F5AC195388D20070C39A /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | /* End PBXResourcesBuildPhase section */ 295 | 296 | /* Begin PBXShellScriptBuildPhase section */ 297 | 24992C42D97012921C6FCE85 /* [CP] Copy Pods Resources */ = { 298 | isa = PBXShellScriptBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | ); 302 | inputPaths = ( 303 | ); 304 | name = "[CP] Copy Pods Resources"; 305 | outputPaths = ( 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | shellPath = /bin/sh; 309 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StackLayout_Example/Pods-StackLayout_Example-resources.sh\"\n"; 310 | showEnvVarsInLog = 0; 311 | }; 312 | 602E9C3FCF91326D8AF62299 /* [CP] Copy Pods Resources */ = { 313 | isa = PBXShellScriptBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | ); 317 | inputPaths = ( 318 | ); 319 | name = "[CP] Copy Pods Resources"; 320 | outputPaths = ( 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | shellPath = /bin/sh; 324 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StackLayout_Tests/Pods-StackLayout_Tests-resources.sh\"\n"; 325 | showEnvVarsInLog = 0; 326 | }; 327 | 9010B163D0499C1A8992AAF2 /* [CP] Embed Pods Frameworks */ = { 328 | isa = PBXShellScriptBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | ); 332 | inputPaths = ( 333 | "${SRCROOT}/Pods/Target Support Files/Pods-StackLayout_Example/Pods-StackLayout_Example-frameworks.sh", 334 | "${BUILT_PRODUCTS_DIR}/StackLayout/StackLayout.framework", 335 | ); 336 | name = "[CP] Embed Pods Frameworks"; 337 | outputPaths = ( 338 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/StackLayout.framework", 339 | ); 340 | runOnlyForDeploymentPostprocessing = 0; 341 | shellPath = /bin/sh; 342 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StackLayout_Example/Pods-StackLayout_Example-frameworks.sh\"\n"; 343 | showEnvVarsInLog = 0; 344 | }; 345 | A0558CCE02F3A7D76E1888A3 /* [CP] Check Pods Manifest.lock */ = { 346 | isa = PBXShellScriptBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | ); 350 | inputPaths = ( 351 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 352 | "${PODS_ROOT}/Manifest.lock", 353 | ); 354 | name = "[CP] Check Pods Manifest.lock"; 355 | outputPaths = ( 356 | "$(DERIVED_FILE_DIR)/Pods-StackLayout_Tests-checkManifestLockResult.txt", 357 | ); 358 | runOnlyForDeploymentPostprocessing = 0; 359 | shellPath = /bin/sh; 360 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 361 | showEnvVarsInLog = 0; 362 | }; 363 | BD6169E73463C259282B2EDF /* [CP] Embed Pods Frameworks */ = { 364 | isa = PBXShellScriptBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | ); 368 | inputPaths = ( 369 | "${SRCROOT}/Pods/Target Support Files/Pods-StackLayout_Tests/Pods-StackLayout_Tests-frameworks.sh", 370 | "${BUILT_PRODUCTS_DIR}/StackLayout/StackLayout.framework", 371 | ); 372 | name = "[CP] Embed Pods Frameworks"; 373 | outputPaths = ( 374 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/StackLayout.framework", 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | shellPath = /bin/sh; 378 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-StackLayout_Tests/Pods-StackLayout_Tests-frameworks.sh\"\n"; 379 | showEnvVarsInLog = 0; 380 | }; 381 | FFA2BCD04229F7C2A74F45D8 /* [CP] Check Pods Manifest.lock */ = { 382 | isa = PBXShellScriptBuildPhase; 383 | buildActionMask = 2147483647; 384 | files = ( 385 | ); 386 | inputPaths = ( 387 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 388 | "${PODS_ROOT}/Manifest.lock", 389 | ); 390 | name = "[CP] Check Pods Manifest.lock"; 391 | outputPaths = ( 392 | "$(DERIVED_FILE_DIR)/Pods-StackLayout_Example-checkManifestLockResult.txt", 393 | ); 394 | runOnlyForDeploymentPostprocessing = 0; 395 | shellPath = /bin/sh; 396 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 397 | showEnvVarsInLog = 0; 398 | }; 399 | /* End PBXShellScriptBuildPhase section */ 400 | 401 | /* Begin PBXSourcesBuildPhase section */ 402 | 6003F586195388D20070C39A /* Sources */ = { 403 | isa = PBXSourcesBuildPhase; 404 | buildActionMask = 2147483647; 405 | files = ( 406 | 6003F59E195388D20070C39A /* SLAppDelegate.m in Sources */, 407 | 6003F5A7195388D20070C39A /* SLViewController.m in Sources */, 408 | 6003F59A195388D20070C39A /* main.m in Sources */, 409 | ); 410 | runOnlyForDeploymentPostprocessing = 0; 411 | }; 412 | 6003F5AA195388D20070C39A /* Sources */ = { 413 | isa = PBXSourcesBuildPhase; 414 | buildActionMask = 2147483647; 415 | files = ( 416 | 6003F5BC195388D20070C39A /* Tests.m in Sources */, 417 | ); 418 | runOnlyForDeploymentPostprocessing = 0; 419 | }; 420 | /* End PBXSourcesBuildPhase section */ 421 | 422 | /* Begin PBXTargetDependency section */ 423 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 424 | isa = PBXTargetDependency; 425 | target = 6003F589195388D20070C39A /* StackLayout_Example */; 426 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 427 | }; 428 | /* End PBXTargetDependency section */ 429 | 430 | /* Begin PBXVariantGroup section */ 431 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 432 | isa = PBXVariantGroup; 433 | children = ( 434 | 6003F597195388D20070C39A /* en */, 435 | ); 436 | name = InfoPlist.strings; 437 | sourceTree = ""; 438 | }; 439 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 440 | isa = PBXVariantGroup; 441 | children = ( 442 | 6003F5B9195388D20070C39A /* en */, 443 | ); 444 | name = InfoPlist.strings; 445 | sourceTree = ""; 446 | }; 447 | /* End PBXVariantGroup section */ 448 | 449 | /* Begin XCBuildConfiguration section */ 450 | 6003F5BD195388D20070C39A /* Debug */ = { 451 | isa = XCBuildConfiguration; 452 | buildSettings = { 453 | ALWAYS_SEARCH_USER_PATHS = NO; 454 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 455 | CLANG_CXX_LIBRARY = "libc++"; 456 | CLANG_ENABLE_MODULES = YES; 457 | CLANG_ENABLE_OBJC_ARC = YES; 458 | CLANG_WARN_BOOL_CONVERSION = YES; 459 | CLANG_WARN_CONSTANT_CONVERSION = YES; 460 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 461 | CLANG_WARN_EMPTY_BODY = YES; 462 | CLANG_WARN_ENUM_CONVERSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 467 | COPY_PHASE_STRIP = NO; 468 | GCC_C_LANGUAGE_STANDARD = gnu99; 469 | GCC_DYNAMIC_NO_PIC = NO; 470 | GCC_OPTIMIZATION_LEVEL = 0; 471 | GCC_PREPROCESSOR_DEFINITIONS = ( 472 | "DEBUG=1", 473 | "$(inherited)", 474 | ); 475 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 476 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_UNDECLARED_SELECTOR = YES; 479 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 480 | GCC_WARN_UNUSED_FUNCTION = YES; 481 | GCC_WARN_UNUSED_VARIABLE = YES; 482 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 483 | ONLY_ACTIVE_ARCH = YES; 484 | SDKROOT = iphoneos; 485 | TARGETED_DEVICE_FAMILY = "1,2"; 486 | }; 487 | name = Debug; 488 | }; 489 | 6003F5BE195388D20070C39A /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | ALWAYS_SEARCH_USER_PATHS = NO; 493 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 494 | CLANG_CXX_LIBRARY = "libc++"; 495 | CLANG_ENABLE_MODULES = YES; 496 | CLANG_ENABLE_OBJC_ARC = YES; 497 | CLANG_WARN_BOOL_CONVERSION = YES; 498 | CLANG_WARN_CONSTANT_CONVERSION = YES; 499 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 500 | CLANG_WARN_EMPTY_BODY = YES; 501 | CLANG_WARN_ENUM_CONVERSION = YES; 502 | CLANG_WARN_INT_CONVERSION = YES; 503 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 504 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 505 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 506 | COPY_PHASE_STRIP = YES; 507 | ENABLE_NS_ASSERTIONS = NO; 508 | GCC_C_LANGUAGE_STANDARD = gnu99; 509 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 510 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 511 | GCC_WARN_UNDECLARED_SELECTOR = YES; 512 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 513 | GCC_WARN_UNUSED_FUNCTION = YES; 514 | GCC_WARN_UNUSED_VARIABLE = YES; 515 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 516 | SDKROOT = iphoneos; 517 | TARGETED_DEVICE_FAMILY = "1,2"; 518 | VALIDATE_PRODUCT = YES; 519 | }; 520 | name = Release; 521 | }; 522 | 6003F5C0195388D20070C39A /* Debug */ = { 523 | isa = XCBuildConfiguration; 524 | baseConfigurationReference = 7F1BD4D7C2A14FDAD7C08CA3 /* Pods-StackLayout_Example.debug.xcconfig */; 525 | buildSettings = { 526 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 527 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 528 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 529 | GCC_PREFIX_HEADER = "StackLayout/StackLayout-Prefix.pch"; 530 | INFOPLIST_FILE = "StackLayout/StackLayout-Info.plist"; 531 | MODULE_NAME = ExampleApp; 532 | PRODUCT_NAME = "$(TARGET_NAME)"; 533 | WRAPPER_EXTENSION = app; 534 | }; 535 | name = Debug; 536 | }; 537 | 6003F5C1195388D20070C39A /* Release */ = { 538 | isa = XCBuildConfiguration; 539 | baseConfigurationReference = 17202CAD40FD122F4C81492B /* Pods-StackLayout_Example.release.xcconfig */; 540 | buildSettings = { 541 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 542 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 543 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 544 | GCC_PREFIX_HEADER = "StackLayout/StackLayout-Prefix.pch"; 545 | INFOPLIST_FILE = "StackLayout/StackLayout-Info.plist"; 546 | MODULE_NAME = ExampleApp; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | WRAPPER_EXTENSION = app; 549 | }; 550 | name = Release; 551 | }; 552 | 6003F5C3195388D20070C39A /* Debug */ = { 553 | isa = XCBuildConfiguration; 554 | baseConfigurationReference = 5EF07AE193D6D0B8F50FE729 /* Pods-StackLayout_Tests.debug.xcconfig */; 555 | buildSettings = { 556 | BUNDLE_LOADER = "$(TEST_HOST)"; 557 | FRAMEWORK_SEARCH_PATHS = ( 558 | "$(SDKROOT)/Developer/Library/Frameworks", 559 | "$(inherited)", 560 | "$(DEVELOPER_FRAMEWORKS_DIR)", 561 | ); 562 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 563 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 564 | GCC_PREPROCESSOR_DEFINITIONS = ( 565 | "DEBUG=1", 566 | "$(inherited)", 567 | ); 568 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 569 | PRODUCT_NAME = "$(TARGET_NAME)"; 570 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StackLayout_Example.app/StackLayout_Example"; 571 | WRAPPER_EXTENSION = xctest; 572 | }; 573 | name = Debug; 574 | }; 575 | 6003F5C4195388D20070C39A /* Release */ = { 576 | isa = XCBuildConfiguration; 577 | baseConfigurationReference = 497B11FAC9F7D1FA195461D5 /* Pods-StackLayout_Tests.release.xcconfig */; 578 | buildSettings = { 579 | BUNDLE_LOADER = "$(TEST_HOST)"; 580 | FRAMEWORK_SEARCH_PATHS = ( 581 | "$(SDKROOT)/Developer/Library/Frameworks", 582 | "$(inherited)", 583 | "$(DEVELOPER_FRAMEWORKS_DIR)", 584 | ); 585 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 586 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 587 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 588 | PRODUCT_NAME = "$(TARGET_NAME)"; 589 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StackLayout_Example.app/StackLayout_Example"; 590 | WRAPPER_EXTENSION = xctest; 591 | }; 592 | name = Release; 593 | }; 594 | /* End XCBuildConfiguration section */ 595 | 596 | /* Begin XCConfigurationList section */ 597 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "StackLayout" */ = { 598 | isa = XCConfigurationList; 599 | buildConfigurations = ( 600 | 6003F5BD195388D20070C39A /* Debug */, 601 | 6003F5BE195388D20070C39A /* Release */, 602 | ); 603 | defaultConfigurationIsVisible = 0; 604 | defaultConfigurationName = Release; 605 | }; 606 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "StackLayout_Example" */ = { 607 | isa = XCConfigurationList; 608 | buildConfigurations = ( 609 | 6003F5C0195388D20070C39A /* Debug */, 610 | 6003F5C1195388D20070C39A /* Release */, 611 | ); 612 | defaultConfigurationIsVisible = 0; 613 | defaultConfigurationName = Release; 614 | }; 615 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "StackLayout_Tests" */ = { 616 | isa = XCConfigurationList; 617 | buildConfigurations = ( 618 | 6003F5C3195388D20070C39A /* Debug */, 619 | 6003F5C4195388D20070C39A /* Release */, 620 | ); 621 | defaultConfigurationIsVisible = 0; 622 | defaultConfigurationName = Release; 623 | }; 624 | /* End XCConfigurationList section */ 625 | }; 626 | rootObject = 6003F582195388D10070C39A /* Project object */; 627 | } 628 | -------------------------------------------------------------------------------- /Example/StackLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/StackLayout.xcodeproj/xcshareddata/xcschemes/StackLayout-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 | 61 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Example/StackLayout.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/StackLayout/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" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/StackLayout/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/StackLayout/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/StackLayout/SLAppDelegate.h: -------------------------------------------------------------------------------- 1 | 2 | @import UIKit; 3 | 4 | @interface SLAppDelegate : UIResponder 5 | 6 | @property (strong, nonatomic) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /Example/StackLayout/SLAppDelegate.m: -------------------------------------------------------------------------------- 1 | 2 | #import "SLAppDelegate.h" 3 | 4 | @implementation SLAppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 7 | { 8 | // Override point for customization after application launch. 9 | return YES; 10 | } 11 | 12 | - (void)applicationWillResignActive:(UIApplication *)application 13 | { 14 | // 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. 15 | // 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. 16 | } 17 | 18 | - (void)applicationDidEnterBackground:(UIApplication *)application 19 | { 20 | // 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. 21 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 22 | } 23 | 24 | - (void)applicationWillEnterForeground:(UIApplication *)application 25 | { 26 | // 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. 27 | } 28 | 29 | - (void)applicationDidBecomeActive:(UIApplication *)application 30 | { 31 | // 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. 32 | } 33 | 34 | - (void)applicationWillTerminate:(UIApplication *)application 35 | { 36 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Example/StackLayout/SLViewController.h: -------------------------------------------------------------------------------- 1 | 2 | @import UIKit; 3 | 4 | @interface SLViewController : UIViewController 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /Example/StackLayout/SLViewController.m: -------------------------------------------------------------------------------- 1 | 2 | #import "SLViewController.h" 3 | 4 | #import "UIView+StackLayout.h" 5 | 6 | @interface SLViewController () 7 | 8 | @end 9 | 10 | @implementation SLViewController 11 | 12 | - (void)viewDidLoad 13 | { 14 | [super viewDidLoad]; 15 | 16 | UIButton *red = [UIButton buttonWithType:UIButtonTypeCustom]; 17 | red.backgroundColor = [UIColor redColor]; 18 | [red setTitle:@"Red" forState:UIControlStateNormal]; 19 | 20 | UIButton *green = [UIButton buttonWithType:UIButtonTypeCustom]; 21 | green.backgroundColor = [UIColor greenColor]; 22 | [green setTitle:@"Green" forState:UIControlStateNormal]; 23 | 24 | UIButton *blue = [UIButton buttonWithType:UIButtonTypeCustom]; 25 | blue.backgroundColor = [UIColor blueColor]; 26 | [blue setTitle:@"Blue" forState:UIControlStateNormal]; 27 | 28 | UILabel *wrappingLabel = [[UILabel alloc] init]; 29 | [wrappingLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; 30 | wrappingLabel.text = @"This is some really long text that will cause the label to wrap to more than one line. Hopefully, when it wraps the layout will adjust the height accordingly. There is no way to know until you try it, though."; 31 | wrappingLabel.numberOfLines = 0; 32 | 33 | UIView *fakeCell = [[UIView alloc] init]; 34 | 35 | [fakeCell addSubviewsWithHorizontalLayout:@[red, green, wrappingLabel, blue] configurationBlock:^(SLHorizontalStackLayout *layout) { 36 | layout.spacingPriority = UILayoutPriorityRequired - 2; 37 | layout.spacing = 20; 38 | layout.verticalAlignment = SLAlignmentTop; 39 | layout.horizontalAlignment = SLAlignmentFill; 40 | [layout setCustomSpacing:0 betweenView:red andView:green]; 41 | }]; 42 | fakeCell.backgroundColor = [UIColor purpleColor]; 43 | 44 | // Weakly make the fake cell as small vertically as possibble 45 | NSLayoutConstraint *heightSmall = [fakeCell.heightAnchor constraintEqualToConstant:0]; 46 | heightSmall.priority = 1; 47 | heightSmall.active = true; 48 | 49 | [self.view addSubviewsWithHorizontalLayout:@[fakeCell] configurationBlock:^(SLHorizontalStackLayout *layout) { 50 | layout.verticalAlignment = SLAlignmentCenter; 51 | layout.horizontalAlignment = SLAlignmentCenter; 52 | }]; 53 | } 54 | 55 | - (void)didReceiveMemoryWarning 56 | { 57 | [super didReceiveMemoryWarning]; 58 | // Dispose of any resources that can be recreated. 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Example/StackLayout/StackLayout-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 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/StackLayout/StackLayout-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/StackLayout/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/StackLayout/main.m: -------------------------------------------------------------------------------- 1 | 2 | @import UIKit; 3 | #import "SLAppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([SLAppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /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 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Example/Tests/Tests.m: -------------------------------------------------------------------------------- 1 | 2 | @import XCTest; 3 | 4 | @interface Tests : XCTestCase 5 | 6 | @end 7 | 8 | @implementation Tests 9 | 10 | - (void)setUp 11 | { 12 | [super setUp]; 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | } 15 | 16 | - (void)tearDown 17 | { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | [super tearDown]; 20 | } 21 | 22 | - (void)testExample 23 | { 24 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 25 | } 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Images/ToolbarLayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridger/StackLayout/427da19e59ab5ee3200804ce985b6395efcb1cb3/Images/ToolbarLayout.png -------------------------------------------------------------------------------- /Images/WelcomeTipLayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridger/StackLayout/427da19e59ab5ee3200804ce985b6395efcb1cb3/Images/WelcomeTipLayout.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Bridger Maxwell 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 | -------------------------------------------------------------------------------- /Pod/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridger/StackLayout/427da19e59ab5ee3200804ce985b6395efcb1cb3/Pod/Assets/.gitkeep -------------------------------------------------------------------------------- /Pod/StackLayout/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bridger/StackLayout/427da19e59ab5ee3200804ce985b6395efcb1cb3/Pod/StackLayout/.gitkeep -------------------------------------------------------------------------------- /Pod/StackLayout/SLStackLayout.h: -------------------------------------------------------------------------------- 1 | // Copyright © 2015 Bridger Maxwell. All rights reserved. 2 | 3 | #import 4 | 5 | NS_ASSUME_NONNULL_BEGIN 6 | 7 | typedef NS_ENUM(NSInteger, SLAlignment) { 8 | SLAlignmentNone, 9 | 10 | SLAlignmentLeading, 11 | SLAlignmentTop = SLAlignmentLeading, 12 | 13 | SLAlignmentTrailing, 14 | SLAlignmentBottom = SLAlignmentTrailing, 15 | 16 | SLAlignmentFill, 17 | 18 | SLAlignmentCenter 19 | }; 20 | 21 | 22 | @protocol SLStackLayout 23 | 24 | #pragma mark Alignment 25 | /* 26 | The alignment pulls subviews to be flush with the margin constraints. Use the fill alignment 27 | to duplicate behavior of UIStackView. 28 | When centering is used it respects the centeringAlignmentPriority. 29 | When you use the center alignment for the major axis of alignment, a hidden subview is added to 30 | help with the centering. 31 | */ 32 | 33 | // Defaults to SLAlignmentNone, which creates no constraints 34 | @property (nonatomic) SLAlignment verticalAlignment; 35 | 36 | // Defaults to SLAlignmentNone, which creates no constraints 37 | @property (nonatomic) SLAlignment horizontalAlignment; 38 | 39 | /* 40 | This is the priority for constraints used for centering views with SLAlignmentCenter. It defaults to 41 | UILayoutPriorityDefaultHigh + 10. 42 | */ 43 | @property (nonatomic) UILayoutPriority centeringAlignmentPriority; 44 | 45 | #pragma mark Spacing 46 | /* 47 | All subviews will have a required space between them. 48 | */ 49 | 50 | /* 51 | ...[firstSubview]-spacing-[secondSubview]-spacing-[thirdSubview]... 52 | Set the spacing between all pairs of adjacent subviews. Any calls to 53 | setCustomSpacing:betweenView:andView: will override this setting for 54 | those views. 55 | */ 56 | @property (nonatomic) CGFloat spacing; 57 | 58 | /* This is the priority that all spacing constraints use. It defaults to required. You can make it non-required 59 | and add additional constraints to create spacing adjustments. 60 | Another strategy to create variable spacings is to insert invisible spacer views. 61 | */ 62 | @property (nonatomic) UILayoutPriority spacingPriority; 63 | 64 | /* 65 | Set the space constraint between two adjacent views. [firstView]-spacing-[secondView] 66 | If firstView and secondView are not adjacent then this will raise an exception. 67 | */ 68 | - (void)setCustomSpacing:(CGFloat)spacing betweenView:(UIView *)firstView andView:(UIView *)secondView; 69 | 70 | /* 71 | Set the space constraint between two adjacent views, only specifying the second view. [whateverViewIsBefore]-spacing-[subview]. 72 | If subview is the first view (so there is no spacing before it) then this will raise an exception. 73 | */ 74 | - (void)setCustomSpacing:(CGFloat)spacing beforeView:(UIView *)subview; 75 | 76 | /* 77 | Set the space constraint between two adjacent views, only specifying the first view. [subview]-spacing-[whateverViewIsAfter]. 78 | If subview is the last view (so there is no spacing after it) then this will raise an exception. 79 | */ 80 | - (void)setCustomSpacing:(CGFloat)spacing afterView:(UIView *)subview; 81 | 82 | #pragma mark Margins 83 | /* 84 | The margins define a region on the edges of the superview where the subviews will not enter. The subviews 85 | might not necessarily go up to the margins, depending on the alignment. To guarantee that subviews go all 86 | the way to the margins use the fill alignment. 87 | */ 88 | 89 | // V:|-(>=margin)-[subview]-.... 90 | @property (nonatomic) CGFloat topMargin; 91 | 92 | // V:...[subview]-(>=margin)-| 93 | @property (nonatomic) CGFloat bottomMargin; 94 | 95 | // A convenience to set both top and bottom margins. The getter should not be invoked. 96 | @property (nonatomic) CGFloat verticalMargins; 97 | 98 | // H:|-(>=margin)-[subview] 99 | @property (nonatomic) CGFloat leadingMargin; 100 | 101 | // H:[subview]-(>=margin)-| 102 | @property (nonatomic) CGFloat trailingMargin; 103 | 104 | // A convenience to set both leading and trailing margins. The getter should not be invoked. 105 | @property (nonatomic) CGFloat horizontalMargins; 106 | 107 | /* Uses margin layout attributes from the superview for edge constraints where applicable. This is similar 108 | to UIStackView's property of the same name. 109 | Defaults to NO. 110 | */ 111 | @property(nonatomic, getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement; 112 | 113 | /* This is the priority that all margin constraints use. It defaults to required. 114 | */ 115 | @property (nonatomic) UILayoutPriority marginsPriority; 116 | 117 | #pragma mark Other 118 | 119 | /* If this is true then any subview which implements setPreferredMaxLayoutWidth: will have that property set 120 | to the actual layout width when layoutSubviews is called on the superview. 121 | It is illegal for this to cause a change of height in the containing view, so this property should be turned 122 | off and managed from a higher view level if that is true. It may trigger an assert in layoutSubviews. 123 | Defaults to NO. 124 | */ 125 | @property (nonatomic) BOOL adjustsPreferredMaxLayoutWidthOnSubviews; 126 | 127 | @end 128 | 129 | 130 | 131 | 132 | /* This class should not be initialized directly! Always use a subclass. */ 133 | @interface SLStackLayoutBase : NSObject 134 | 135 | @property (readonly) NSArray *views; 136 | @property (readonly, weak, nullable) UIView * superview; 137 | 138 | /* This is called when the superview's layoutSubviews is invoked (which depends on method swizzling). This 139 | is when setPreferredMaxLayoutWidth: will be called on subviews if adjustsPreferredMaxLayoutWidthOnSubviews is 140 | true. 141 | */ 142 | - (void)subviewsLaidOut; 143 | 144 | @end 145 | 146 | 147 | 148 | @interface SLHorizontalStackLayout : SLStackLayoutBase 149 | 150 | /* Instead of using this directly, use UIView's -addSubviewsWithVerticalLayout: or -addSubviewsWithHorizontalLayout: 151 | If you do call this directly then all subviews should already be added to the superview. The subviews should all have 152 | translatesAutoresizingMaskIntoConstraints set to false. 153 | */ 154 | - (instancetype)initWithViews:(NSArray *)subviews inSuperview:(UIView *)superview configurationBlock:(void (NS_NOESCAPE ^__nullable)(SLHorizontalStackLayout *))configurationBlock; 155 | 156 | /* 157 | This sets a margin different than the global topMargin for a specific view. 158 | */ 159 | - (void)setCustomTopMargin:(CGFloat)topMargin forView:(UIView *)view; 160 | 161 | /* 162 | This sets a margin different than the global bottomMargin for a specific view. 163 | */ 164 | - (void)setCustomBottomMargin:(CGFloat)bottomMargin forView:(UIView *)view; 165 | 166 | /* 167 | This sets a custom top and bottom margin for a specific view. 168 | */ 169 | - (void)setCustomVerticalMargins:(CGFloat)verticalMargins forView:(UIView *)view; 170 | 171 | @end 172 | 173 | 174 | 175 | @interface SLVerticalStackLayout : SLStackLayoutBase 176 | 177 | /* Instead of using this directly, use UIView's -addSubviewsWithVerticalLayout: or -addSubviewsWithHorizontalLayout: 178 | If you do call this directly then all subviews should already be added to the superview. The subviews should all have 179 | translatesAutoresizingMaskIntoConstraints set to false. 180 | */ 181 | - (instancetype)initWithViews:(NSArray *)subviews inSuperview:(UIView *)superview configurationBlock:(void (NS_NOESCAPE ^__nullable)(SLVerticalStackLayout *))configurationBlock; 182 | 183 | /* 184 | This sets a margin different than the global leadingMargin for a specific view. 185 | */ 186 | - (void)setCustomLeadingMargin:(CGFloat)leadingMargin forView:(UIView *)view; 187 | 188 | /* 189 | This sets a margin different than the global trailingMargin for a specific view. 190 | */ 191 | - (void)setCustomTrailingMargin:(CGFloat)trailingMargin forView:(UIView *)view; 192 | 193 | /* 194 | This sets a custom leading and trailing margin for a specific view. 195 | */ 196 | - (void)setCustomHorizontalMargins:(CGFloat)horizontalMargins forView:(UIView *)view; 197 | 198 | @end 199 | 200 | 201 | NS_ASSUME_NONNULL_END 202 | -------------------------------------------------------------------------------- /Pod/StackLayout/SLStackLayout.m: -------------------------------------------------------------------------------- 1 | // Copyright © 2015 Bridger Maxwell. All rights reserved. 2 | 3 | #import "SLStackLayout.h" 4 | #import "UIView+StackLayout.h" 5 | 6 | NS_ASSUME_NONNULL_BEGIN 7 | 8 | @implementation UIView (StackLayoutInternal) 9 | 10 | - (NSLayoutConstraint *)sl_constraintAligningAttribute:(NSLayoutAttribute)attribute withView:(UIView *)view 11 | { 12 | return [NSLayoutConstraint constraintWithItem:self attribute:attribute relatedBy:NSLayoutRelationEqual toItem:view attribute:attribute multiplier:1.0 constant:0]; 13 | } 14 | 15 | - (NSLayoutConstraint *)sl_constraintWithSpace:(CGFloat)space followedByView:(UIView *)view isHorizontal:(BOOL)isHorizontal 16 | { 17 | NSLayoutAttribute leadingAttribute = isHorizontal ? NSLayoutAttributeLeading : NSLayoutAttributeTop; 18 | NSLayoutAttribute trailingAttribute = isHorizontal ? NSLayoutAttributeTrailing : NSLayoutAttributeBottom; 19 | 20 | // view.leading = self.trailing + space 21 | return [NSLayoutConstraint constraintWithItem:view attribute:leadingAttribute relatedBy:NSLayoutRelationEqual toItem:self attribute:trailingAttribute multiplier:1.0 constant:space]; 22 | } 23 | 24 | @end 25 | 26 | 27 | @interface SLStackLayoutBase () 28 | 29 | @property (readonly) BOOL isHorizontal; 30 | @property (readonly) NSLayoutAttribute majorLeadingAttribute; 31 | @property (readonly) NSLayoutAttribute majorTrailingAttribute; 32 | @property (readonly) NSLayoutAttribute majorLeadingMarginAttribute; 33 | @property (readonly) NSLayoutAttribute majorTrailingMarginAttribute; 34 | 35 | @property (readonly) NSLayoutAttribute minorLeadingAttribute; 36 | @property (readonly) NSLayoutAttribute minorTrailingAttribute; 37 | @property (readonly) NSLayoutAttribute minorLeadingMarginAttribute; 38 | @property (readonly) NSLayoutAttribute minorTrailingMarginAttribute; 39 | 40 | @property (readonly) NSLayoutAttribute majorSizeAttribute; 41 | @property (readonly) NSLayoutAttribute majorCenterAttribute; 42 | 43 | @property (readonly) NSLayoutAttribute minorSizeAttribute; 44 | @property (readonly) NSLayoutAttribute minorCenterAttribute; 45 | 46 | @property (readonly) UIView *leadingView; 47 | @property (readonly) UIView *trailingView; 48 | 49 | @property (nonatomic) NSArray *spacingConstraints; 50 | @property (nonatomic) NSLayoutConstraint *majorLeadingMarginConstraint; 51 | @property (nonatomic) NSLayoutConstraint *majorTrailingMarginConstraint; 52 | @property (nonatomic) NSArray *minorLeadingMarginConstraints; 53 | @property (nonatomic) NSArray *minorTrailingMarginConstraints; 54 | 55 | @property (nonatomic, nullable) NSMapTable *minorLeadingMarginOverrides; 56 | @property (nonatomic, nullable) NSMapTable *minorTrailingMarginOverrides; 57 | 58 | @property (nonatomic, nullable) NSArray *majorAlignmentConstraints; 59 | @property (nonatomic, nullable) UIView *majorAlignmentHelperView; 60 | @property (nonatomic, nullable) NSArray *minorAlignmentConstraints; 61 | 62 | @property (nonatomic) CGFloat majorLeadingMargin; 63 | @property (nonatomic) CGFloat majorTrailingMargin; 64 | @property (nonatomic) CGFloat minorLeadingMargin; 65 | @property (nonatomic) CGFloat minorTrailingMargin; 66 | 67 | @property (nonatomic) SLAlignment majorAlignment; 68 | @property (nonatomic) SLAlignment minorAlignment; 69 | 70 | @property (nonatomic) BOOL suppressInitialConstraints; 71 | @property (nonatomic, nullable) NSMapTable *customSpaces; 72 | 73 | // These are all the public properties that have just been marked readwrite 74 | @property (readwrite) NSArray *views; 75 | @property (readwrite, weak, nullable) UIView *superview; 76 | 77 | // These are implemented by this base class but are only exposed in the two subclasses 78 | @property (nonatomic) CGFloat spacing; 79 | - (void)setCustomSpacing:(CGFloat)spacing betweenView:(UIView *)firstView andView:(UIView *)secondView; 80 | - (void)setCustomSpacing:(CGFloat)spacing beforeView:(UIView *)subview; 81 | - (void)setCustomSpacing:(CGFloat)spacing afterView:(UIView *)subview; 82 | @property (nonatomic) UILayoutPriority spacingPriority; 83 | @property (nonatomic) UILayoutPriority centeringAlignmentPriority; 84 | @property(nonatomic, getter=isLayoutMarginsRelativeArrangement) BOOL layoutMarginsRelativeArrangement; 85 | @property (nonatomic) BOOL adjustsPreferredMaxLayoutWidthOnSubviews; 86 | @property (nonatomic) UILayoutPriority marginsPriority; 87 | 88 | @end 89 | 90 | @implementation SLStackLayoutBase 91 | 92 | - (BOOL)isHorizontal 93 | { 94 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 95 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 96 | userInfo:nil]; 97 | } 98 | 99 | - (NSLayoutAttribute)majorLeadingAttribute 100 | { 101 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 102 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 103 | userInfo:nil]; 104 | } 105 | 106 | - (NSLayoutAttribute)majorTrailingAttribute 107 | { 108 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 109 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 110 | userInfo:nil]; 111 | } 112 | 113 | - (NSLayoutAttribute)majorLeadingMarginAttribute 114 | { 115 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 116 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 117 | userInfo:nil]; 118 | } 119 | 120 | - (NSLayoutAttribute)majorTrailingMarginAttribute 121 | { 122 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 123 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 124 | userInfo:nil]; 125 | } 126 | 127 | - (NSLayoutAttribute)minorLeadingAttribute 128 | { 129 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 130 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 131 | userInfo:nil]; 132 | } 133 | 134 | - (NSLayoutAttribute)minorTrailingAttribute 135 | { 136 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 137 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 138 | userInfo:nil]; 139 | } 140 | 141 | - (NSLayoutAttribute)minorLeadingMarginAttribute 142 | { 143 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 144 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 145 | userInfo:nil]; 146 | } 147 | 148 | - (NSLayoutAttribute)minorTrailingMarginAttribute 149 | { 150 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 151 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 152 | userInfo:nil]; 153 | } 154 | 155 | - (NSLayoutAttribute)majorSizeAttribute 156 | { 157 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 158 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 159 | userInfo:nil]; 160 | } 161 | 162 | - (NSLayoutAttribute)majorCenterAttribute 163 | { 164 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 165 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 166 | userInfo:nil]; 167 | } 168 | 169 | - (NSLayoutAttribute)minorSizeAttribute 170 | { 171 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 172 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 173 | userInfo:nil]; 174 | } 175 | 176 | - (NSLayoutAttribute)minorCenterAttribute 177 | { 178 | @throw [NSException exceptionWithName:NSInternalInconsistencyException 179 | reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] 180 | userInfo:nil]; 181 | } 182 | 183 | - (instancetype)initWithViews:(NSArray *)subviews inSuperview:(UIView *)superview 184 | { 185 | if (subviews.count == 0) { 186 | [NSException raise:NSInvalidArgumentException format:@"there must be at least one view in a layout"]; 187 | } 188 | 189 | if (self = [super init]) { 190 | self.views = subviews; 191 | self.superview = superview; 192 | 193 | // The alignment priority is high, but not required. This is so it very strongly tries to align, 194 | // but won't override the margin constraints (which are required) 195 | _centeringAlignmentPriority = UILayoutPriorityDefaultHigh + 10; 196 | _spacingPriority = UILayoutPriorityRequired; 197 | _marginsPriority = UILayoutPriorityRequired; 198 | _adjustsPreferredMaxLayoutWidthOnSubviews = NO; 199 | 200 | self.suppressInitialConstraints = YES; 201 | } 202 | return self; 203 | } 204 | 205 | - (void)buildInitialConstraints 206 | { 207 | self.suppressInitialConstraints = NO; 208 | 209 | [self rebuildSpacingConstraints]; 210 | self.majorAlignment = self.majorAlignment; 211 | self.minorAlignment = self.minorAlignment; 212 | 213 | [self rebuildMajorLeadingConstraint]; 214 | [self rebuildMajorTrailingConstraint]; 215 | [self rebuildMinorLeadingConstraints]; 216 | [self rebuildMinorTrailingConstraints]; 217 | } 218 | 219 | - (void)rebuildSpacingConstraints 220 | { 221 | if (self.suppressInitialConstraints) return; 222 | 223 | for (NSLayoutConstraint *constraint in self.spacingConstraints) { 224 | constraint.active = false; 225 | } 226 | 227 | NSMutableArray *spacingConstraints = [NSMutableArray new]; 228 | UIView *previousSubview = nil; 229 | for (UIView *subview in self.views) { 230 | if (previousSubview) { 231 | if (self.spacingPriority > 0) { 232 | NSNumber *initialSpace = [self.customSpaces objectForKey:previousSubview]; 233 | CGFloat spacing = initialSpace ? [initialSpace doubleValue] : self.spacing; 234 | 235 | // subview.leading = previousSubview.trailing + spacing 236 | NSLayoutConstraint *spaceConstraint = [previousSubview sl_constraintWithSpace:spacing followedByView:subview isHorizontal:self.isHorizontal]; 237 | spaceConstraint.priority = self.spacingPriority; 238 | [spacingConstraints addObject:spaceConstraint]; 239 | spaceConstraint.active = true; 240 | } 241 | } 242 | previousSubview = subview; 243 | } 244 | self.spacingConstraints = spacingConstraints; 245 | } 246 | 247 | - (void)setSpacingPriority:(UILayoutPriority)priority 248 | { 249 | if (_spacingPriority != priority) { 250 | _spacingPriority = priority; 251 | [self rebuildSpacingConstraints]; 252 | } 253 | } 254 | 255 | - (void)rebuildMajorLeadingConstraint 256 | { 257 | if (self.suppressInitialConstraints) return; 258 | 259 | self.majorLeadingMarginConstraint.active = false; 260 | 261 | NSLayoutAttribute marginAttribute = (self.isLayoutMarginsRelativeArrangement 262 | ? self.majorLeadingMarginAttribute 263 | : self.majorLeadingAttribute); 264 | NSLayoutRelation relation = ((self.majorAlignment == SLAlignmentLeading || self.majorAlignment == SLAlignmentFill) 265 | ? NSLayoutRelationEqual 266 | : NSLayoutRelationGreaterThanOrEqual); 267 | 268 | // leadingView.leading >= superview.leadingMargin + margin 269 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.leadingView attribute:self.majorLeadingAttribute relatedBy:relation toItem:self.superview attribute:marginAttribute multiplier:1.0 constant:self.majorLeadingMargin]; 270 | constraint.priority = self.marginsPriority; 271 | constraint.active = true; 272 | 273 | self.majorLeadingMarginConstraint = constraint; 274 | } 275 | 276 | - (void)rebuildMajorTrailingConstraint 277 | { 278 | if (self.suppressInitialConstraints) return; 279 | 280 | self.majorTrailingMarginConstraint.active = false; 281 | 282 | NSLayoutAttribute marginAttribute = (self.isLayoutMarginsRelativeArrangement 283 | ? self.majorTrailingMarginAttribute 284 | : self.majorTrailingAttribute); 285 | NSLayoutRelation relation = ((self.majorAlignment == SLAlignmentTrailing || self.majorAlignment == SLAlignmentFill) 286 | ? NSLayoutRelationEqual 287 | : NSLayoutRelationGreaterThanOrEqual); 288 | 289 | // superview.trailingMargin >= trailingView.trailing + margin 290 | NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.superview attribute:marginAttribute relatedBy:relation toItem:self.trailingView attribute:self.majorTrailingAttribute multiplier:1.0 constant:self.majorTrailingMargin]; 291 | constraint.priority = self.marginsPriority; 292 | constraint.active = true; 293 | 294 | self.majorTrailingMarginConstraint = constraint; 295 | } 296 | 297 | - (void)rebuildMinorLeadingConstraints 298 | { 299 | if (self.suppressInitialConstraints) return; 300 | 301 | for (NSLayoutConstraint *constraint in self.minorLeadingMarginConstraints) { 302 | constraint.active = false; 303 | } 304 | 305 | NSLayoutAttribute marginAttribute = (self.isLayoutMarginsRelativeArrangement 306 | ? self.minorLeadingMarginAttribute 307 | : self.minorLeadingAttribute); 308 | NSLayoutRelation relation = ((self.minorAlignment == SLAlignmentLeading || self.minorAlignment == SLAlignmentFill) 309 | ? NSLayoutRelationEqual 310 | : NSLayoutRelationGreaterThanOrEqual); 311 | 312 | NSMutableArray *constraints = [NSMutableArray array]; 313 | for (UIView *subview in self.views) { 314 | NSNumber *override = [self.minorLeadingMarginOverrides objectForKey:subview]; 315 | CGFloat margin = override ? [override doubleValue] : self.minorLeadingMargin; 316 | 317 | //subview.leading >= superview.leadingMargin + margin 318 | [constraints addObject:[NSLayoutConstraint 319 | constraintWithItem:subview attribute:self.minorLeadingAttribute 320 | relatedBy:relation 321 | toItem:self.superview attribute:marginAttribute 322 | multiplier:1.0 constant:margin]]; 323 | constraints.lastObject.priority = self.marginsPriority; 324 | constraints.lastObject.active = true; 325 | } 326 | 327 | self.minorLeadingMarginConstraints = constraints; 328 | } 329 | 330 | 331 | - (void)rebuildMinorTrailingConstraints 332 | { 333 | if (self.suppressInitialConstraints) return; 334 | 335 | for (NSLayoutConstraint *constraint in self.minorTrailingMarginConstraints) { 336 | constraint.active = false; 337 | } 338 | 339 | NSLayoutAttribute marginAttribute = (self.isLayoutMarginsRelativeArrangement 340 | ? self.minorTrailingMarginAttribute 341 | : self.minorTrailingAttribute); 342 | NSLayoutRelation relation = ((self.minorAlignment == SLAlignmentTrailing || self.minorAlignment == SLAlignmentFill) 343 | ? NSLayoutRelationEqual 344 | : NSLayoutRelationGreaterThanOrEqual); 345 | 346 | NSMutableArray *constraints = [NSMutableArray array]; 347 | for (UIView *subview in self.views) { 348 | NSNumber *override = [self.minorTrailingMarginOverrides objectForKey:subview]; 349 | CGFloat margin = override ? [override doubleValue] : self.minorTrailingMargin; 350 | 351 | //superview.trailingMargin >= subview.trailing + margin 352 | [constraints addObject:[NSLayoutConstraint 353 | constraintWithItem:self.superview attribute:marginAttribute 354 | relatedBy:relation 355 | toItem:subview attribute:self.minorTrailingAttribute 356 | multiplier:1.0 constant:margin]]; 357 | constraints.lastObject.priority = self.marginsPriority; 358 | constraints.lastObject.active = true; 359 | } 360 | 361 | self.minorTrailingMarginConstraints = constraints; 362 | } 363 | 364 | - (UIView *)leadingView 365 | { 366 | return self.views.firstObject; 367 | } 368 | 369 | - (UIView *)trailingView 370 | { 371 | return self.views.lastObject; 372 | } 373 | 374 | - (void)setSpacing:(CGFloat)spacing 375 | { 376 | _spacing = spacing; 377 | for (NSLayoutConstraint *spacingConstraint in self.spacingConstraints) { 378 | spacingConstraint.constant = spacing; 379 | } 380 | } 381 | 382 | - (void)setCustomSpacing:(CGFloat)spacing betweenView:(UIView *)firstView andView:(UIView *)secondView 383 | { 384 | // We store these in a map table. We find the first view and store the space that should be after that view. 385 | UIView *previousView; 386 | BOOL foundMatch = NO; 387 | for (UIView *nextView in self.views) { 388 | if ((firstView == previousView && secondView == nextView) || 389 | (firstView == nextView && secondView == previousView)) { 390 | 391 | // Found it! 392 | if (!self.customSpaces) { 393 | self.customSpaces = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:self.views.count - 1]; 394 | } 395 | [self.customSpaces setObject:@(spacing) forKey:previousView]; 396 | foundMatch = YES; 397 | } 398 | previousView = nextView; 399 | } 400 | if (!foundMatch) { 401 | // If we reach this point then the correct spacing constraint wasn't found. This means firstView and 402 | // secondView weren't adjacent siblings 403 | [NSException raise:NSInvalidArgumentException format:@"Can't set space between two views which aren't adjacent siblings."]; 404 | } 405 | 406 | // Adjust any existing constraint 407 | for (NSLayoutConstraint *spacingConstraint in self.spacingConstraints) { 408 | if ((spacingConstraint.firstItem == firstView && spacingConstraint.secondItem == secondView) || 409 | (spacingConstraint.firstItem == secondView && spacingConstraint.secondItem == firstView)) { 410 | 411 | // Found the correct space constraint! 412 | spacingConstraint.constant = spacing; 413 | return; 414 | } 415 | } 416 | } 417 | 418 | - (void)setCustomSpacing:(CGFloat)spacing beforeView:(UIView *)subview 419 | { 420 | UIView *previousView = nil; 421 | BOOL foundMatch = NO; 422 | for (UIView *view in self.views) { 423 | if (view == subview) { 424 | foundMatch = YES; 425 | break; 426 | } 427 | previousView = view; 428 | } 429 | if (foundMatch) { 430 | if (previousView) { 431 | [self setCustomSpacing:spacing betweenView:previousView andView:subview]; 432 | } else { 433 | [NSException raise:NSInvalidArgumentException format:@"Can't set a custom space before the view because there is no preceeding view."]; 434 | } 435 | } else { 436 | [NSException raise:NSInvalidArgumentException format:@"Can't set a custom space because the view isn't part of this layout."]; 437 | } 438 | } 439 | 440 | - (void)setCustomSpacing:(CGFloat)spacing afterView:(UIView *)subview 441 | { 442 | BOOL foundMatch = NO; 443 | UIView *nextView = nil; 444 | for (UIView *view in self.views) { 445 | if (view == subview) { 446 | foundMatch = YES; 447 | // Loop one more time 448 | continue; 449 | } else if (foundMatch) { 450 | nextView = view; 451 | break; 452 | } 453 | } 454 | if (foundMatch) { 455 | if (nextView) { 456 | [self setCustomSpacing:spacing betweenView:subview andView:nextView]; 457 | } else { 458 | [NSException raise:NSInvalidArgumentException format:@"Can't set a custom space after the view because it is the last view."]; 459 | } 460 | } else { 461 | [NSException raise:NSInvalidArgumentException format:@"Can't set a custom space because the view isn't part of this layout."]; 462 | } 463 | } 464 | 465 | - (void)setMajorLeadingMargin:(CGFloat)majorLeadingMargin 466 | { 467 | _majorLeadingMargin = majorLeadingMargin; 468 | self.majorLeadingMarginConstraint.constant = majorLeadingMargin; 469 | } 470 | 471 | - (void)setMajorTrailingMargin:(CGFloat)majorTrailingMargin 472 | { 473 | _majorTrailingMargin = majorTrailingMargin; 474 | self.majorTrailingMarginConstraint.constant = majorTrailingMargin; 475 | } 476 | 477 | - (void)setMinorLeadingMargin:(CGFloat)minorLeadingMargin 478 | { 479 | _minorLeadingMargin = minorLeadingMargin; 480 | for (NSLayoutConstraint *constraint in self.minorLeadingMarginConstraints) { 481 | if (![self.minorLeadingMarginOverrides objectForKey:constraint.firstItem]) { 482 | constraint.constant = minorLeadingMargin; 483 | } 484 | } 485 | } 486 | 487 | - (void)setCustomMinorLeadingMargin:(CGFloat)minorLeadingMargin forView:(UIView *)view 488 | { 489 | if (!self.minorLeadingMarginOverrides) { 490 | self.minorLeadingMarginOverrides = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:self.views.count - 1]; 491 | } 492 | [self.minorLeadingMarginOverrides setObject:@(minorLeadingMargin) forKey:view]; 493 | 494 | for (NSLayoutConstraint *constraint in self.minorLeadingMarginConstraints) { 495 | if (constraint.firstItem == view) { 496 | constraint.constant = minorLeadingMargin; 497 | } 498 | } 499 | } 500 | 501 | - (void)setMinorTrailingMargin:(CGFloat)minorTrailingMargin 502 | { 503 | _minorTrailingMargin = minorTrailingMargin; 504 | for (NSLayoutConstraint *constraint in self.minorTrailingMarginConstraints) { 505 | if (![self.minorTrailingMarginOverrides objectForKey:constraint.secondItem]) { 506 | constraint.constant = minorTrailingMargin; 507 | } 508 | } 509 | } 510 | 511 | - (void)setCustomMinorTrailingMargin:(CGFloat)minorTrailingMargin forView:(UIView *)view 512 | { 513 | if (!self.minorTrailingMarginOverrides) { 514 | self.minorTrailingMarginOverrides = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:self.views.count - 1]; 515 | } 516 | [self.minorTrailingMarginOverrides setObject:@(minorTrailingMargin) forKey:view]; 517 | 518 | for (NSLayoutConstraint *constraint in self.minorTrailingMarginConstraints) { 519 | if (constraint.secondItem == view) { 520 | constraint.constant = minorTrailingMargin; 521 | } 522 | } 523 | } 524 | 525 | - (void)setLayoutMarginsRelativeArrangement:(BOOL)layoutMarginsRelativeArrangement 526 | { 527 | if (_layoutMarginsRelativeArrangement != layoutMarginsRelativeArrangement) { 528 | _layoutMarginsRelativeArrangement = layoutMarginsRelativeArrangement; 529 | 530 | [self rebuildMajorLeadingConstraint]; 531 | [self rebuildMajorTrailingConstraint]; 532 | [self rebuildMinorLeadingConstraints]; 533 | [self rebuildMinorTrailingConstraints]; 534 | } 535 | } 536 | 537 | - (void)setMarginsPriority:(UILayoutPriority)priority 538 | { 539 | if (_marginsPriority != priority) { 540 | _marginsPriority = priority; 541 | 542 | [self rebuildMajorLeadingConstraint]; 543 | [self rebuildMajorTrailingConstraint]; 544 | [self rebuildMinorLeadingConstraints]; 545 | [self rebuildMinorTrailingConstraints]; 546 | } 547 | } 548 | 549 | - (void)setMajorAlignment:(SLAlignment)majorAlignment 550 | { 551 | if (_majorAlignment != majorAlignment || !self.minorLeadingMarginConstraints) { 552 | _majorAlignment = majorAlignment; 553 | 554 | if (self.suppressInitialConstraints) return; 555 | 556 | // We can't change these on the fly so we need to remove them and rebuild them 557 | [NSLayoutConstraint deactivateConstraints:self.majorAlignmentConstraints]; 558 | self.majorAlignmentConstraints = nil; 559 | 560 | [self.majorAlignmentHelperView removeFromSuperview]; 561 | self.majorAlignmentHelperView = nil; 562 | 563 | // By rebuilding these constraints we make sure they are either >= or ==, depending on the alignment 564 | [self rebuildMajorLeadingConstraint]; 565 | [self rebuildMajorTrailingConstraint]; 566 | 567 | if (self.majorAlignment == SLAlignmentCenter) { 568 | // This one is tricky. We need a hidden helper view to encompass the content, then we can center that view 569 | 570 | UIView *helperView = [[UIView alloc] init]; 571 | helperView.accessibilityIdentifier = @"SLStackLayoutCenteringHelper"; 572 | helperView.translatesAutoresizingMaskIntoConstraints = false; 573 | helperView.hidden = YES; 574 | [self.superview addSubview:helperView]; 575 | 576 | // Tie the helperView's edges to emcompass the whole content, and then center the helperView 577 | NSMutableArray *constraints = [NSMutableArray array]; 578 | [constraints addObject:[helperView sl_constraintAligningAttribute:self.majorLeadingAttribute withView:self.leadingView]]; 579 | [constraints addObject:[helperView sl_constraintAligningAttribute:self.majorTrailingAttribute withView:self.trailingView]]; 580 | 581 | [constraints addObject:[helperView sl_constraintAligningAttribute:self.majorCenterAttribute withView:self.superview]]; 582 | 583 | // The minor axis for this view doesn't matter, but we need to set it so that the layout isn't ambiguous 584 | [constraints addObject:[helperView sl_constraintAligningAttribute:self.minorLeadingAttribute withView:self.superview]]; 585 | [constraints addObject:[NSLayoutConstraint constraintWithItem:helperView attribute:self.minorSizeAttribute relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0.0]]; 586 | 587 | for (NSLayoutConstraint *constraint in constraints) { 588 | constraint.priority = self.centeringAlignmentPriority; 589 | } 590 | [NSLayoutConstraint activateConstraints:constraints]; 591 | self.majorAlignmentConstraints = constraints; 592 | self.majorAlignmentHelperView = helperView; 593 | } 594 | } 595 | } 596 | 597 | - (void)setMinorAlignment:(SLAlignment)minorAlignment 598 | { 599 | if (_minorAlignment != minorAlignment || !self.minorLeadingMarginConstraints) { 600 | _minorAlignment = minorAlignment; 601 | 602 | if (self.suppressInitialConstraints) return; 603 | 604 | // We can't change these on the fly so we need to remove them and rebuild them 605 | [NSLayoutConstraint deactivateConstraints:self.minorAlignmentConstraints]; 606 | self.minorAlignmentConstraints = nil; 607 | 608 | // By rebuilding these constraints we make sure they are either >= or ==, depending on the alignment 609 | [self rebuildMinorLeadingConstraints]; 610 | [self rebuildMinorTrailingConstraints]; 611 | 612 | if (self.minorAlignment == SLAlignmentCenter) { 613 | NSMutableArray *constraints = [NSMutableArray array]; 614 | for (UIView *subview in self.views) { 615 | // superview.center = subview.center 616 | NSLayoutConstraint *constraint = [self.superview sl_constraintAligningAttribute:self.minorCenterAttribute withView:subview]; 617 | constraint.priority = self.centeringAlignmentPriority; 618 | [constraints addObject:constraint]; 619 | } 620 | [NSLayoutConstraint activateConstraints:constraints]; 621 | self.minorAlignmentConstraints = constraints; 622 | } 623 | } 624 | } 625 | 626 | - (void)setCenteringAlignmentPriority:(UILayoutPriority)alignmentPriority 627 | { 628 | if (_centeringAlignmentPriority != alignmentPriority) { 629 | // Trigger a rebuild of these constraints by setting and unsetting them 630 | if (self.majorAlignment == SLAlignmentCenter) { 631 | self.majorAlignment = SLAlignmentNone; 632 | self.majorAlignment = SLAlignmentCenter; 633 | } 634 | 635 | if (self.minorAlignment == SLAlignmentCenter) { 636 | self.minorAlignment = SLAlignmentNone; 637 | self.minorAlignment = SLAlignmentCenter; 638 | } 639 | } 640 | } 641 | 642 | - (void)setAdjustsPreferredMaxLayoutWidthOnSubviews:(BOOL)adjustValues 643 | { 644 | _adjustsPreferredMaxLayoutWidthOnSubviews = adjustValues; 645 | } 646 | 647 | - (void)subviewsLaidOut 648 | { 649 | if (!self.adjustsPreferredMaxLayoutWidthOnSubviews) { 650 | return; 651 | } 652 | for (UIView *subview in self.views) { 653 | if ([subview respondsToSelector:@selector(setPreferredMaxLayoutWidth:)]) { 654 | CGFloat layoutWidth = subview.frame.size.width; 655 | [(id)subview setPreferredMaxLayoutWidth:layoutWidth]; 656 | 657 | [subview updateConstraintsIfNeeded]; 658 | } 659 | } 660 | } 661 | 662 | @end 663 | 664 | 665 | @implementation SLHorizontalStackLayout 666 | 667 | - (instancetype)initWithViews:(NSArray *)subviews inSuperview:(UIView *)superview configurationBlock:(void (NS_NOESCAPE ^__nullable)(SLHorizontalStackLayout *))configurationBlock 668 | { 669 | self = [super initWithViews:subviews inSuperview:superview]; 670 | if (self) { 671 | if (configurationBlock) { 672 | configurationBlock(self); 673 | } 674 | [self buildInitialConstraints]; 675 | } 676 | return self; 677 | } 678 | 679 | - (BOOL)isHorizontal 680 | { 681 | return YES; 682 | } 683 | 684 | - (NSLayoutAttribute)majorLeadingAttribute 685 | { 686 | return NSLayoutAttributeLeading; 687 | } 688 | 689 | - (NSLayoutAttribute)majorTrailingAttribute 690 | { 691 | return NSLayoutAttributeTrailing; 692 | } 693 | 694 | - (NSLayoutAttribute)majorLeadingMarginAttribute 695 | { 696 | return NSLayoutAttributeLeadingMargin; 697 | } 698 | 699 | - (NSLayoutAttribute)majorTrailingMarginAttribute 700 | { 701 | return NSLayoutAttributeTrailingMargin; 702 | } 703 | 704 | - (NSLayoutAttribute)minorLeadingAttribute 705 | { 706 | return NSLayoutAttributeTop; 707 | } 708 | 709 | - (NSLayoutAttribute)minorTrailingAttribute 710 | { 711 | return NSLayoutAttributeBottom; 712 | } 713 | 714 | - (NSLayoutAttribute)minorLeadingMarginAttribute 715 | { 716 | return NSLayoutAttributeTopMargin; 717 | } 718 | 719 | - (NSLayoutAttribute)minorTrailingMarginAttribute 720 | { 721 | return NSLayoutAttributeBottomMargin; 722 | } 723 | 724 | - (NSLayoutAttribute)majorSizeAttribute 725 | { 726 | return NSLayoutAttributeWidth; 727 | } 728 | 729 | - (NSLayoutAttribute)majorCenterAttribute 730 | { 731 | return NSLayoutAttributeCenterX; 732 | } 733 | 734 | - (NSLayoutAttribute)minorSizeAttribute 735 | { 736 | return NSLayoutAttributeHeight; 737 | } 738 | 739 | - (NSLayoutAttribute)minorCenterAttribute 740 | { 741 | return NSLayoutAttributeCenterY; 742 | } 743 | 744 | - (void)setLeadingMargin:(CGFloat)margin 745 | { 746 | self.majorLeadingMargin = margin; 747 | } 748 | - (CGFloat)leadingMargin 749 | { 750 | return self.majorLeadingMargin; 751 | } 752 | 753 | - (void)setTrailingMargin:(CGFloat)margin 754 | { 755 | self.majorTrailingMargin = margin; 756 | } 757 | - (CGFloat)trailingMargin 758 | { 759 | return self.majorTrailingMargin; 760 | } 761 | - (void)setHorizontalMargins:(CGFloat)margin 762 | { 763 | self.majorTrailingMargin = margin; 764 | self.majorLeadingMargin = margin; 765 | } 766 | - (CGFloat)horizontalMargins 767 | { 768 | return self.majorLeadingMargin; 769 | } 770 | 771 | - (void)setTopMargin:(CGFloat)margin 772 | { 773 | self.minorLeadingMargin = margin; 774 | } 775 | - (CGFloat)topMargin 776 | { 777 | return self.minorLeadingMargin; 778 | } 779 | 780 | - (void)setBottomMargin:(CGFloat)margin 781 | { 782 | self.minorTrailingMargin = margin; 783 | } 784 | - (CGFloat)bottomMargin 785 | { 786 | return self.minorTrailingMargin; 787 | } 788 | - (void)setVerticalMargins:(CGFloat)margin 789 | { 790 | self.minorTrailingMargin = margin; 791 | self.minorLeadingMargin = margin; 792 | } 793 | - (CGFloat)verticalMargins 794 | { 795 | return self.minorLeadingMargin; 796 | } 797 | 798 | - (void)setCustomTopMargin:(CGFloat)topMargin forView:(UIView *)view 799 | { 800 | [self setCustomMinorLeadingMargin:topMargin forView:view]; 801 | } 802 | 803 | - (void)setCustomBottomMargin:(CGFloat)bottomMargin forView:(UIView *)view 804 | { 805 | [self setCustomMinorTrailingMargin:bottomMargin forView:view]; 806 | } 807 | 808 | - (void)setCustomVerticalMargins:(CGFloat)verticalMargins forView:(UIView *)view 809 | { 810 | [self setCustomMinorLeadingMargin:verticalMargins forView:view]; 811 | [self setCustomMinorTrailingMargin:verticalMargins forView:view]; 812 | } 813 | 814 | - (void)setHorizontalAlignment:(SLAlignment)alignment 815 | { 816 | self.majorAlignment = alignment; 817 | } 818 | - (SLAlignment)horizontalAlignment 819 | { 820 | return self.majorAlignment; 821 | } 822 | 823 | - (void)setVerticalAlignment:(SLAlignment)alignment 824 | { 825 | self.minorAlignment = alignment; 826 | } 827 | - (SLAlignment)verticalAlignment 828 | { 829 | return self.minorAlignment; 830 | } 831 | 832 | 833 | @end 834 | 835 | 836 | @implementation SLVerticalStackLayout 837 | 838 | - (instancetype)initWithViews:(NSArray *)subviews inSuperview:(UIView *)superview configurationBlock:(void (NS_NOESCAPE ^__nullable)(SLVerticalStackLayout *))configurationBlock 839 | { 840 | self = [super initWithViews:subviews inSuperview:superview]; 841 | if (self) { 842 | if (configurationBlock) { 843 | configurationBlock(self); 844 | } 845 | [self buildInitialConstraints]; 846 | } 847 | return self; 848 | } 849 | 850 | - (BOOL)isHorizontal 851 | { 852 | return NO; 853 | } 854 | 855 | - (NSLayoutAttribute)majorLeadingAttribute 856 | { 857 | return NSLayoutAttributeTop; 858 | } 859 | 860 | - (NSLayoutAttribute)majorTrailingAttribute 861 | { 862 | return NSLayoutAttributeBottom; 863 | } 864 | 865 | - (NSLayoutAttribute)majorLeadingMarginAttribute 866 | { 867 | return NSLayoutAttributeTopMargin; 868 | } 869 | 870 | - (NSLayoutAttribute)majorTrailingMarginAttribute 871 | { 872 | return NSLayoutAttributeBottomMargin; 873 | } 874 | 875 | - (NSLayoutAttribute)minorLeadingAttribute 876 | { 877 | return NSLayoutAttributeLeading; 878 | } 879 | 880 | - (NSLayoutAttribute)minorTrailingAttribute 881 | { 882 | return NSLayoutAttributeTrailing; 883 | } 884 | 885 | - (NSLayoutAttribute)minorLeadingMarginAttribute 886 | { 887 | return NSLayoutAttributeLeadingMargin; 888 | } 889 | 890 | - (NSLayoutAttribute)minorTrailingMarginAttribute 891 | { 892 | return NSLayoutAttributeTrailingMargin; 893 | } 894 | 895 | - (NSLayoutAttribute)majorSizeAttribute 896 | { 897 | return NSLayoutAttributeHeight; 898 | } 899 | 900 | - (NSLayoutAttribute)majorCenterAttribute 901 | { 902 | return NSLayoutAttributeCenterY; 903 | } 904 | 905 | - (NSLayoutAttribute)minorSizeAttribute 906 | { 907 | return NSLayoutAttributeWidth; 908 | } 909 | 910 | - (NSLayoutAttribute)minorCenterAttribute 911 | { 912 | return NSLayoutAttributeCenterX; 913 | } 914 | 915 | - (void)setLeadingMargin:(CGFloat)margin 916 | { 917 | self.minorLeadingMargin = margin; 918 | } 919 | - (CGFloat)leadingMargin 920 | { 921 | return self.minorLeadingMargin; 922 | } 923 | 924 | - (void)setTrailingMargin:(CGFloat)margin 925 | { 926 | self.minorTrailingMargin = margin; 927 | } 928 | - (CGFloat)trailingMargin 929 | { 930 | return self.minorTrailingMargin; 931 | } 932 | 933 | - (void)setHorizontalMargins:(CGFloat)margin 934 | { 935 | self.minorTrailingMargin = margin; 936 | self.minorLeadingMargin = margin; 937 | } 938 | - (CGFloat)horizontalMargins 939 | { 940 | return self.minorLeadingMargin; 941 | } 942 | 943 | - (void)setTopMargin:(CGFloat)margin 944 | { 945 | self.majorLeadingMargin = margin; 946 | } 947 | - (CGFloat)topMargin 948 | { 949 | return self.majorLeadingMargin; 950 | } 951 | 952 | - (void)setBottomMargin:(CGFloat)margin 953 | { 954 | self.majorTrailingMargin = margin; 955 | } 956 | - (CGFloat)bottomMargin 957 | { 958 | return self.majorTrailingMargin; 959 | } 960 | 961 | - (void)setVerticalMargins:(CGFloat)margin 962 | { 963 | self.majorTrailingMargin = margin; 964 | self.majorLeadingMargin = margin; 965 | } 966 | - (CGFloat)verticalMargins 967 | { 968 | return self.majorLeadingMargin; 969 | } 970 | 971 | - (void)setCustomLeadingMargin:(CGFloat)leadingMargin forView:(UIView *)view 972 | { 973 | [self setCustomMinorLeadingMargin:leadingMargin forView:view]; 974 | } 975 | 976 | - (void)setCustomTrailingMargin:(CGFloat)trailingMargin forView:(UIView *)view 977 | { 978 | [self setCustomMinorTrailingMargin:trailingMargin forView:view]; 979 | } 980 | 981 | - (void)setCustomHorizontalMargins:(CGFloat)horizontalMargins forView:(UIView *)view 982 | { 983 | [self setCustomMinorLeadingMargin:horizontalMargins forView:view]; 984 | [self setCustomMinorTrailingMargin:horizontalMargins forView:view]; 985 | } 986 | 987 | - (void)setHorizontalAlignment:(SLAlignment)alignment 988 | { 989 | self.minorAlignment = alignment; 990 | } 991 | - (SLAlignment)horizontalAlignment 992 | { 993 | return self.minorAlignment; 994 | } 995 | 996 | - (void)setVerticalAlignment:(SLAlignment)alignment 997 | { 998 | self.majorAlignment = alignment; 999 | } 1000 | - (SLAlignment)verticalAlignment 1001 | { 1002 | return self.majorAlignment; 1003 | } 1004 | 1005 | @end 1006 | 1007 | 1008 | NS_ASSUME_NONNULL_END 1009 | -------------------------------------------------------------------------------- /Pod/StackLayout/UIView+StackLayout.h: -------------------------------------------------------------------------------- 1 | // Copyright © 2015 Bridger Maxwell. All rights reserved. 2 | 3 | #import 4 | #import "SLStackLayout.h" 5 | 6 | NS_ASSUME_NONNULL_BEGIN 7 | 8 | @interface UIView (SLStackLayout) 9 | 10 | /* 11 | These methods add an array of views as subviews and sets translatesAutoresizingMaskIntoConstraints to false 12 | on each of them. It returns the resulting layout object. 13 | You can optionally get a reference to the layout object in the configuration block. This is a chance to 14 | change the layout parameters before any constraints are created. This is also a conveniently lexically-scoped 15 | place to modify properties on a layout object you don't need a reference to afterwards. 16 | */ 17 | 18 | - (SLHorizontalStackLayout *)addSubviewsWithHorizontalLayout:(NSArray *)subviews; 19 | - (SLHorizontalStackLayout *)addSubviewsWithHorizontalLayout:(NSArray *)subviews configurationBlock:(void (NS_NOESCAPE ^__nullable)(SLHorizontalStackLayout *layout))configurationBlock; 20 | 21 | - (SLVerticalStackLayout *)addSubviewsWithVerticalLayout:(NSArray *)subviews; 22 | - (SLVerticalStackLayout *)addSubviewsWithVerticalLayout:(NSArray *)subviews configurationBlock:(void (NS_NOESCAPE ^__nullable)(SLVerticalStackLayout *layout))configurationBlock; 23 | 24 | - (void)removeSubviewsInLayout:(SLStackLayoutBase *)layout; 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /Pod/StackLayout/UIView+StackLayout.m: -------------------------------------------------------------------------------- 1 | // Copyright © 2015 Bridger Maxwell. All rights reserved. 2 | 3 | #import 4 | 5 | #import "UIView+StackLayout.h" 6 | 7 | NS_ASSUME_NONNULL_BEGIN 8 | 9 | @implementation UIView (SLStackLayout) 10 | 11 | - (SLHorizontalStackLayout *)addSubviewsWithHorizontalLayout:(NSArray *)subviews 12 | { 13 | return [self addSubviewsWithHorizontalLayout:subviews configurationBlock:nil]; 14 | } 15 | 16 | - (SLHorizontalStackLayout *)addSubviewsWithHorizontalLayout:(NSArray *)subviews configurationBlock:(nullable void (NS_NOESCAPE ^)(SLHorizontalStackLayout *_Nonnull))configurationBlock 17 | { 18 | for (UIView *subview in subviews) { 19 | if (subview.superview) { 20 | NSLog(@"Warning: Adding subview which already had a superview. %@", subview); 21 | } 22 | subview.translatesAutoresizingMaskIntoConstraints = false; 23 | [self addSubview:subview]; 24 | } 25 | SLHorizontalStackLayout *layout = [[SLHorizontalStackLayout alloc] initWithViews:subviews inSuperview:self configurationBlock:configurationBlock]; 26 | [self installStackLayout:layout]; 27 | return layout; 28 | } 29 | 30 | - (SLVerticalStackLayout *)addSubviewsWithVerticalLayout:(NSArray *)subviews 31 | { 32 | return [self addSubviewsWithVerticalLayout:subviews configurationBlock:nil]; 33 | } 34 | 35 | - (SLVerticalStackLayout *)addSubviewsWithVerticalLayout:(NSArray *)subviews configurationBlock:(nullable void (NS_NOESCAPE ^)(SLVerticalStackLayout *_Nonnull))configurationBlock 36 | { 37 | for (UIView *subview in subviews) { 38 | if (subview.superview) { 39 | NSLog(@"Warning: Adding subview which already had a superview. %@", subview); 40 | } 41 | subview.translatesAutoresizingMaskIntoConstraints = false; 42 | [self addSubview:subview]; 43 | } 44 | SLVerticalStackLayout *layout = [[SLVerticalStackLayout alloc] initWithViews:subviews inSuperview:self configurationBlock:configurationBlock]; 45 | [self installStackLayout:layout]; 46 | return layout; 47 | } 48 | 49 | - (void)removeSubviewsInLayout:(SLStackLayoutBase *)layout 50 | { 51 | if (!layout) { 52 | return; 53 | } 54 | NSArray *layouts = [self sl_installedLayouts]; 55 | if ([layouts containsObject:layout]) { 56 | for (UIView *subview in layout.views) { 57 | [subview removeFromSuperview]; 58 | } 59 | 60 | NSMutableArray *mutableLayouts = [layouts mutableCopy]; 61 | [mutableLayouts removeObject:layout]; 62 | 63 | [self sl_setInstalledLayouts:mutableLayouts]; 64 | } 65 | } 66 | 67 | - (NSArray * _Nullable)sl_installedLayouts 68 | { 69 | return objc_getAssociatedObject(self, @selector((sl_installedLayouts))); 70 | } 71 | 72 | - (void)sl_setInstalledLayouts:(NSArray *)layouts 73 | { 74 | objc_setAssociatedObject(self, @selector(sl_installedLayouts), layouts, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 75 | } 76 | 77 | static dispatch_once_t swizzleOnceToken; 78 | - (void)installStackLayout:(SLStackLayoutBase *)stackLayout 79 | { 80 | dispatch_once(&swizzleOnceToken, ^{ 81 | // This is the first stack layout that has been installed. We are going to swizzle out the layoutSubviews 82 | // method to give the layouts a hook. 83 | Class class = [UIView class]; 84 | 85 | SEL originalSelector = @selector(layoutSubviews); 86 | SEL swizzledSelector = @selector(sl_layoutSubviews); 87 | 88 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 89 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 90 | 91 | BOOL didAddMethod = class_addMethod(class, 92 | originalSelector, 93 | method_getImplementation(swizzledMethod), 94 | method_getTypeEncoding(swizzledMethod)); 95 | 96 | if (didAddMethod) { 97 | class_replaceMethod(class, 98 | swizzledSelector, 99 | method_getImplementation(originalMethod), 100 | method_getTypeEncoding(originalMethod)); 101 | } else { 102 | method_exchangeImplementations(originalMethod, swizzledMethod); 103 | } 104 | }); 105 | 106 | NSArray *layouts = [self sl_installedLayouts] ?: [NSArray array]; 107 | layouts = [layouts arrayByAddingObject:stackLayout]; 108 | 109 | [self sl_setInstalledLayouts:layouts]; 110 | } 111 | 112 | // This is a swizzled version of layoutSubviews that allows the layout objects to get a chance to do adjustments 113 | // in layoutSubviews 114 | - (void)sl_layoutSubviews 115 | { 116 | // In this context [self sl_layoutSubviews] is like calling the original layoutSubviews method on self 117 | [self sl_layoutSubviews]; 118 | 119 | for (SLStackLayoutBase *layoutBase in [self sl_installedLayouts]) { 120 | [layoutBase subviewsLaidOut]; 121 | } 122 | } 123 | 124 | @end 125 | 126 | NS_ASSUME_NONNULL_END 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StackLayout 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/StackLayout.svg?style=flat)](http://cocoapods.org/pods/StackLayout) 4 | [![License](https://img.shields.io/cocoapods/l/StackLayout.svg?style=flat)](http://cocoapods.org/pods/StackLayout) 5 | [![Platform](https://img.shields.io/cocoapods/p/StackLayout.svg?style=flat)](http://cocoapods.org/pods/StackLayout) 6 | 7 | StackLayout builds on Auto Layout to make some of the most common layouts easier to manage. It creates the constraints that you need and allows you to edit them with semantic method names, like `setTopMargin:` or `setHorizontalAlignment:`. It is similar to UIStackView, but unlike UIStackView it is not a subclass of UIView. It is a layout manager you can use with any view. This has a few advantages: 8 | 9 | - Multiple layouts in one view. 10 | - Less wrapper views in your layout. 11 | - Compatible with iOS 8+ 12 | 13 | ## Basics 14 | 15 | Three labels stacked vertically, hugging the top, with a space between them. 16 | 17 | ``` 18 | let verticalLayout = container.addSubviewsWithVerticalLayout([headerLabel, subtitleLabel, bodyLabel]) { layout in 19 | layout.verticalAlignment = .Top 20 | layout.horizontalAlignment = .Fill 21 | } 22 | ``` 23 | 24 | The above layout takes about 10 constraints usually, which can be a hassle to manage. The layout object manages them for you and allows you to easily change them later. You don't need to keep a reference to the layout object if you don't need it though. 25 | 26 | ## More examples 27 | 28 | 29 | ![WelcomeTipLayout](https://github.com/bridger/StackLayout/blob/master/Images/WelcomeTipLayout.png?raw=true) 30 | 31 | ``` 32 | tipView.addSubviewsWithVerticalLayout([titleLabel, bodyLabel, tipsButton, laterButton]) { layout in 33 | layout.verticalAlignment = .Fill 34 | layout.verticalMargins = 46 35 | layout.topMargin = 34 36 | layout.bottomMargin = 17 37 | layout.spacing = 28 38 | layout.setSpacing(10, between: titleLabel, and: bodyLabel) 39 | } 40 | 41 | // Constrain the width of the tip view so the text will wrap. 42 | tipView.widthAnchor.constraintEqualToConstant(290).active = true 43 | ``` 44 | 45 | ------ 46 | 47 | ![ToolbarLayout](https://github.com/bridger/StackLayout/blob/master/Images/ToolbarLayout.png?raw=true) 48 | ``` 49 | toolbar.addSubviewsWithHorizontalLayout([trashButton]) { layout in 50 | layout.horizontalAlignment = .Leading 51 | layout.verticalAlignment = .Center 52 | layout.leadingMargin = 15 53 | } 54 | 55 | toolbar.addSubviewsWithHorizontalLayout([resizeButton, spotlightButton]) { layout in 56 | layout.verticalAlignment = .Center 57 | layout.horizontalAlignment = .Center 58 | } 59 | ``` 60 | 61 | ## Most used properties 62 | 63 | `spacing` 64 | 65 | `setSpacing:betweenView:andView:` 66 | 67 | `(vertical)(horizontal)Alignment` 68 | 69 | `(vertical)(horizontal)Margins` 70 | 71 | `(top)(bottom)(leading)(horizontal)Margin` 72 | 73 | 74 | ## Alignment 75 | 76 | You usually want to choose both a vertical and horizontal alignment. 77 | 78 | - **SLAlignmentFill** 79 | - Use this when you want the subviews to completely fill the container (exluding space for margins). This can also make the container shrink until it is just big enough to hold the subviews. This is the most common alignment. 80 | - **SLAlignmentTop/Bottom/Leading/Trailing** 81 | - Just as it sounds. The view(s) will be stuck to the margin of the container view. 82 | - **SLAlignmentCenter** 83 | - This pulls the views to the middle. The layout might create invisible "helper" views to accomplish this. You can control the strength of the centering constraints using `centeringPriority`. 84 | - **SLAlignmentNone** 85 | - This is the default. Without an alignment the views can be layed out anywhere within the margins of the container. This is only useful when you want to align the views relative to something else in the view hierarchy. 86 | 87 | 88 | ## Spacing 89 | 90 | All ajdacent subviews have a "space" constraint for the space between them. In the Auto Layout Visual Format Language, it looks like "[first]-space-[second]". By default, this space is set to 0 so all subviews are edge-to-edge. You can set the space between two adjacent subviews by calling `setSpacing:betweenView:andView:`. You can also adjust the spacing constraints at once by setting `spacing`, which will override any other previous `setSpacing:betweenView:andView:` calls. 91 | 92 | Spacing constraints are required by default, but can be weakened by setting `spacingPriority`. 93 | 94 | ## Rules to Remember 95 | 96 | If you are working with a layout where these rules are getting in the way, don't be afraid to just ditch Stack Layout and make the constraints yourself! [PureLayout](https://github.com/PureLayout/PureLayout) or NSLayoutAnchor (iOS 9+) make this easier. 97 | 98 | #### Set both vertical and horizontal alignments 99 | 100 | If you don't set an aligment, there are many places a subview can end up. In this layout: `container.addSubviewsWithVerticalLayout([redButton, blueButton])`, the red and blue button stack can be in the top-left or bottom-right (or anywhere in between) of the container. This can be useful if you want views to be layed out relative to another view in the hierarchy. Generally though, you want to set both `verticalAligment` and `horizontalAlignment`. 101 | 102 | #### Subviews stay within margins 103 | 104 | Subviews usually stay entirely within the bounds of the containing view, even if there is no vertical or horizontal alignment set. This is done by the "margin" constraints. You can override this by setting the margins to a negative number or reducing the priority of the margin constraints by setting `marginsPriority`. 105 | 106 | #### Subviews need sizing constraints 107 | 108 | StackLayout doesn't govern how big subviews are relative to each other. For example, this layout is ambiguous: 109 | 110 | ``` 111 | container.addSubviewsWithVerticalLayout([redView, blueView]) { layout in 112 | layout.verticalAlignment = .Fill 113 | layout.horizontalAlignment = .Fill 114 | } 115 | ``` 116 | 117 | Together, redView and blueView fill the container but you need another constraint to say how big they are relative to each other. To make them each take up half of the container you'd need this supplemental constraint: `[redView.heightAnchor constraintEqualToAnchor:blueView.heightAnchor].active = YES;` 118 | 119 | Often subviews already have their own size, either from `intrinsicContentSize` or from their own subviews and contraints. 120 | 121 | ## Example Project 122 | 123 | The example project has not been built yet. If you'd like to contribute, you can build examples in the example project. 124 | 125 | Clone the repo, and run `pod install` from the Example directory first. 126 | 127 | ## Requirements 128 | 129 | ## Installation 130 | 131 | StackLayout is available through [CocoaPods](http://cocoapods.org). To install 132 | it, simply add the following line to your Podfile: 133 | 134 | ```ruby 135 | pod "StackLayout" 136 | ``` 137 | 138 | ## Author 139 | 140 | Bridger Maxwell, bridger.maxwell@gmail.com 141 | 142 | ## License 143 | 144 | StackLayout is available under the MIT license. See the LICENSE file for more info. 145 | -------------------------------------------------------------------------------- /StackLayout.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint StackLayout.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "StackLayout" 11 | s.version = "1.0.6" 12 | s.summary = "An more flexible alternative to UIStackView." 13 | s.description = <<-DESC 14 | Create Auto Layout constraints to layout views in stack. Alternative to UIStackView which supports multiple different layouts in a single view. Also includes helpful shortcuts for creating NSLayoutConstraints. 15 | DESC 16 | 17 | s.homepage = "https://github.com/bridger/StackLayout" 18 | s.license = 'MIT' 19 | s.author = { "Bridger Maxwell" => "bridger.maxwell@gmail.com" } 20 | s.source = { :git => "https://github.com/bridger/StackLayout.git", :tag => s.version.to_s } 21 | s.social_media_url = 'https://twitter.com/bridgermax' 22 | 23 | s.platform = :ios, '8.0' 24 | s.requires_arc = true 25 | s.static_framework = true 26 | 27 | s.source_files = 'Pod/StackLayout/**/*' 28 | end 29 | -------------------------------------------------------------------------------- /StackLayout.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AEF50FC21BBC70A00013B603 /* SLStackLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = AEF50FBF1BBC70A00013B603 /* SLStackLayout.m */; settings = {ASSET_TAGS = (); }; }; 11 | AEF50FC31BBC70A00013B603 /* UIView+StackLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = AEF50FC11BBC70A00013B603 /* UIView+StackLayout.m */; settings = {ASSET_TAGS = (); }; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | AEF50FAD1BBC70340013B603 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | AEF50FAF1BBC70340013B603 /* libStackLayout.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStackLayout.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | AEF50FBE1BBC70A00013B603 /* SLStackLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLStackLayout.h; sourceTree = ""; }; 29 | AEF50FBF1BBC70A00013B603 /* SLStackLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLStackLayout.m; sourceTree = ""; }; 30 | AEF50FC01BBC70A00013B603 /* UIView+StackLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+StackLayout.h"; sourceTree = ""; }; 31 | AEF50FC11BBC70A00013B603 /* UIView+StackLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+StackLayout.m"; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | AEF50FAC1BBC70340013B603 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | AEF50FA61BBC70340013B603 = { 46 | isa = PBXGroup; 47 | children = ( 48 | AEF50FBC1BBC70A00013B603 /* StackLayout */, 49 | AEF50FB01BBC70340013B603 /* Products */, 50 | ); 51 | sourceTree = ""; 52 | }; 53 | AEF50FB01BBC70340013B603 /* Products */ = { 54 | isa = PBXGroup; 55 | children = ( 56 | AEF50FAF1BBC70340013B603 /* libStackLayout.a */, 57 | ); 58 | name = Products; 59 | sourceTree = ""; 60 | }; 61 | AEF50FBC1BBC70A00013B603 /* StackLayout */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | AEF50FBE1BBC70A00013B603 /* SLStackLayout.h */, 65 | AEF50FBF1BBC70A00013B603 /* SLStackLayout.m */, 66 | AEF50FC01BBC70A00013B603 /* UIView+StackLayout.h */, 67 | AEF50FC11BBC70A00013B603 /* UIView+StackLayout.m */, 68 | ); 69 | name = StackLayout; 70 | path = Pod/StackLayout; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | AEF50FAE1BBC70340013B603 /* StackLayout */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = AEF50FB81BBC70340013B603 /* Build configuration list for PBXNativeTarget "StackLayout" */; 79 | buildPhases = ( 80 | AEF50FAB1BBC70340013B603 /* Sources */, 81 | AEF50FAC1BBC70340013B603 /* Frameworks */, 82 | AEF50FAD1BBC70340013B603 /* CopyFiles */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = StackLayout; 89 | productName = StackLayout; 90 | productReference = AEF50FAF1BBC70340013B603 /* libStackLayout.a */; 91 | productType = "com.apple.product-type.library.static"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | AEF50FA71BBC70340013B603 /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastUpgradeCheck = 0700; 100 | ORGANIZATIONNAME = "Bridger Maxwell"; 101 | TargetAttributes = { 102 | AEF50FAE1BBC70340013B603 = { 103 | CreatedOnToolsVersion = 7.0; 104 | }; 105 | }; 106 | }; 107 | buildConfigurationList = AEF50FAA1BBC70340013B603 /* Build configuration list for PBXProject "StackLayout" */; 108 | compatibilityVersion = "Xcode 3.2"; 109 | developmentRegion = English; 110 | hasScannedForEncodings = 0; 111 | knownRegions = ( 112 | en, 113 | ); 114 | mainGroup = AEF50FA61BBC70340013B603; 115 | productRefGroup = AEF50FB01BBC70340013B603 /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | AEF50FAE1BBC70340013B603 /* StackLayout */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXSourcesBuildPhase section */ 125 | AEF50FAB1BBC70340013B603 /* Sources */ = { 126 | isa = PBXSourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | AEF50FC21BBC70A00013B603 /* SLStackLayout.m in Sources */, 130 | AEF50FC31BBC70A00013B603 /* UIView+StackLayout.m in Sources */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXSourcesBuildPhase section */ 135 | 136 | /* Begin XCBuildConfiguration section */ 137 | AED3F1861BC33F0B008BA886 /* Preview */ = { 138 | isa = XCBuildConfiguration; 139 | buildSettings = { 140 | ALWAYS_SEARCH_USER_PATHS = NO; 141 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 142 | CLANG_CXX_LIBRARY = "libc++"; 143 | CLANG_ENABLE_MODULES = YES; 144 | CLANG_ENABLE_OBJC_ARC = YES; 145 | CLANG_WARN_BOOL_CONVERSION = YES; 146 | CLANG_WARN_CONSTANT_CONVERSION = YES; 147 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 148 | CLANG_WARN_EMPTY_BODY = YES; 149 | CLANG_WARN_ENUM_CONVERSION = YES; 150 | CLANG_WARN_INT_CONVERSION = YES; 151 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 152 | CLANG_WARN_UNREACHABLE_CODE = YES; 153 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 154 | COPY_PHASE_STRIP = NO; 155 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 156 | ENABLE_NS_ASSERTIONS = NO; 157 | ENABLE_STRICT_OBJC_MSGSEND = YES; 158 | GCC_C_LANGUAGE_STANDARD = gnu99; 159 | GCC_NO_COMMON_BLOCKS = YES; 160 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 161 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 162 | GCC_WARN_UNDECLARED_SELECTOR = YES; 163 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 164 | GCC_WARN_UNUSED_FUNCTION = YES; 165 | GCC_WARN_UNUSED_VARIABLE = YES; 166 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 167 | MTL_ENABLE_DEBUG_INFO = NO; 168 | SDKROOT = iphoneos; 169 | VALIDATE_PRODUCT = YES; 170 | }; 171 | name = Preview; 172 | }; 173 | AED3F1871BC33F0B008BA886 /* Preview */ = { 174 | isa = XCBuildConfiguration; 175 | buildSettings = { 176 | OTHER_LDFLAGS = "-ObjC"; 177 | PRODUCT_NAME = "$(TARGET_NAME)"; 178 | SKIP_INSTALL = YES; 179 | }; 180 | name = Preview; 181 | }; 182 | AED3F1881BC33F10008BA886 /* Production */ = { 183 | isa = XCBuildConfiguration; 184 | buildSettings = { 185 | ALWAYS_SEARCH_USER_PATHS = NO; 186 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 187 | CLANG_CXX_LIBRARY = "libc++"; 188 | CLANG_ENABLE_MODULES = YES; 189 | CLANG_ENABLE_OBJC_ARC = YES; 190 | CLANG_WARN_BOOL_CONVERSION = YES; 191 | CLANG_WARN_CONSTANT_CONVERSION = YES; 192 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 193 | CLANG_WARN_EMPTY_BODY = YES; 194 | CLANG_WARN_ENUM_CONVERSION = YES; 195 | CLANG_WARN_INT_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_UNREACHABLE_CODE = YES; 198 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 199 | COPY_PHASE_STRIP = NO; 200 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 201 | ENABLE_NS_ASSERTIONS = NO; 202 | ENABLE_STRICT_OBJC_MSGSEND = YES; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 212 | MTL_ENABLE_DEBUG_INFO = NO; 213 | SDKROOT = iphoneos; 214 | VALIDATE_PRODUCT = YES; 215 | }; 216 | name = Production; 217 | }; 218 | AED3F1891BC33F10008BA886 /* Production */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | OTHER_LDFLAGS = "-ObjC"; 222 | PRODUCT_NAME = "$(TARGET_NAME)"; 223 | SKIP_INSTALL = YES; 224 | }; 225 | name = Production; 226 | }; 227 | AEF50FB61BBC70340013B603 /* Debug */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | ALWAYS_SEARCH_USER_PATHS = NO; 231 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 232 | CLANG_CXX_LIBRARY = "libc++"; 233 | CLANG_ENABLE_MODULES = YES; 234 | CLANG_ENABLE_OBJC_ARC = YES; 235 | CLANG_WARN_BOOL_CONVERSION = YES; 236 | CLANG_WARN_CONSTANT_CONVERSION = YES; 237 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 238 | CLANG_WARN_EMPTY_BODY = YES; 239 | CLANG_WARN_ENUM_CONVERSION = YES; 240 | CLANG_WARN_INT_CONVERSION = YES; 241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 242 | CLANG_WARN_UNREACHABLE_CODE = YES; 243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 244 | COPY_PHASE_STRIP = NO; 245 | DEBUG_INFORMATION_FORMAT = dwarf; 246 | ENABLE_STRICT_OBJC_MSGSEND = YES; 247 | ENABLE_TESTABILITY = YES; 248 | GCC_C_LANGUAGE_STANDARD = gnu99; 249 | GCC_DYNAMIC_NO_PIC = NO; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_OPTIMIZATION_LEVEL = 0; 252 | GCC_PREPROCESSOR_DEFINITIONS = ( 253 | "DEBUG=1", 254 | "$(inherited)", 255 | ); 256 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 257 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 258 | GCC_WARN_UNDECLARED_SELECTOR = YES; 259 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 260 | GCC_WARN_UNUSED_FUNCTION = YES; 261 | GCC_WARN_UNUSED_VARIABLE = YES; 262 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 263 | MTL_ENABLE_DEBUG_INFO = YES; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = iphoneos; 266 | }; 267 | name = Debug; 268 | }; 269 | AEF50FB71BBC70340013B603 /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ALWAYS_SEARCH_USER_PATHS = NO; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_WARN_BOOL_CONVERSION = YES; 278 | CLANG_WARN_CONSTANT_CONVERSION = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_EMPTY_BODY = YES; 281 | CLANG_WARN_ENUM_CONVERSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 284 | CLANG_WARN_UNREACHABLE_CODE = YES; 285 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 286 | COPY_PHASE_STRIP = NO; 287 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 288 | ENABLE_NS_ASSERTIONS = NO; 289 | ENABLE_STRICT_OBJC_MSGSEND = YES; 290 | GCC_C_LANGUAGE_STANDARD = gnu99; 291 | GCC_NO_COMMON_BLOCKS = YES; 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 299 | MTL_ENABLE_DEBUG_INFO = NO; 300 | SDKROOT = iphoneos; 301 | VALIDATE_PRODUCT = YES; 302 | }; 303 | name = Release; 304 | }; 305 | AEF50FB91BBC70340013B603 /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | OTHER_LDFLAGS = "-ObjC"; 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | SKIP_INSTALL = YES; 311 | }; 312 | name = Debug; 313 | }; 314 | AEF50FBA1BBC70340013B603 /* Release */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | OTHER_LDFLAGS = "-ObjC"; 318 | PRODUCT_NAME = "$(TARGET_NAME)"; 319 | SKIP_INSTALL = YES; 320 | }; 321 | name = Release; 322 | }; 323 | /* End XCBuildConfiguration section */ 324 | 325 | /* Begin XCConfigurationList section */ 326 | AEF50FAA1BBC70340013B603 /* Build configuration list for PBXProject "StackLayout" */ = { 327 | isa = XCConfigurationList; 328 | buildConfigurations = ( 329 | AEF50FB61BBC70340013B603 /* Debug */, 330 | AEF50FB71BBC70340013B603 /* Release */, 331 | AED3F1881BC33F10008BA886 /* Production */, 332 | AED3F1861BC33F0B008BA886 /* Preview */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | AEF50FB81BBC70340013B603 /* Build configuration list for PBXNativeTarget "StackLayout" */ = { 338 | isa = XCConfigurationList; 339 | buildConfigurations = ( 340 | AEF50FB91BBC70340013B603 /* Debug */, 341 | AEF50FBA1BBC70340013B603 /* Release */, 342 | AED3F1891BC33F10008BA886 /* Production */, 343 | AED3F1871BC33F0B008BA886 /* Preview */, 344 | ); 345 | defaultConfigurationIsVisible = 0; 346 | defaultConfigurationName = Release; 347 | }; 348 | /* End XCConfigurationList section */ 349 | }; 350 | rootObject = AEF50FA71BBC70340013B603 /* Project object */; 351 | } 352 | -------------------------------------------------------------------------------- /StackLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------