├── .gitignore ├── Cartfile.private ├── Cartfile.resolved ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist └── ViewController.swift ├── Images └── iPhoneX-Layoutable.jpg ├── LICENSE.md ├── Layoutable.podspec ├── Layoutable.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── Example.xcscheme │ ├── Layoutable.xcscheme │ └── LayoutableTests.xcscheme ├── Layoutable ├── Info.plist └── UIView+Layoutable.swift ├── LayoutableTests ├── Info.plist └── Specs │ └── LayoutableSpec.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Trashes 3 | .localized 4 | 5 | build 6 | .build 7 | xcuserdata 8 | DerivedData 9 | 10 | *.mode1v3 11 | *.mode2v3 12 | *.perspectivev3 13 | *.pbxuser 14 | *.xccheckout 15 | *.xcuserstate 16 | *.xcscmblueprint 17 | *.moved-aside 18 | *.hmap 19 | *.o 20 | *.hmap 21 | *.ipa 22 | *.dSYM.zip 23 | 24 | timeline.xctimeline 25 | playground.xcworkspace 26 | 27 | .idea 28 | *.iml 29 | 30 | Pods 31 | Carthage 32 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" ~> 1.3.1 2 | github "Quick/Nimble" ~> 7.3.0 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v7.3.0" 2 | github "Quick/Quick" "v1.3.1" 3 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Copyright © 2018 kwiecien.co. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | final class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds) 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 16 | window?.rootViewController = ViewController() 17 | window?.makeKeyAndVisible() 18 | return true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.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 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Example 4 | // 5 | // Copyright © 2018 kwiecien.co. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | import Layoutable 10 | 11 | final class ViewController: UIViewController { 12 | 13 | private let headerView: UIView = { 14 | let view = UIView() 15 | view.backgroundColor = .white 16 | return view.layoutable() 17 | }() 18 | 19 | private let headerCenterView: UIView = { 20 | let view = UIView() 21 | view.backgroundColor = .red 22 | return view.layoutable() 23 | }() 24 | 25 | private let contentContainerView: UIView = { 26 | let view = UIView() 27 | view.backgroundColor = .lightGray 28 | return view.layoutable() 29 | }() 30 | 31 | private let contentView: UIView = { 32 | let view = UIView() 33 | view.backgroundColor = .gray 34 | return view.layoutable() 35 | }() 36 | 37 | override func loadView() { 38 | view = UIView() 39 | view.addSubview(headerView) 40 | view.addSubview(contentContainerView) 41 | headerView.addSubview(headerCenterView) 42 | contentContainerView.addSubview(contentView) 43 | 44 | headerView.constrainToSuperviewEdges(excluding: [.bottom]) 45 | headerView.heightAnchor.constraint(equalToConstant: 200).isActive = true 46 | 47 | headerCenterView.constrainToConstant(size: .init(width: 80, height: 80)) 48 | headerCenterView.constrainCenterToSuperview() 49 | 50 | contentContainerView.constrainToSuperviewEdges(excluding: [.top]) 51 | contentContainerView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true 52 | 53 | contentView.constrainToSuperviewEdges(insets: .init(top: 80, left: 20, bottom: 20, right: 20)) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Images/iPhoneX-Layoutable.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MichalTKwiecien/Layoutable/0809773f52642c5272e546f4a05fe403705b752d/Images/iPhoneX-Layoutable.jpg -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michał Kwiecień 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Layoutable.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | 3 | spec.name = 'Layoutable' 4 | spec.version = '1.1.0' 5 | spec.summary = 'Extension for UIView apps that make use of Auto Layout easier.' 6 | spec.homepage = 'https://github.com/MichalTKwiecien/Layoutable' 7 | 8 | spec.license = { type: 'MIT', file: 'LICENSE.md' } 9 | spec.authors = { 'Michał Kwiecień' => 'michal@kwiecien.co' } 10 | spec.source = { git: 'https://github.com/MichalTKwiecien/Layoutable.git', tag: spec.version.to_s } 11 | 12 | spec.source_files = 'Layoutable/**/*.swift' 13 | 14 | spec.requires_arc = true 15 | spec.frameworks = 'UIKit' 16 | 17 | spec.swift_version = '4.1' 18 | spec.ios.deployment_target = '9.0' 19 | 20 | end 21 | -------------------------------------------------------------------------------- /Layoutable.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7C081444214060D900CF401C /* UIView+Layoutable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C081443214060D900CF401C /* UIView+Layoutable.swift */; }; 11 | 7C08144C21406B6100CF401C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C08144B21406B6100CF401C /* AppDelegate.swift */; }; 12 | 7C08144E21406B6100CF401C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C08144D21406B6100CF401C /* ViewController.swift */; }; 13 | 7C08145321406B6200CF401C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7C08145221406B6200CF401C /* Assets.xcassets */; }; 14 | 7C08145621406B6200CF401C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7C08145421406B6200CF401C /* LaunchScreen.storyboard */; }; 15 | 7C08145C214072EE00CF401C /* Layoutable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C0814382140605700CF401C /* Layoutable.framework */; }; 16 | 7C2869392146CC570008885A /* LayoutableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C2869382146CC570008885A /* LayoutableSpec.swift */; }; 17 | 7C28693C2146CD450008885A /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C28693A2146CD450008885A /* Nimble.framework */; }; 18 | 7C28693D2146CD450008885A /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C28693B2146CD450008885A /* Quick.framework */; }; 19 | 7C492D032146C658004BC9E5 /* Layoutable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C0814382140605700CF401C /* Layoutable.framework */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 7C492D042146C658004BC9E5 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 7C08142F2140605700CF401C /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 7C0814372140605700CF401C; 28 | remoteInfo = Layoutable; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 7C0814382140605700CF401C /* Layoutable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Layoutable.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 7C08143C2140605800CF401C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 7C081443214060D900CF401C /* UIView+Layoutable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Layoutable.swift"; sourceTree = ""; }; 36 | 7C08144921406B6100CF401C /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 7C08144B21406B6100CF401C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | 7C08144D21406B6100CF401C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 39 | 7C08145221406B6200CF401C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40 | 7C08145521406B6200CF401C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 41 | 7C08145721406B6200CF401C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | 7C2869322146C7920008885A /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; }; 43 | 7C2869332146C7920008885A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 44 | 7C2869342146C7920008885A /* Layoutable.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Layoutable.podspec; sourceTree = ""; }; 45 | 7C2869352146CA6F0008885A /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; 46 | 7C2869382146CC570008885A /* LayoutableSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutableSpec.swift; sourceTree = ""; }; 47 | 7C28693A2146CD450008885A /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; 48 | 7C28693B2146CD450008885A /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; 49 | 7C492CFE2146C658004BC9E5 /* LayoutableTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LayoutableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 7C492D022146C658004BC9E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 7C492D092146C6E8004BC9E5 /* Cartfile.private */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.private; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 7C0814342140605700CF401C /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | 7C08144621406B6100CF401C /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 7C08145C214072EE00CF401C /* Layoutable.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | 7C492CFB2146C658004BC9E5 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | 7C492D032146C658004BC9E5 /* Layoutable.framework in Frameworks */, 75 | 7C28693C2146CD450008885A /* Nimble.framework in Frameworks */, 76 | 7C28693D2146CD450008885A /* Quick.framework in Frameworks */, 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | 7C08142E2140605700CF401C = { 84 | isa = PBXGroup; 85 | children = ( 86 | 7C492D0A2146C6EF004BC9E5 /* Supporting Files */, 87 | 7C08143A2140605700CF401C /* Layoutable */, 88 | 7C08144A21406B6100CF401C /* Example */, 89 | 7C492CFF2146C658004BC9E5 /* LayoutableTests */, 90 | 7C0814392140605700CF401C /* Products */, 91 | 7C08145B214072EE00CF401C /* Frameworks */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 7C0814392140605700CF401C /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 7C0814382140605700CF401C /* Layoutable.framework */, 99 | 7C08144921406B6100CF401C /* Example.app */, 100 | 7C492CFE2146C658004BC9E5 /* LayoutableTests.xctest */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 7C08143A2140605700CF401C /* Layoutable */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 7C081443214060D900CF401C /* UIView+Layoutable.swift */, 109 | 7C08143C2140605800CF401C /* Info.plist */, 110 | ); 111 | path = Layoutable; 112 | sourceTree = ""; 113 | }; 114 | 7C08144A21406B6100CF401C /* Example */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 7C08144B21406B6100CF401C /* AppDelegate.swift */, 118 | 7C08144D21406B6100CF401C /* ViewController.swift */, 119 | 7C08145221406B6200CF401C /* Assets.xcassets */, 120 | 7C08145421406B6200CF401C /* LaunchScreen.storyboard */, 121 | 7C08145721406B6200CF401C /* Info.plist */, 122 | ); 123 | path = Example; 124 | sourceTree = ""; 125 | }; 126 | 7C08145B214072EE00CF401C /* Frameworks */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 7C28693A2146CD450008885A /* Nimble.framework */, 130 | 7C28693B2146CD450008885A /* Quick.framework */, 131 | ); 132 | name = Frameworks; 133 | sourceTree = ""; 134 | }; 135 | 7C2869372146CC060008885A /* Specs */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 7C2869382146CC570008885A /* LayoutableSpec.swift */, 139 | ); 140 | path = Specs; 141 | sourceTree = ""; 142 | }; 143 | 7C492CFF2146C658004BC9E5 /* LayoutableTests */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 7C2869372146CC060008885A /* Specs */, 147 | 7C492D022146C658004BC9E5 /* Info.plist */, 148 | ); 149 | path = LayoutableTests; 150 | sourceTree = ""; 151 | }; 152 | 7C492D0A2146C6EF004BC9E5 /* Supporting Files */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 7C2869352146CA6F0008885A /* .gitignore */, 156 | 7C492D092146C6E8004BC9E5 /* Cartfile.private */, 157 | 7C2869342146C7920008885A /* Layoutable.podspec */, 158 | 7C2869322146C7920008885A /* LICENSE.md */, 159 | 7C2869332146C7920008885A /* README.md */, 160 | ); 161 | name = "Supporting Files"; 162 | sourceTree = ""; 163 | }; 164 | /* End PBXGroup section */ 165 | 166 | /* Begin PBXHeadersBuildPhase section */ 167 | 7C0814352140605700CF401C /* Headers */ = { 168 | isa = PBXHeadersBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | ); 172 | runOnlyForDeploymentPostprocessing = 0; 173 | }; 174 | /* End PBXHeadersBuildPhase section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 7C0814372140605700CF401C /* Layoutable */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 7C0814402140605800CF401C /* Build configuration list for PBXNativeTarget "Layoutable" */; 180 | buildPhases = ( 181 | 7C0814332140605700CF401C /* Sources */, 182 | 7C0814342140605700CF401C /* Frameworks */, 183 | 7C0814352140605700CF401C /* Headers */, 184 | 7C0814362140605700CF401C /* Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = Layoutable; 191 | productName = Layoutable; 192 | productReference = 7C0814382140605700CF401C /* Layoutable.framework */; 193 | productType = "com.apple.product-type.framework"; 194 | }; 195 | 7C08144821406B6100CF401C /* Example */ = { 196 | isa = PBXNativeTarget; 197 | buildConfigurationList = 7C08145A21406B6200CF401C /* Build configuration list for PBXNativeTarget "Example" */; 198 | buildPhases = ( 199 | 7C08144521406B6100CF401C /* Sources */, 200 | 7C08144621406B6100CF401C /* Frameworks */, 201 | 7C08144721406B6100CF401C /* Resources */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | ); 207 | name = Example; 208 | productName = Example; 209 | productReference = 7C08144921406B6100CF401C /* Example.app */; 210 | productType = "com.apple.product-type.application"; 211 | }; 212 | 7C492CFD2146C658004BC9E5 /* LayoutableTests */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = 7C492D082146C658004BC9E5 /* Build configuration list for PBXNativeTarget "LayoutableTests" */; 215 | buildPhases = ( 216 | 7C492CFA2146C658004BC9E5 /* Sources */, 217 | 7C492CFB2146C658004BC9E5 /* Frameworks */, 218 | 7C492CFC2146C658004BC9E5 /* Resources */, 219 | 7C2869362146CAFF0008885A /* ShellScript */, 220 | ); 221 | buildRules = ( 222 | ); 223 | dependencies = ( 224 | 7C492D052146C658004BC9E5 /* PBXTargetDependency */, 225 | ); 226 | name = LayoutableTests; 227 | productName = LayoutableTests; 228 | productReference = 7C492CFE2146C658004BC9E5 /* LayoutableTests.xctest */; 229 | productType = "com.apple.product-type.bundle.unit-test"; 230 | }; 231 | /* End PBXNativeTarget section */ 232 | 233 | /* Begin PBXProject section */ 234 | 7C08142F2140605700CF401C /* Project object */ = { 235 | isa = PBXProject; 236 | attributes = { 237 | LastSwiftUpdateCheck = 0940; 238 | LastUpgradeCheck = 0940; 239 | ORGANIZATIONNAME = kwiecien.co; 240 | TargetAttributes = { 241 | 7C0814372140605700CF401C = { 242 | CreatedOnToolsVersion = 9.4.1; 243 | LastSwiftMigration = 0940; 244 | }; 245 | 7C08144821406B6100CF401C = { 246 | CreatedOnToolsVersion = 9.4.1; 247 | }; 248 | 7C492CFD2146C658004BC9E5 = { 249 | CreatedOnToolsVersion = 9.4.1; 250 | LastSwiftMigration = 0940; 251 | }; 252 | }; 253 | }; 254 | buildConfigurationList = 7C0814322140605700CF401C /* Build configuration list for PBXProject "Layoutable" */; 255 | compatibilityVersion = "Xcode 9.3"; 256 | developmentRegion = en; 257 | hasScannedForEncodings = 0; 258 | knownRegions = ( 259 | en, 260 | Base, 261 | ); 262 | mainGroup = 7C08142E2140605700CF401C; 263 | productRefGroup = 7C0814392140605700CF401C /* Products */; 264 | projectDirPath = ""; 265 | projectRoot = ""; 266 | targets = ( 267 | 7C0814372140605700CF401C /* Layoutable */, 268 | 7C08144821406B6100CF401C /* Example */, 269 | 7C492CFD2146C658004BC9E5 /* LayoutableTests */, 270 | ); 271 | }; 272 | /* End PBXProject section */ 273 | 274 | /* Begin PBXResourcesBuildPhase section */ 275 | 7C0814362140605700CF401C /* Resources */ = { 276 | isa = PBXResourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 7C08144721406B6100CF401C /* Resources */ = { 283 | isa = PBXResourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 7C08145621406B6200CF401C /* LaunchScreen.storyboard in Resources */, 287 | 7C08145321406B6200CF401C /* Assets.xcassets in Resources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | 7C492CFC2146C658004BC9E5 /* Resources */ = { 292 | isa = PBXResourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXResourcesBuildPhase section */ 299 | 300 | /* Begin PBXShellScriptBuildPhase section */ 301 | 7C2869362146CAFF0008885A /* ShellScript */ = { 302 | isa = PBXShellScriptBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | ); 306 | inputPaths = ( 307 | "$(SRCROOT)/Carthage/Build/iOS/Quick.framework", 308 | "$(SRCROOT)/Carthage/Build/iOS/Nimble.framework", 309 | ); 310 | outputPaths = ( 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | shellPath = /bin/sh; 314 | shellScript = "carthage copy-frameworks"; 315 | }; 316 | /* End PBXShellScriptBuildPhase section */ 317 | 318 | /* Begin PBXSourcesBuildPhase section */ 319 | 7C0814332140605700CF401C /* Sources */ = { 320 | isa = PBXSourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 7C081444214060D900CF401C /* UIView+Layoutable.swift in Sources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | 7C08144521406B6100CF401C /* Sources */ = { 328 | isa = PBXSourcesBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | 7C08144E21406B6100CF401C /* ViewController.swift in Sources */, 332 | 7C08144C21406B6100CF401C /* AppDelegate.swift in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | 7C492CFA2146C658004BC9E5 /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 7C2869392146CC570008885A /* LayoutableSpec.swift in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXSourcesBuildPhase section */ 345 | 346 | /* Begin PBXTargetDependency section */ 347 | 7C492D052146C658004BC9E5 /* PBXTargetDependency */ = { 348 | isa = PBXTargetDependency; 349 | target = 7C0814372140605700CF401C /* Layoutable */; 350 | targetProxy = 7C492D042146C658004BC9E5 /* PBXContainerItemProxy */; 351 | }; 352 | /* End PBXTargetDependency section */ 353 | 354 | /* Begin PBXVariantGroup section */ 355 | 7C08145421406B6200CF401C /* LaunchScreen.storyboard */ = { 356 | isa = PBXVariantGroup; 357 | children = ( 358 | 7C08145521406B6200CF401C /* Base */, 359 | ); 360 | name = LaunchScreen.storyboard; 361 | sourceTree = ""; 362 | }; 363 | /* End PBXVariantGroup section */ 364 | 365 | /* Begin XCBuildConfiguration section */ 366 | 7C08143E2140605800CF401C /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ALWAYS_SEARCH_USER_PATHS = NO; 370 | CLANG_ANALYZER_NONNULL = YES; 371 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 373 | CLANG_CXX_LIBRARY = "libc++"; 374 | CLANG_ENABLE_MODULES = YES; 375 | CLANG_ENABLE_OBJC_ARC = YES; 376 | CLANG_ENABLE_OBJC_WEAK = YES; 377 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 378 | CLANG_WARN_BOOL_CONVERSION = YES; 379 | CLANG_WARN_COMMA = YES; 380 | CLANG_WARN_CONSTANT_CONVERSION = YES; 381 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 382 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 383 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 384 | CLANG_WARN_EMPTY_BODY = YES; 385 | CLANG_WARN_ENUM_CONVERSION = YES; 386 | CLANG_WARN_INFINITE_RECURSION = YES; 387 | CLANG_WARN_INT_CONVERSION = YES; 388 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 389 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 390 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 391 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 392 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 393 | CLANG_WARN_STRICT_PROTOTYPES = YES; 394 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 395 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 396 | CLANG_WARN_UNREACHABLE_CODE = YES; 397 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 398 | CODE_SIGN_IDENTITY = "iPhone Developer"; 399 | COPY_PHASE_STRIP = NO; 400 | CURRENT_PROJECT_VERSION = 1; 401 | DEBUG_INFORMATION_FORMAT = dwarf; 402 | ENABLE_STRICT_OBJC_MSGSEND = YES; 403 | ENABLE_TESTABILITY = YES; 404 | GCC_C_LANGUAGE_STANDARD = gnu11; 405 | GCC_DYNAMIC_NO_PIC = NO; 406 | GCC_NO_COMMON_BLOCKS = YES; 407 | GCC_OPTIMIZATION_LEVEL = 0; 408 | GCC_PREPROCESSOR_DEFINITIONS = ( 409 | "DEBUG=1", 410 | "$(inherited)", 411 | ); 412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 414 | GCC_WARN_UNDECLARED_SELECTOR = YES; 415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 416 | GCC_WARN_UNUSED_FUNCTION = YES; 417 | GCC_WARN_UNUSED_VARIABLE = YES; 418 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 419 | MTL_ENABLE_DEBUG_INFO = YES; 420 | ONLY_ACTIVE_ARCH = YES; 421 | SDKROOT = iphoneos; 422 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 423 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 424 | VERSIONING_SYSTEM = "apple-generic"; 425 | VERSION_INFO_PREFIX = ""; 426 | }; 427 | name = Debug; 428 | }; 429 | 7C08143F2140605800CF401C /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ALWAYS_SEARCH_USER_PATHS = NO; 433 | CLANG_ANALYZER_NONNULL = YES; 434 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 435 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 436 | CLANG_CXX_LIBRARY = "libc++"; 437 | CLANG_ENABLE_MODULES = YES; 438 | CLANG_ENABLE_OBJC_ARC = YES; 439 | CLANG_ENABLE_OBJC_WEAK = YES; 440 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 441 | CLANG_WARN_BOOL_CONVERSION = YES; 442 | CLANG_WARN_COMMA = YES; 443 | CLANG_WARN_CONSTANT_CONVERSION = YES; 444 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 445 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 446 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 447 | CLANG_WARN_EMPTY_BODY = YES; 448 | CLANG_WARN_ENUM_CONVERSION = YES; 449 | CLANG_WARN_INFINITE_RECURSION = YES; 450 | CLANG_WARN_INT_CONVERSION = YES; 451 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 452 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 453 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 454 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 455 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 456 | CLANG_WARN_STRICT_PROTOTYPES = YES; 457 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 458 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 459 | CLANG_WARN_UNREACHABLE_CODE = YES; 460 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 461 | CODE_SIGN_IDENTITY = "iPhone Developer"; 462 | COPY_PHASE_STRIP = NO; 463 | CURRENT_PROJECT_VERSION = 1; 464 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 465 | ENABLE_NS_ASSERTIONS = NO; 466 | ENABLE_STRICT_OBJC_MSGSEND = YES; 467 | GCC_C_LANGUAGE_STANDARD = gnu11; 468 | GCC_NO_COMMON_BLOCKS = YES; 469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 471 | GCC_WARN_UNDECLARED_SELECTOR = YES; 472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 473 | GCC_WARN_UNUSED_FUNCTION = YES; 474 | GCC_WARN_UNUSED_VARIABLE = YES; 475 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 476 | MTL_ENABLE_DEBUG_INFO = NO; 477 | SDKROOT = iphoneos; 478 | SWIFT_COMPILATION_MODE = wholemodule; 479 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 480 | VALIDATE_PRODUCT = YES; 481 | VERSIONING_SYSTEM = "apple-generic"; 482 | VERSION_INFO_PREFIX = ""; 483 | }; 484 | name = Release; 485 | }; 486 | 7C0814412140605800CF401C /* Debug */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | CLANG_ENABLE_MODULES = YES; 490 | CODE_SIGN_IDENTITY = ""; 491 | CODE_SIGN_STYLE = Manual; 492 | DEFINES_MODULE = YES; 493 | DEVELOPMENT_TEAM = ""; 494 | DYLIB_COMPATIBILITY_VERSION = 1; 495 | DYLIB_CURRENT_VERSION = 1; 496 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 497 | INFOPLIST_FILE = Layoutable/Info.plist; 498 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 499 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 500 | LD_RUNPATH_SEARCH_PATHS = ( 501 | "$(inherited)", 502 | "@executable_path/Frameworks", 503 | "@loader_path/Frameworks", 504 | ); 505 | PRODUCT_BUNDLE_IDENTIFIER = co.kwiecien.Layoutable; 506 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 507 | PROVISIONING_PROFILE_SPECIFIER = ""; 508 | SKIP_INSTALL = YES; 509 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 510 | SWIFT_VERSION = 4.0; 511 | TARGETED_DEVICE_FAMILY = "1,2"; 512 | }; 513 | name = Debug; 514 | }; 515 | 7C0814422140605800CF401C /* Release */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | CLANG_ENABLE_MODULES = YES; 519 | CODE_SIGN_IDENTITY = ""; 520 | CODE_SIGN_STYLE = Manual; 521 | DEFINES_MODULE = YES; 522 | DEVELOPMENT_TEAM = ""; 523 | DYLIB_COMPATIBILITY_VERSION = 1; 524 | DYLIB_CURRENT_VERSION = 1; 525 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 526 | INFOPLIST_FILE = Layoutable/Info.plist; 527 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 528 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 529 | LD_RUNPATH_SEARCH_PATHS = ( 530 | "$(inherited)", 531 | "@executable_path/Frameworks", 532 | "@loader_path/Frameworks", 533 | ); 534 | PRODUCT_BUNDLE_IDENTIFIER = co.kwiecien.Layoutable; 535 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 536 | PROVISIONING_PROFILE_SPECIFIER = ""; 537 | SKIP_INSTALL = YES; 538 | SWIFT_VERSION = 4.0; 539 | TARGETED_DEVICE_FAMILY = "1,2"; 540 | }; 541 | name = Release; 542 | }; 543 | 7C08145821406B6200CF401C /* Debug */ = { 544 | isa = XCBuildConfiguration; 545 | buildSettings = { 546 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 547 | CODE_SIGN_STYLE = Manual; 548 | DEVELOPMENT_TEAM = ""; 549 | INFOPLIST_FILE = Example/Info.plist; 550 | LD_RUNPATH_SEARCH_PATHS = ( 551 | "$(inherited)", 552 | "@executable_path/Frameworks", 553 | ); 554 | PRODUCT_BUNDLE_IDENTIFIER = co.kwiecien.layoutable.Example; 555 | PRODUCT_NAME = "$(TARGET_NAME)"; 556 | PROVISIONING_PROFILE_SPECIFIER = ""; 557 | SWIFT_VERSION = 4.0; 558 | TARGETED_DEVICE_FAMILY = "1,2"; 559 | }; 560 | name = Debug; 561 | }; 562 | 7C08145921406B6200CF401C /* Release */ = { 563 | isa = XCBuildConfiguration; 564 | buildSettings = { 565 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 566 | CODE_SIGN_STYLE = Manual; 567 | DEVELOPMENT_TEAM = ""; 568 | INFOPLIST_FILE = Example/Info.plist; 569 | LD_RUNPATH_SEARCH_PATHS = ( 570 | "$(inherited)", 571 | "@executable_path/Frameworks", 572 | ); 573 | PRODUCT_BUNDLE_IDENTIFIER = co.kwiecien.layoutable.Example; 574 | PRODUCT_NAME = "$(TARGET_NAME)"; 575 | PROVISIONING_PROFILE_SPECIFIER = ""; 576 | SWIFT_VERSION = 4.0; 577 | TARGETED_DEVICE_FAMILY = "1,2"; 578 | }; 579 | name = Release; 580 | }; 581 | 7C492D062146C658004BC9E5 /* Debug */ = { 582 | isa = XCBuildConfiguration; 583 | buildSettings = { 584 | CLANG_ENABLE_MODULES = YES; 585 | CODE_SIGN_STYLE = Manual; 586 | DEVELOPMENT_TEAM = ""; 587 | FRAMEWORK_SEARCH_PATHS = ( 588 | "$(inherited)", 589 | "$(PROJECT_DIR)/Carthage/Build/iOS", 590 | ); 591 | INFOPLIST_FILE = LayoutableTests/Info.plist; 592 | LD_RUNPATH_SEARCH_PATHS = ( 593 | "$(inherited)", 594 | "@executable_path/Frameworks", 595 | "@loader_path/Frameworks", 596 | ); 597 | PRODUCT_BUNDLE_IDENTIFIER = co.kwiecien.Layoutable.LayoutableTests; 598 | PRODUCT_NAME = "$(TARGET_NAME)"; 599 | PROVISIONING_PROFILE_SPECIFIER = ""; 600 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 601 | SWIFT_VERSION = 4.0; 602 | TARGETED_DEVICE_FAMILY = "1,2"; 603 | }; 604 | name = Debug; 605 | }; 606 | 7C492D072146C658004BC9E5 /* Release */ = { 607 | isa = XCBuildConfiguration; 608 | buildSettings = { 609 | CLANG_ENABLE_MODULES = YES; 610 | CODE_SIGN_STYLE = Manual; 611 | DEVELOPMENT_TEAM = ""; 612 | FRAMEWORK_SEARCH_PATHS = ( 613 | "$(inherited)", 614 | "$(PROJECT_DIR)/Carthage/Build/iOS", 615 | ); 616 | INFOPLIST_FILE = LayoutableTests/Info.plist; 617 | LD_RUNPATH_SEARCH_PATHS = ( 618 | "$(inherited)", 619 | "@executable_path/Frameworks", 620 | "@loader_path/Frameworks", 621 | ); 622 | PRODUCT_BUNDLE_IDENTIFIER = co.kwiecien.Layoutable.LayoutableTests; 623 | PRODUCT_NAME = "$(TARGET_NAME)"; 624 | PROVISIONING_PROFILE_SPECIFIER = ""; 625 | SWIFT_VERSION = 4.0; 626 | TARGETED_DEVICE_FAMILY = "1,2"; 627 | }; 628 | name = Release; 629 | }; 630 | /* End XCBuildConfiguration section */ 631 | 632 | /* Begin XCConfigurationList section */ 633 | 7C0814322140605700CF401C /* Build configuration list for PBXProject "Layoutable" */ = { 634 | isa = XCConfigurationList; 635 | buildConfigurations = ( 636 | 7C08143E2140605800CF401C /* Debug */, 637 | 7C08143F2140605800CF401C /* Release */, 638 | ); 639 | defaultConfigurationIsVisible = 0; 640 | defaultConfigurationName = Release; 641 | }; 642 | 7C0814402140605800CF401C /* Build configuration list for PBXNativeTarget "Layoutable" */ = { 643 | isa = XCConfigurationList; 644 | buildConfigurations = ( 645 | 7C0814412140605800CF401C /* Debug */, 646 | 7C0814422140605800CF401C /* Release */, 647 | ); 648 | defaultConfigurationIsVisible = 0; 649 | defaultConfigurationName = Release; 650 | }; 651 | 7C08145A21406B6200CF401C /* Build configuration list for PBXNativeTarget "Example" */ = { 652 | isa = XCConfigurationList; 653 | buildConfigurations = ( 654 | 7C08145821406B6200CF401C /* Debug */, 655 | 7C08145921406B6200CF401C /* Release */, 656 | ); 657 | defaultConfigurationIsVisible = 0; 658 | defaultConfigurationName = Release; 659 | }; 660 | 7C492D082146C658004BC9E5 /* Build configuration list for PBXNativeTarget "LayoutableTests" */ = { 661 | isa = XCConfigurationList; 662 | buildConfigurations = ( 663 | 7C492D062146C658004BC9E5 /* Debug */, 664 | 7C492D072146C658004BC9E5 /* Release */, 665 | ); 666 | defaultConfigurationIsVisible = 0; 667 | defaultConfigurationName = Release; 668 | }; 669 | /* End XCConfigurationList section */ 670 | }; 671 | rootObject = 7C08142F2140605700CF401C /* Project object */; 672 | } 673 | -------------------------------------------------------------------------------- /Layoutable.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Layoutable.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Layoutable.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Layoutable.xcodeproj/xcshareddata/xcschemes/Layoutable.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 39 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Layoutable.xcodeproj/xcshareddata/xcschemes/LayoutableTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Layoutable/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.1 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Layoutable/UIView+Layoutable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Layoutable.swift 3 | // Layoutable 4 | // 5 | // Copyright © 2018 kwiecien.co. All rights reserved. 6 | // 7 | 8 | import UIKit 9 | 10 | public extension UIView { 11 | 12 | /// Anchor of the view. 13 | enum Anchor { 14 | case top 15 | case trailing 16 | case bottom 17 | case leading 18 | } 19 | 20 | /// Returns view with the same type that can be used with Auto Layout. 21 | /// 22 | /// - Returns: Prepared view for Auto Layout. 23 | func layoutable() -> Self { 24 | translatesAutoresizingMaskIntoConstraints = false 25 | return self 26 | } 27 | 28 | private func assertLayoutability(_ view: UIView) { 29 | assert(!view.translatesAutoresizingMaskIntoConstraints, "You are trying to use Auto Layout with `translatesAutoresizingMaskIntoConstraints` set to true for one of your views. Doing so would lead to constraint conflicts. Use `layoutable()` method to convert the view to the layoutable form.") 30 | } 31 | 32 | /// Constrain edges of the view to its superview edges. 33 | /// 34 | /// - Parameters: 35 | /// - excludedAnchors: Anchors that shouldn't be constrainted. 36 | /// - insets: Insets to use, .zero by default. 37 | /// - Returns: Created and already activated constraints. 38 | @discardableResult func constrainToSuperviewEdges(excluding excludedAnchors: [Anchor]? = nil, insets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] { 39 | guard let superview = superview else { fatalError("Cannot constrain to nil superview") } 40 | return constrainToEdges(of: superview, excluding: excludedAnchors, insets: insets) 41 | } 42 | 43 | /// Constrain edges of the view to given view edges. 44 | /// 45 | /// - Parameters: 46 | /// - view: View to constrain edges to. 47 | /// - excludedAnchors: Anchors that shouldn't be constrainted. 48 | /// - insets: Insets to use, .zero by default. 49 | /// - Returns: Created and already activated constraints. 50 | @discardableResult func constrainToEdges(of view: UIView, excluding excludedAnchors: [Anchor]? = nil, insets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] { 51 | assertLayoutability(self) 52 | var constraints = [NSLayoutConstraint]() 53 | if let excludedAnchors = excludedAnchors { 54 | if !excludedAnchors.contains(.top) { constraints.append(topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top)) } 55 | if !excludedAnchors.contains(.trailing) { constraints.append(trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right)) } 56 | if !excludedAnchors.contains(.bottom) { constraints.append(bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom)) } 57 | if !excludedAnchors.contains(.leading) { constraints.append(leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left)) } 58 | } else { 59 | constraints = [ 60 | topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top), 61 | leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left), 62 | trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right), 63 | bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom) 64 | ] 65 | } 66 | NSLayoutConstraint.activate(constraints) 67 | return constraints 68 | } 69 | 70 | /// Constrain edges of the view to its superview layout guide. 71 | /// 72 | /// - Parameters: 73 | /// - excludedAnchors: Anchors that shouldn't be constrainted. 74 | /// - insets: Insets to use, .zero by default. 75 | /// - Returns: Created and already activated constraints. 76 | @discardableResult func constrainToSuperviewLayoutGuide(excluding excludedAnchors: [Anchor]? = nil, insets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] { 77 | guard let superview = superview else { fatalError("Cannot constrain to nil superview") } 78 | return constrainToLayoutGuide(of: superview, excluding: excludedAnchors, insets: insets) 79 | } 80 | 81 | /// Constrain edges of the view to given view layout guide. 82 | /// 83 | /// - Parameters: 84 | /// - view: View to constrain edges to. 85 | /// - excludedAnchors: Anchors that shouldn't be constrainted. 86 | /// - insets: Insets to use, .zero by default. 87 | /// - Returns: Created and already activated constraints. 88 | @discardableResult func constrainToLayoutGuide(of view: UIView, excluding excludedAnchors: [Anchor]? = nil, insets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] { 89 | assertLayoutability(self) 90 | var constraints = [NSLayoutConstraint]() 91 | if #available(iOS 11.0, *) { 92 | let layoutGuide = view.safeAreaLayoutGuide 93 | if let excludedAnchors = excludedAnchors { 94 | if !excludedAnchors.contains(.top) { constraints.append(topAnchor.constraint(equalTo: layoutGuide.topAnchor, constant: insets.top)) } 95 | if !excludedAnchors.contains(.trailing) { constraints.append(trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor, constant: -insets.right)) } 96 | if !excludedAnchors.contains(.bottom) { constraints.append(bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor, constant: -insets.bottom)) } 97 | if !excludedAnchors.contains(.leading) { constraints.append(leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor, constant: insets.left)) } 98 | } else { 99 | constraints = [ 100 | topAnchor.constraint(equalTo: layoutGuide.topAnchor, constant: insets.top), 101 | leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor, constant: insets.left), 102 | trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor, constant: -insets.right), 103 | bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor, constant: -insets.bottom) 104 | ] 105 | } 106 | NSLayoutConstraint.activate(constraints) 107 | return constraints 108 | } 109 | else { 110 | return [] 111 | } 112 | } 113 | 114 | /// Constrain center of the view to the superview center. 115 | /// 116 | /// - Parameters: 117 | /// - axis: Axis that should be constrainted. 118 | /// - constant: Constant value to use for constraining. 119 | /// - Returns: Created and already activated constraints. 120 | @discardableResult func constrainCenterToSuperview(axis: [UILayoutConstraintAxis] = [.horizontal, .vertical], constant: CGPoint = .zero) -> [NSLayoutConstraint] { 121 | guard let superview = superview else { fatalError("Cannot constrain to nil superview") } 122 | return constrainCenter(to: superview, axis: axis, withConstant: constant) 123 | } 124 | 125 | /// Constrain center of the view to the given view center. 126 | /// 127 | /// - Parameters: 128 | /// - view: View to constraint center to. 129 | /// - axis: Axis that should be constrainted. 130 | /// - constant: Constant value to use for constraining. 131 | /// - Returns: Created and already activated constraints. 132 | @discardableResult func constrainCenter(to view: UIView, axis: [UILayoutConstraintAxis] = [.horizontal, .vertical], withConstant constant: CGPoint = .zero) -> [NSLayoutConstraint] { 133 | assertLayoutability(self) 134 | var constraints = [NSLayoutConstraint]() 135 | if axis.contains(.horizontal) { constraints.append(centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: constant.x)) } 136 | if axis.contains(.vertical) { constraints.append(centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: constant.y)) } 137 | NSLayoutConstraint.activate(constraints) 138 | return constraints 139 | } 140 | 141 | /// Constraints width and height anchors to the given constant size. 142 | /// 143 | /// - Parameter size: Size to be used for creating constraints. 144 | /// - Returns: Created and already activated constraints. 145 | @discardableResult func constrainToConstant(size: CGSize) -> [NSLayoutConstraint] { 146 | assertLayoutability(self) 147 | let constraints = [ 148 | widthAnchor.constraint(equalToConstant: size.width), 149 | heightAnchor.constraint(equalToConstant: size.height) 150 | ] 151 | NSLayoutConstraint.activate(constraints) 152 | return constraints 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /LayoutableTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LayoutableTests/Specs/LayoutableSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutableSpec.swift 3 | // LayoutableTests 4 | // 5 | // Copyright © 2018 kwiecien.co. All rights reserved. 6 | // 7 | 8 | @testable import Layoutable 9 | import Quick 10 | import Nimble 11 | 12 | internal final class LayoutableSpec: QuickSpec { 13 | 14 | override func spec() { 15 | context("having superview") { 16 | let superview = UIView() 17 | 18 | describe("laoutable()") { 19 | it("disables `translatesAutoresizingMaskIntoConstraints` for the view") { 20 | let view = UIView().layoutable() 21 | expect(view.translatesAutoresizingMaskIntoConstraints) == false 22 | } 23 | } 24 | 25 | describe("`translatesAutoresizingMaskIntoConstraints` assertion") { 26 | it("crashes the app if view hasn't been prepared") { 27 | let view = UIView() 28 | superview.addSubview(view) 29 | expect { view.constrainToSuperviewEdges() }.to(throwAssertion()) 30 | } 31 | 32 | it("doesn't crash the app if view has been prepared") { 33 | let view = UIView().layoutable() 34 | superview.addSubview(view) 35 | expect { view.constrainToSuperviewEdges() }.toNot(throwAssertion()) 36 | } 37 | } 38 | 39 | describe("`constrainToSuperviewEdges()` superview assertion") { 40 | it("crashes the app if view doesn't have superview") { 41 | let view = UIView() 42 | expect { view.constrainToSuperviewEdges() }.to(throwAssertion()) 43 | } 44 | 45 | it("doesn't crash the app if view has superview") { 46 | let view = UIView().layoutable() 47 | superview.addSubview(view) 48 | expect { view.constrainToSuperviewEdges() }.toNot(throwAssertion()) 49 | } 50 | } 51 | 52 | describe("`constrainToSuperviewLayoutGuide()` superview assertion") { 53 | it("crashes the app if view doesn't have superview") { 54 | let view = UIView() 55 | expect { view.constrainToSuperviewLayoutGuide() }.to(throwAssertion()) 56 | } 57 | 58 | it("doesn't crash the app if view has superview") { 59 | let view = UIView().layoutable() 60 | superview.addSubview(view) 61 | expect { view.constrainToSuperviewLayoutGuide() }.toNot(throwAssertion()) 62 | } 63 | } 64 | 65 | describe("`constrainCenterToSuperview()` superview assertion") { 66 | it("crashes the app if view doesn't have superview") { 67 | let view = UIView() 68 | expect { view.constrainCenterToSuperview() }.to(throwAssertion()) 69 | } 70 | 71 | it("doesn't crash the app if view has superview") { 72 | let view = UIView().layoutable() 73 | superview.addSubview(view) 74 | expect { view.constrainCenterToSuperview() }.toNot(throwAssertion()) 75 | } 76 | } 77 | 78 | context("and properly prepared and added view to the superview") { 79 | let view = UIView().layoutable() 80 | superview.addSubview(view) 81 | 82 | describe("`constrainToSuperviewEdges()`") { 83 | describe("without parameters") { 84 | it("returns all constraints") { 85 | let constraints = view.constrainToSuperviewEdges() 86 | expect(constraints.count) == 4 87 | 88 | let topConstraint = constraints.first(where: { $0.firstAnchor == view.topAnchor })! 89 | expect(topConstraint.constant) == 0 90 | expect(topConstraint.relation) == .equal 91 | expect(topConstraint.secondAnchor) == superview.topAnchor 92 | expect(topConstraint.isActive) == true 93 | 94 | let trailingConstraint = constraints.first(where: { $0.firstAnchor == view.trailingAnchor })! 95 | expect(trailingConstraint.constant) == 0 96 | expect(trailingConstraint.relation) == .equal 97 | expect(trailingConstraint.secondAnchor) == superview.trailingAnchor 98 | expect(trailingConstraint.isActive) == true 99 | 100 | let bottomConstraint = constraints.first(where: { $0.firstAnchor == view.bottomAnchor })! 101 | expect(bottomConstraint.constant) == 0 102 | expect(bottomConstraint.relation) == .equal 103 | expect(bottomConstraint.secondAnchor) == superview.bottomAnchor 104 | expect(bottomConstraint.isActive) == true 105 | 106 | let leadingConstraint = constraints.first(where: { $0.firstAnchor == view.leadingAnchor })! 107 | expect(leadingConstraint.constant) == 0 108 | expect(leadingConstraint.relation) == .equal 109 | expect(leadingConstraint.secondAnchor) == superview.leadingAnchor 110 | expect(leadingConstraint.isActive) == true 111 | } 112 | } 113 | 114 | describe("excluding all edges") { 115 | it("returns empty array of constraints") { 116 | let constraints = view.constrainToSuperviewEdges(excluding: [.top, .trailing, .bottom, .leading]) 117 | expect(constraints.count) == 0 118 | } 119 | } 120 | 121 | describe("excluding top and bottom edges") { 122 | it("returns array of constraints with leading and trailing constraint") { 123 | let constraints = view.constrainToSuperviewEdges(excluding: [.top, .bottom]) 124 | expect(constraints.count) == 2 125 | expect(constraints.first(where: { $0.firstAnchor == view.trailingAnchor })!).toNot(beNil()) 126 | expect(constraints.first(where: { $0.firstAnchor == view.leadingAnchor })!).toNot(beNil()) 127 | } 128 | } 129 | 130 | describe("excluding leading and trailing edges") { 131 | it("returns array of constraints with top and bottom constraint") { 132 | let constraints = view.constrainToSuperviewEdges(excluding: [.leading, .trailing]) 133 | expect(constraints.count) == 2 134 | expect(constraints.first(where: { $0.firstAnchor == view.topAnchor })!).toNot(beNil()) 135 | expect(constraints.first(where: { $0.firstAnchor == view.bottomAnchor })!).toNot(beNil()) 136 | } 137 | } 138 | 139 | describe("with various insets") { 140 | it("returns array of constraints with properly added insets") { 141 | let constraints = view.constrainToSuperviewEdges(insets: .init(top: 1, left: 2, bottom: 3, right: 4)) 142 | expect(constraints.first(where: { $0.firstAnchor == view.topAnchor })!.constant) == 1 143 | expect(constraints.first(where: { $0.firstAnchor == view.trailingAnchor })!.constant) == -4 144 | expect(constraints.first(where: { $0.firstAnchor == view.bottomAnchor })!.constant) == -3 145 | expect(constraints.first(where: { $0.firstAnchor == view.leadingAnchor })!.constant) == 2 146 | } 147 | } 148 | } 149 | 150 | describe("`constrainToSuperviewLayoutGuide()`") { 151 | describe("without parameters") { 152 | it("returns all constraints") { 153 | let constraints = view.constrainToSuperviewLayoutGuide() 154 | expect(constraints.count) == 4 155 | 156 | let topConstraint = constraints.first(where: { $0.firstAnchor == view.topAnchor })! 157 | expect(topConstraint.constant) == 0 158 | expect(topConstraint.relation) == .equal 159 | expect(topConstraint.secondAnchor) == superview.safeAreaLayoutGuide.topAnchor 160 | expect(topConstraint.isActive) == true 161 | 162 | let trailingConstraint = constraints.first(where: { $0.firstAnchor == view.trailingAnchor })! 163 | expect(trailingConstraint.constant) == 0 164 | expect(trailingConstraint.relation) == .equal 165 | expect(trailingConstraint.secondAnchor) == superview.safeAreaLayoutGuide.trailingAnchor 166 | expect(trailingConstraint.isActive) == true 167 | 168 | let bottomConstraint = constraints.first(where: { $0.firstAnchor == view.bottomAnchor })! 169 | expect(bottomConstraint.constant) == 0 170 | expect(bottomConstraint.relation) == .equal 171 | expect(bottomConstraint.secondAnchor) == superview.safeAreaLayoutGuide.bottomAnchor 172 | expect(bottomConstraint.isActive) == true 173 | 174 | let leadingConstraint = constraints.first(where: { $0.firstAnchor == view.leadingAnchor })! 175 | expect(leadingConstraint.constant) == 0 176 | expect(leadingConstraint.relation) == .equal 177 | expect(leadingConstraint.secondAnchor) == superview.safeAreaLayoutGuide.leadingAnchor 178 | expect(leadingConstraint.isActive) == true 179 | } 180 | } 181 | 182 | describe("excluding all edges") { 183 | it("returns empty array of constraints") { 184 | let constraints = view.constrainToSuperviewLayoutGuide(excluding: [.top, .trailing, .bottom, .leading]) 185 | expect(constraints.count) == 0 186 | } 187 | } 188 | 189 | describe("excluding top and bottom edges") { 190 | it("returns array of constraints with leading and trailing constraint") { 191 | let constraints = view.constrainToSuperviewLayoutGuide(excluding: [.top, .bottom]) 192 | expect(constraints.count) == 2 193 | expect(constraints.first(where: { $0.firstAnchor == view.trailingAnchor })!).toNot(beNil()) 194 | expect(constraints.first(where: { $0.firstAnchor == view.leadingAnchor })!).toNot(beNil()) 195 | } 196 | } 197 | 198 | describe("excluding leading and trailing edges") { 199 | it("returns array of constraints with top and bottom constraint") { 200 | let constraints = view.constrainToSuperviewLayoutGuide(excluding: [.leading, .trailing]) 201 | expect(constraints.count) == 2 202 | expect(constraints.first(where: { $0.firstAnchor == view.topAnchor })!).toNot(beNil()) 203 | expect(constraints.first(where: { $0.firstAnchor == view.bottomAnchor })!).toNot(beNil()) 204 | } 205 | } 206 | 207 | describe("with various insets") { 208 | it("returns array of constraints with properly added insets") { 209 | let constraints = view.constrainToSuperviewLayoutGuide(insets: .init(top: 1, left: 2, bottom: 3, right: 4)) 210 | expect(constraints.first(where: { $0.firstAnchor == view.topAnchor })!.constant) == 1 211 | expect(constraints.first(where: { $0.firstAnchor == view.trailingAnchor })!.constant) == -4 212 | expect(constraints.first(where: { $0.firstAnchor == view.bottomAnchor })!.constant) == -3 213 | expect(constraints.first(where: { $0.firstAnchor == view.leadingAnchor })!.constant) == 2 214 | } 215 | } 216 | } 217 | 218 | describe("`constrainCenterToSuperview()`") { 219 | describe("without parameters") { 220 | it("returns all constraints") { 221 | let constraints = view.constrainCenterToSuperview() 222 | expect(constraints.count) == 2 223 | 224 | let xConstraint = constraints.first(where: { $0.firstAnchor == view.centerXAnchor })! 225 | expect(xConstraint.constant) == 0 226 | expect(xConstraint.relation) == .equal 227 | expect(xConstraint.secondAnchor) == superview.centerXAnchor 228 | expect(xConstraint.isActive) == true 229 | 230 | let yConstraint = constraints.first(where: { $0.firstAnchor == view.centerYAnchor })! 231 | expect(yConstraint.constant) == 0 232 | expect(yConstraint.relation) == .equal 233 | expect(yConstraint.secondAnchor) == superview.centerYAnchor 234 | expect(yConstraint.isActive) == true 235 | } 236 | } 237 | 238 | 239 | describe("with various insets") { 240 | it("returns array of constraints with properly added insets") { 241 | let constraints = view.constrainCenterToSuperview(constant: .init(x: 1, y: 2)) 242 | expect(constraints.first(where: { $0.firstAnchor == view.centerXAnchor })!.constant) == 1 243 | expect(constraints.first(where: { $0.firstAnchor == view.centerYAnchor })!.constant) == 2 244 | } 245 | } 246 | } 247 | 248 | describe("`constrainToConstant()`") { 249 | describe("without parameters") { 250 | it("returns all constraints") { 251 | let constraints = view.constrainToConstant(size: .zero) 252 | expect(constraints.count) == 2 253 | 254 | let xConstraint = constraints.first(where: { $0.firstAnchor == view.widthAnchor })! 255 | expect(xConstraint.constant) == 0 256 | expect(xConstraint.relation) == .equal 257 | expect(xConstraint.secondAnchor).to(beNil()) 258 | expect(xConstraint.isActive) == true 259 | 260 | let yConstraint = constraints.first(where: { $0.firstAnchor == view.heightAnchor })! 261 | expect(yConstraint.constant) == 0 262 | expect(yConstraint.relation) == .equal 263 | expect(yConstraint.secondAnchor).to(beNil()) 264 | expect(yConstraint.isActive) == true 265 | } 266 | } 267 | 268 | describe("with custom constant") { 269 | it("returns array of constraints with properly set constants") { 270 | let constraints = view.constrainToConstant(size: .init(width: 1, height: 2)) 271 | expect(constraints.first(where: { $0.firstAnchor == view.widthAnchor })!.constant) == 1 272 | expect(constraints.first(where: { $0.firstAnchor == view.heightAnchor })!.constant) == 2 273 | } 274 | } 275 | } 276 | } 277 | } 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://img.shields.io/badge/iOS-9.0%20%2B-green.svg) 2 | ![](https://img.shields.io/badge/carthage-compatible-green.svg) 3 | ![](https://img.shields.io/badge/cocoapods-compatible-green.svg) 4 | 5 | **Layoutable** is an extension for UIView apps that make use of Auto Layout easier. 6 | This framework doesn't replace any existing Auto Layout API available in UIKit. Anchor API is very good 7 | and easy to use, but sometimes require a lot of code repetition for common tasks. This framework only adds a couple of useful extensions. You can check the implementation in just [one single file](https://github.com/MichalTKwiecien/Layoutable/blob/master/Layoutable/UIView%2BLayoutable.swift). 8 | 9 | ## Usage & features 10 | 11 |

