├── .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 | 
2 | 
3 | 
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 |
--------------------------------------------------------------------------------