12 | 13 |

14 | 15 | Here's how the code would look like when constraining the screen above. See the [example](https://github.com/MichalTKwiecien/Layoutable/blob/master/Example/ViewController.swift) for the full version. 16 | 17 | ### Just UIKit 18 | 19 | ```swift 20 | import UIKit 21 | 22 | final class ViewController: UIViewController { 23 | 24 | private let headerView: UIView = { 25 | let view = UIView() 26 | view.backgroundColor = .white 27 | view.translatesAutoresizingMaskIntoConstraints = false 28 | return view 29 | }() 30 | 31 | [...] 32 | 33 | override func loadView() { 34 | [...] 35 | 36 | NSLayoutConstraint.activate([ 37 | headerView.topAnchor.constraint(equalTo: view.topAnchor), 38 | headerView.leftAnchor.constraint(equalTo: view.leftAnchor), 39 | headerView.rightAnchor.constraint(equalTo: view.rightAnchor), 40 | headerView.heightAnchor.constraint(equalToConstant: 200), 41 | 42 | headerCenterView.widthAnchor.constraint(equalToConstant: 80), 43 | headerCenterView.heightAnchor.constraint(equalToConstant: 80), 44 | headerCenterView.centerXAnchor.constraint(equalTo: headerView.centerXAnchor), 45 | headerCenterView.centerYAnchor.constraint(equalTo: headerView.centerYAnchor), 46 | 47 | contentContainerView.topAnchor.constraint(equalTo: headerView.bottomAnchor), 48 | contentContainerView.leftAnchor.constraint(equalTo: view.leftAnchor), 49 | contentContainerView.rightAnchor.constraint(equalTo: view.rightAnchor), 50 | contentContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 51 | 52 | contentView.topAnchor.constraint(equalTo: contentContainerView.topAnchor, constant: 80), 53 | contentView.leftAnchor.constraint(equalTo: contentContainerView.leftAnchor, constant: 20), 54 | contentView.rightAnchor.constraint(equalTo: contentContainerView.rightAnchor, constant: -20), 55 | contentView.bottomAnchor.constraint(equalTo: contentContainerView.bottomAnchor, constant: -20), 56 | ]) 57 | } 58 | } 59 | ``` 60 | 61 | ### UIKit + Layoutable 62 | 63 | ```swift 64 | import UIKit 65 | import Layoutable 66 | 67 | final class ViewController: UIViewController { 68 | 69 | private let headerView: UIView = { 70 | let view = UIView() 71 | view.backgroundColor = .white 72 | return view.layoutable() 73 | }() 74 | 75 | [...] 76 | 77 | override func loadView() { 78 | [...] 79 | 80 | headerView.constrainToSuperviewEdges(excluding: [.bottom]) 81 | headerView.heightAnchor.constraint(equalToConstant: 200).isActive = true 82 | 83 | headerCenterView.constrainToConstant(size: .init(width: 80, height: 80)) 84 | headerCenterView.constrainCenterToSuperview() 85 | 86 | contentContainerView.constrainToSuperviewEdges(excluding: [.top]) 87 | contentContainerView.topAnchor.constraint(equalTo: headerView.bottomAnchor).isActive = true 88 | 89 | contentView.constrainToSuperviewEdges(insets: .init(top: 80, left: 20, bottom: 20, right: 20)) 90 | } 91 | } 92 | ``` 93 | 94 | ### Detecting if view can be used with Auto Layout 95 | For all of the views that you want to use with Auto Layout you have to type 96 | `translatesAutoresizingMaskIntoConstraints = false`. 97 | It's very easy to forget and have a lot of conflicts after running your app. 98 | Layoutable checks for this situation and informs you how to fix it. 99 | 100 | ## Requirements 101 | 102 | Layoutable supports **iOS 9.0 or higher**. 103 | 104 | ## Installation 105 | 106 | ### Carthage 107 | 108 | If you're using [Carthage](https://github.com/Carthage/Carthage), add the following dependency to your `Cartfile`: 109 | 110 | ```ruby 111 | github "MichalTKwiecien/Layoutable" {version} 112 | ``` 113 | 114 | ### CocoaPods 115 | 116 | If you're using [CocoaPods](http://cocoapods.org), add the following dependency to your `Podfile`: 117 | 118 | ```ruby 119 | pod 'Layoutable', '~> {version}' 120 | ``` 121 | 122 | ### Manually 123 | 124 | You can just drag and drop [single file](https://github.com/MichalTKwiecien/Layoutable/blob/master/Layoutable/UIView%2BLayoutable.swift) to your project. 125 | 126 | ## Contributing 127 | 128 | Development requires following tools: 129 | 130 | - **[Xcode](https://developer.apple.com/xcode/) 9.4** with **Swift 4.1**, 131 | - **[Carthage](https://github.com/Carthage/Carthage) 0.3** or higher. 132 | 133 | After cloning the repository, install project's dependencies: 134 | 135 | ```sh 136 | $ carthage bootstrap --platform iOS 137 | ``` 138 | --------------------------------------------------------------------------------