├── .gitignore
├── CHANGELOG.md
├── Design
└── SwiftAutoLayoutBanner.gif
├── LICENSE.md
├── README.md
├── SwiftAutoLayout.podspec
├── SwiftAutoLayout.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── SwiftAutoLayout
├── CGFloat+SystemSpacing.swift
├── Constrainable.swift
├── ConstraintBuilder.swift
├── DistributiveConstraintBuilder.swift
├── Info.plist
├── RelationalConstraintBuilder.swift
├── SelfConstraintBuilder.swift
└── SwiftAutoLayout.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 | *.dSYM.zip
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | .build/
40 |
41 | .DS_Store
42 | *.mobileprovision
43 | *.cer
44 | *.certSigningRequest
45 | *.p12
46 | *.pem
47 | *.pkey
48 | screenshots
49 | rvm.env
50 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | ## 1.0.1
5 |
6 | ### Bug Fixes
7 | * Prevent unwanted animations when adding subviews.
8 |
9 | ## 1.0.0
10 |
11 | Initial release!
12 |
--------------------------------------------------------------------------------
/Design/SwiftAutoLayoutBanner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SwiftKickMobile/SwiftAutoLayout/5e72f4b54b1208f1fd039dcdcb4fe306e220af89/Design/SwiftAutoLayoutBanner.gif
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 SwiftKick Mobile LLC
2 |
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SwiftAutoLayout
2 |
3 | [](https://twitter.com/TimothyMoose)
4 | [](http://cocoadocs.org/docsets/SwiftAutoLayout)
5 | [](http://cocoadocs.org/docsets/SwiftAutoLayout)
6 | [](http://cocoadocs.org/docsets/SwiftAutoLayout)
7 | [](https://github.com/Carthage/Carthage)
8 |
9 |
10 |
11 |
12 |
13 | ## Overview
14 |
15 | SwiftAutoLayout helps you write AutoLayout constraints as consisely, Swiftly, and as natively as possible. Constrain `UIView` and `UILayoutGuide`s interchangeably with a familiar syntax named to match their native properties. This library purposefuly minimizes the repetitive code of defining view hierarchies and building constraints while maximizing constraint flexibility via optional parameters.
16 |
17 | SwiftAutoLayout is written to match the AutoLayout APIs as closely as possible, only wrapping types where it improves legibility and simplifies amount of written code. This means your knowledge of AutoLayout directly translates to SwiftAutoLayout, with minimal functionality introduced on top. SwiftAutoLayout does not provide any custom closures or syntaxes for defining constraints, and prefers a functional proramming angle to keep things on a minimum number of lines.
18 |
19 | ## Usage
20 |
21 | ### Constraining a View to a Parent View
22 |
23 | Start by thinking about which two views you want to affect. In this example, a label will be constrained to a `UIViewController`'s view and added to its hierarchy.
24 |
25 | ```swift
26 | // UIViewController subclass
27 | override func viewDidLoad() {
28 | super.viewDidLoad()
29 |
30 | // Create a label
31 | let label = UILabel()
32 | label.text = "SwiftAutoLayout is neato!"
33 |
34 | // Constrain its leading and centerY anchors to be equal to our view's respective anchors
35 | // Because label doesn't yet have a parent, it will become a child of our view
36 | label.constrain(to: view).leading().centerY()
37 | }
38 | ```
39 |
40 | That's it! The label is now in the hierarchy and constrained correctly.
41 |
42 | ### Building the View Hierarchy
43 |
44 | The `constrain(to:)` method performs a couple useful actions before any constraints are made. The view calling it will have its `translatesAutoResizingMasksIntoConstraints` disabled, and will become a child of the second view if it doesn't yet have a parent. This makes it easy to define your view hierarchy while building constraints at the same time.
45 |
46 | ```swift
47 | // Wrap a label in a container view that has a grey background and some internal padding.
48 | let container = UIView()
49 | container.backgroundColor = .gray
50 | container.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
51 | container.constrain(to: view).centerXY()
52 |
53 | // Constrain a label to the layout margins guide of the container. This means we
54 | // get padding for free! No need to define constants in these constraints.
55 | // Label will become a child of the layout guide's owning view.
56 | let label = UILabel()
57 | label.constrain(to: container.layoutMarginsGuide).leadingTrailingTopBottom()
58 |
59 | // Ensure the label doesn't get wider than our view within a constant.
60 | // Since label is a child of container by this point, SwiftAutoLayout doesn't set its parent.
61 | label.constrain(to: view).width(.lessThanOrEqual, constant: -60, priority: .defaultHigh)
62 |
63 | // The view hierarchy is now:
64 | // view
65 | // └ container
66 | // └ label
67 | ```
68 |
69 | ### Customizing Constraints
70 |
71 | SwiftAutoLayout makes use of optional arguments to provide clean code when the constraint criteria is an `.equal` relationship, has a constant of 0, multiplier of 1, priority of `.required` and should activate the constraint upon creation. To specify custom values, supply the appropriate method with an argument.
72 |
73 | ```swift
74 | // Simple leading padding of 16 points
75 | label.constrain(to: view).leading(constant: 16)
76 |
77 | // Simple trailing padding of 16 points.
78 | // NOTE: Constraints between trailing and bottom anchors have their items reversed
79 | // so your constants can always be positive when insetting!
80 | label.constrain(to: view).trailing(constant: 16)
81 |
82 | // Get as customized as you like!
83 | label.constrain(to: view).top(.greaterThanOrEqual, constant: 8, multiplier: 0.5, priority: .defaultLow)
84 |
85 | // In common scenarios where multiple constraints are defined together,
86 | // helper methods create multiple constraints using the supplied arguments
87 | label.constrain(to: view).leadingTrailing(constant: 16).topBottom(constant: 8)
88 |
89 | // As a bonus, this makes it super easy to pin a view to a container,
90 | // become its child, and disable its resizing mask in a single line of code
91 | label.constrain(to: view).leadingTrailingTopBottom()
92 |
93 | // And as a bonus to that bonus, if you want the label to be constrained to
94 | // the view's margins inset from the safe area, use its margins layout guide!
95 | label.constrain(to: view.layoutMarginsGuide).leadingTrailingTopBottom()
96 | ```
97 |
98 | ### Defining Different Kinds of Constraints
99 |
100 | SwiftAutoLayout has 3 main methods for creating different constraint builders suited for different tasks.
101 |
102 | - `constrain(to:)` returns a [`RelationalConstraintBuilder`](./SwiftAutoLayout/RelationalConstraintBuilder.swift) that is useful for embedding a view inside another and creating constraints that match anchors. In uncommon scenarios where you want to define a constraint between two different anchors, use this builder's `xAxis(_:to:)`, `yAxis(_:to:)`, and `dimension(_:to:)` methods.
103 |
104 | - `constrain(after:)` returns a [`DistributiveConstraintBuilder`](./SwiftAutoLayout/DistributiveConstraintBuilder.swift) that has a couple methods for placing this view vertically or horizontally after another. This builder expects its views and layout guides to already have parents.
105 |
106 | - `constrainSelf()` returns a [`SelfConstraintBuilder`](./SwiftAutoLayout/SelfConstraintBuilder.swift) which is great for constraining your view's width, height, or aspect ratio.
107 |
108 | ### Getting Constraints
109 |
110 | All three builders provide an array of their constraints in their created order.
111 |
112 | ```swift
113 | // You can grab a reference to the builder itself...
114 | let builder = label.constrain(to: view).leading().centerY()
115 | print(builder.constraints.last!) // NSLayoutConstraint between centerY anchors
116 |
117 | // ...or just access the array of constraints directly!
118 | let constraint = label.constrain(to: view).centerY(constant: 0).constraints.last!
119 |
120 | // Then use the constraint later as needed.
121 | constraint.constant = 100
122 |
123 | // Keep in mind some helper methods create multiple constraints in the order they're named.
124 | // This should be clear based on method name, and their documentation will specify constraint count.
125 | let constraints = label.constrain(to: view).leadingTrailingTopBottom().constraints
126 | print(constraints.count) // 4
127 |
128 | // You can put constraints on their own lines thanks to functional chanining. Here we
129 | // dynamically activate a constraint later, as such its priority must be lower than `.required`
130 | let constraint = label.constrainSelf()
131 | .height(constant: 0, priority: .required - 1, activate: false)
132 | .constraints.last!
133 |
134 | constraint.isActive = true // smoosh!
135 | ```
136 |
137 | ### System Spacing
138 |
139 | You can specify constraints that use system spacing for their "constant" in iOS 11 and later. This is accomplished by an extension on `CGFloat` named `.systemSpacing` — which is a special placeholder value SwiftAutoLayout will take into account when creating your constraint. This value has no use outside of SwiftAutoLayout, and does not work with the `constrainSelf()` builder.
140 |
141 | ```swift
142 | label.constrain(to: view).leadingTrailing(constant: .systemSpacing)
143 | ```
144 |
145 | ### Custom Constraints
146 |
147 | In scenarios where you want to make a custom constraint between two different anchors, use the appropriate method after `constrain(to:)`. The need for specialized methods for `T` in `NSLayoutAnchor` makes creating these custom constraints type-safe and more crash resistant.
148 |
149 | ```swift
150 | // NSLayoutXAxisAnchor: Constrain label's centerXAnchor to view's leadingAnchor
151 | label.constrain(to: view).xAxis(.centerX, to: .leading)
152 |
153 | // NSLayoutYAxisAnchor: Constrain label's centerYAnchor to view's topAnchor
154 | label.constrain(to: view).yAxis(.centerY, to: .top)
155 |
156 | // NSLayoutDimension: Constrain label's widthAnchor to view's heightAnchor
157 | label.constrain(to: view).dimension(.width, to: .height)
158 | ```
159 |
160 | ## Ideology
161 |
162 | ### Note about Left and Right Anchors
163 |
164 | SwiftAutoLayout does not use left and right anchors. This simplifies x axis anchors usage by disallowing incorrect usage (mixing left and leading) and cleans up autocomplete. [Apple states](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/AnatomyofaConstraint.html) you should use leading and trailing anchors always, and in scenarios where you want your constraints to not be affected by language direction, change your view's [`semanticContentAttribute`](https://developer.apple.com/documentation/uikit/uiview/1622461-semanticcontentattribute).
165 |
166 | > Avoid using Left and Right attributes. Use Leading and Trailing instead. This allows the layout to adapt to the view’s reading direction.
167 |
168 | > By default the reading direction is determined based on the current language set by the user. However, you can override this where necessary. In iOS, set the [`semanticContentAttribute`](https://developer.apple.com/documentation/uikit/uiview/1622461-semanticcontentattribute) property on the view holding the constraint (the nearest common ancestor of all views affected by the constraint) to specify whether the content’s layout should be flipped when switching between left-to-right and right-to-left languages.
169 |
170 | ## Tips, Tricks, and Gotchas
171 |
172 | ### Custom Parents
173 |
174 | If you have a special scenario where you want a view's parent to not be set when using `constrain(to:)`, just set its parent beforehand. SwiftAutoLayout's goal is to simplify hierarchy generation and ensure a view has a parent when constraints are created, and it will not change a hierarchy once it exists.
175 |
176 | ### Work from the Bottom Up
177 |
178 | When defining your view hierarchy, it's best to start by defining and constraining the first views to become children of your root view and constraining children views after. In general, you want to use `constrain(to:)` before you use `constrain(after:)` since the latter expects both views/layout guides to have parents. `constrainSelf()` can be called at any time, the view doesn't need a parent for a self constraint.
179 |
180 | ### Define Constraints Consistently
181 |
182 | It's easy to attempt to compartmentalize all your constraint code with your view setup code, but it is recommended to set up all of your constraints in a place where your root view has a parent. For view controllers, set up your constraints in `viewDidLoad()` or later, and avoid defining constraints in a `UIView` or `UIViewController` initializer.
183 |
184 | ### Debugging Constraint Issues
185 |
186 | As a reminder, setting a view's [`accessibilityIdentifier`](https://developer.apple.com/documentation/uikit/uiaccessibilityidentification/1623132-accessibilityidentifier) to a concise string will help you identify problem views when constraint errors are printed.
187 |
188 | ### Use Layout Guides!
189 |
190 | `UILayoutGuide`s are awesome. If you set up your views correctly and use their [`directionalLayoutMargins`](https://developer.apple.com/documentation/uikit/uiview/2865930-directionallayoutmargins) you can write elegant constraints with minimal constants. Since SwiftAutoLayout doesn't use left and right anchors, it's recommended to use [`NSDirectionalEdgeInsets`](https://developer.apple.com/documentation/uikit/nsdirectionaledgeinsets) when setting up your layout guides.
191 |
192 | You can also create new layout guides instead of views when you need to simplify view layout.
193 |
194 | ```swift
195 | // Create a layout guide that will determine a height in which some buttons
196 | // will be spread out along the x axis, and centered on the y axis
197 | let buttonsLayoutGuide = UILayoutGuide()
198 | buttonsLayoutGuide.constrain(to: view.layoutMarginsGuide).leadingTrailing().bottom()
199 | buttonsLayoutGuide.constrainSelf().height(constant: 60)
200 |
201 | let buttons = [UIButton(), UIButton(), UIButton()]
202 | buttons.forEach { $0.constrainSelf().widthHeight(constant: 40) }
203 | zip(buttons, [0.5, 1.0, 1.5]) { (button, multiplier)
204 | button.constrain(to: buttonsLayoutGuide).centerX(multiplier: multiplier).centerY()
205 | }
206 | ```
207 |
208 | ## Room for Improvement
209 |
210 | As of now, SwiftAutoLayout does not support AppKit, but is open to pull requests!
211 |
212 | SwiftAutoLayout only supports anchors that both `UIView` and `UILayoutGuide` have, so `firstBaselineAnchor` and `lastBaselineAnchor` (which only exist on `UIView`) are not yet supported. Again, pull requests are welcome!
213 |
214 | ## Installation
215 |
216 | ### CocoaPods
217 |
218 | Add the following line to your Podfile:
219 |
220 | ````ruby
221 | pod 'SwiftAutoLayout'
222 | ````
223 |
224 | ### Carthage
225 |
226 | Add the following line to your Cartfile:
227 |
228 | ````ruby
229 | github "SwiftKickMobile/SwiftAutoLayout"
230 | ````
231 |
232 | ### Manual
233 |
234 | 1. Put SwiftAutoLayout repo somewhere in your project directory.
235 | 1. In Xcode, add `SwiftAutoLayout.xcodeproj` to your project.
236 | 1. On your app's target, add the SwiftAutoLayout framework:
237 | 1. as an embedded binary on the General tab.
238 | 1. as a target dependency on the Build Phases tab.
239 |
240 | ## About SwiftKick Mobile
241 | We build high quality apps! [Get in touch](http://www.swiftkickmobile.com) if you need help with a project.
242 |
243 | ## License
244 |
245 | SwiftAutoLayout is distributed under the MIT license. [See LICENSE](./LICENSE.md) for details.
246 |
--------------------------------------------------------------------------------
/SwiftAutoLayout.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'SwiftAutoLayout'
3 | spec.version = '1.0.1'
4 | spec.license = { :type => 'MIT' }
5 | spec.homepage = 'https://github.com/SwiftKickMobile/SwiftAutoLayout'
6 | spec.authors = { 'Timothy Moose' => 'tim@swiftkick.it' }
7 | spec.summary = 'Write constraints in a concise, expressive, Swifty way.'
8 | spec.source = {:git => 'https://github.com/SwiftKickMobile/SwiftAutoLayout.git', :tag => spec.version}
9 | spec.platform = :ios, '10.0'
10 | spec.swift_version = '4.2'
11 | spec.ios.deployment_target = '10.0'
12 | spec.source_files = 'SwiftAutoLayout/**/*.swift'
13 | spec.framework = 'UIKit'
14 | spec.requires_arc = true
15 | end
16 |
--------------------------------------------------------------------------------
/SwiftAutoLayout.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 222201EB21A107C600F00B03 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 222201E821A107C600F00B03 /* README.md */; };
11 | 222201EC21A107C600F00B03 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = 222201E921A107C600F00B03 /* LICENSE.md */; };
12 | 222201ED21A107C600F00B03 /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = 222201EA21A107C600F00B03 /* CHANGELOG.md */; };
13 | 22AEE38721A1056800295C85 /* SwiftAutoLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 22AEE38521A1056800295C85 /* SwiftAutoLayout.h */; settings = {ATTRIBUTES = (Public, ); }; };
14 | 22AEE39721A106DB00295C85 /* ConstraintBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AEE38D21A106DB00295C85 /* ConstraintBuilder.swift */; };
15 | 22AEE39A21A106DB00295C85 /* DistributiveConstraintBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AEE39021A106DB00295C85 /* DistributiveConstraintBuilder.swift */; };
16 | 22AEE39C21A106DB00295C85 /* RelationalConstraintBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AEE39221A106DB00295C85 /* RelationalConstraintBuilder.swift */; };
17 | 22AEE39E21A106DB00295C85 /* Constrainable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AEE39421A106DB00295C85 /* Constrainable.swift */; };
18 | 22AEE3A021A106DB00295C85 /* SelfConstraintBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AEE39621A106DB00295C85 /* SelfConstraintBuilder.swift */; };
19 | 22AEE3A221A1078200295C85 /* SwiftAutoLayout.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 22AEE3A121A1078200295C85 /* SwiftAutoLayout.podspec */; };
20 | 61374AF721ADF52500E543BC /* CGFloat+SystemSpacing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61374AF621ADF52500E543BC /* CGFloat+SystemSpacing.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXFileReference section */
24 | 222201E821A107C600F00B03 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
25 | 222201E921A107C600F00B03 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; };
26 | 222201EA21A107C600F00B03 /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; };
27 | 22AEE38221A1056800295C85 /* SwiftAutoLayout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftAutoLayout.framework; sourceTree = BUILT_PRODUCTS_DIR; };
28 | 22AEE38521A1056800295C85 /* SwiftAutoLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftAutoLayout.h; sourceTree = ""; };
29 | 22AEE38621A1056800295C85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
30 | 22AEE38D21A106DB00295C85 /* ConstraintBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintBuilder.swift; sourceTree = ""; };
31 | 22AEE39021A106DB00295C85 /* DistributiveConstraintBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistributiveConstraintBuilder.swift; sourceTree = ""; };
32 | 22AEE39221A106DB00295C85 /* RelationalConstraintBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelationalConstraintBuilder.swift; sourceTree = ""; };
33 | 22AEE39421A106DB00295C85 /* Constrainable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constrainable.swift; sourceTree = ""; };
34 | 22AEE39621A106DB00295C85 /* SelfConstraintBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfConstraintBuilder.swift; sourceTree = ""; };
35 | 22AEE3A121A1078200295C85 /* SwiftAutoLayout.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SwiftAutoLayout.podspec; sourceTree = ""; };
36 | 61374AF621ADF52500E543BC /* CGFloat+SystemSpacing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+SystemSpacing.swift"; sourceTree = ""; };
37 | /* End PBXFileReference section */
38 |
39 | /* Begin PBXFrameworksBuildPhase section */
40 | 22AEE37F21A1056800295C85 /* Frameworks */ = {
41 | isa = PBXFrameworksBuildPhase;
42 | buildActionMask = 2147483647;
43 | files = (
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXFrameworksBuildPhase section */
48 |
49 | /* Begin PBXGroup section */
50 | 22AEE37821A1056800295C85 = {
51 | isa = PBXGroup;
52 | children = (
53 | 22AEE38421A1056800295C85 /* SwiftAutoLayout */,
54 | 22AEE38321A1056800295C85 /* Products */,
55 | 222201E821A107C600F00B03 /* README.md */,
56 | 222201EA21A107C600F00B03 /* CHANGELOG.md */,
57 | 222201E921A107C600F00B03 /* LICENSE.md */,
58 | 22AEE3A121A1078200295C85 /* SwiftAutoLayout.podspec */,
59 | );
60 | sourceTree = "";
61 | };
62 | 22AEE38321A1056800295C85 /* Products */ = {
63 | isa = PBXGroup;
64 | children = (
65 | 22AEE38221A1056800295C85 /* SwiftAutoLayout.framework */,
66 | );
67 | name = Products;
68 | sourceTree = "";
69 | };
70 | 22AEE38421A1056800295C85 /* SwiftAutoLayout */ = {
71 | isa = PBXGroup;
72 | children = (
73 | 22AEE39421A106DB00295C85 /* Constrainable.swift */,
74 | 22AEE38D21A106DB00295C85 /* ConstraintBuilder.swift */,
75 | 22AEE39021A106DB00295C85 /* DistributiveConstraintBuilder.swift */,
76 | 22AEE39221A106DB00295C85 /* RelationalConstraintBuilder.swift */,
77 | 22AEE39621A106DB00295C85 /* SelfConstraintBuilder.swift */,
78 | 61374AF621ADF52500E543BC /* CGFloat+SystemSpacing.swift */,
79 | 22AEE38521A1056800295C85 /* SwiftAutoLayout.h */,
80 | 22AEE38621A1056800295C85 /* Info.plist */,
81 | );
82 | path = SwiftAutoLayout;
83 | sourceTree = "";
84 | };
85 | /* End PBXGroup section */
86 |
87 | /* Begin PBXHeadersBuildPhase section */
88 | 22AEE37D21A1056800295C85 /* Headers */ = {
89 | isa = PBXHeadersBuildPhase;
90 | buildActionMask = 2147483647;
91 | files = (
92 | 22AEE38721A1056800295C85 /* SwiftAutoLayout.h in Headers */,
93 | );
94 | runOnlyForDeploymentPostprocessing = 0;
95 | };
96 | /* End PBXHeadersBuildPhase section */
97 |
98 | /* Begin PBXNativeTarget section */
99 | 22AEE38121A1056800295C85 /* SwiftAutoLayout */ = {
100 | isa = PBXNativeTarget;
101 | buildConfigurationList = 22AEE38A21A1056800295C85 /* Build configuration list for PBXNativeTarget "SwiftAutoLayout" */;
102 | buildPhases = (
103 | 22AEE37D21A1056800295C85 /* Headers */,
104 | 22AEE37E21A1056800295C85 /* Sources */,
105 | 22AEE37F21A1056800295C85 /* Frameworks */,
106 | 22AEE38021A1056800295C85 /* Resources */,
107 | );
108 | buildRules = (
109 | );
110 | dependencies = (
111 | );
112 | name = SwiftAutoLayout;
113 | productName = SwiftAutoLayout;
114 | productReference = 22AEE38221A1056800295C85 /* SwiftAutoLayout.framework */;
115 | productType = "com.apple.product-type.framework";
116 | };
117 | /* End PBXNativeTarget section */
118 |
119 | /* Begin PBXProject section */
120 | 22AEE37921A1056800295C85 /* Project object */ = {
121 | isa = PBXProject;
122 | attributes = {
123 | LastUpgradeCheck = 1010;
124 | ORGANIZATIONNAME = it.swiftkick;
125 | TargetAttributes = {
126 | 22AEE38121A1056800295C85 = {
127 | CreatedOnToolsVersion = 10.1;
128 | LastSwiftMigration = 1010;
129 | };
130 | };
131 | };
132 | buildConfigurationList = 22AEE37C21A1056800295C85 /* Build configuration list for PBXProject "SwiftAutoLayout" */;
133 | compatibilityVersion = "Xcode 9.3";
134 | developmentRegion = en;
135 | hasScannedForEncodings = 0;
136 | knownRegions = (
137 | en,
138 | );
139 | mainGroup = 22AEE37821A1056800295C85;
140 | productRefGroup = 22AEE38321A1056800295C85 /* Products */;
141 | projectDirPath = "";
142 | projectRoot = "";
143 | targets = (
144 | 22AEE38121A1056800295C85 /* SwiftAutoLayout */,
145 | );
146 | };
147 | /* End PBXProject section */
148 |
149 | /* Begin PBXResourcesBuildPhase section */
150 | 22AEE38021A1056800295C85 /* Resources */ = {
151 | isa = PBXResourcesBuildPhase;
152 | buildActionMask = 2147483647;
153 | files = (
154 | 222201EB21A107C600F00B03 /* README.md in Resources */,
155 | 222201EC21A107C600F00B03 /* LICENSE.md in Resources */,
156 | 22AEE3A221A1078200295C85 /* SwiftAutoLayout.podspec in Resources */,
157 | 222201ED21A107C600F00B03 /* CHANGELOG.md in Resources */,
158 | );
159 | runOnlyForDeploymentPostprocessing = 0;
160 | };
161 | /* End PBXResourcesBuildPhase section */
162 |
163 | /* Begin PBXSourcesBuildPhase section */
164 | 22AEE37E21A1056800295C85 /* Sources */ = {
165 | isa = PBXSourcesBuildPhase;
166 | buildActionMask = 2147483647;
167 | files = (
168 | 22AEE39A21A106DB00295C85 /* DistributiveConstraintBuilder.swift in Sources */,
169 | 61374AF721ADF52500E543BC /* CGFloat+SystemSpacing.swift in Sources */,
170 | 22AEE39721A106DB00295C85 /* ConstraintBuilder.swift in Sources */,
171 | 22AEE39E21A106DB00295C85 /* Constrainable.swift in Sources */,
172 | 22AEE3A021A106DB00295C85 /* SelfConstraintBuilder.swift in Sources */,
173 | 22AEE39C21A106DB00295C85 /* RelationalConstraintBuilder.swift in Sources */,
174 | );
175 | runOnlyForDeploymentPostprocessing = 0;
176 | };
177 | /* End PBXSourcesBuildPhase section */
178 |
179 | /* Begin XCBuildConfiguration section */
180 | 22AEE38821A1056800295C85 /* Debug */ = {
181 | isa = XCBuildConfiguration;
182 | buildSettings = {
183 | ALWAYS_SEARCH_USER_PATHS = NO;
184 | CLANG_ANALYZER_NONNULL = YES;
185 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
186 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
187 | CLANG_CXX_LIBRARY = "libc++";
188 | CLANG_ENABLE_MODULES = YES;
189 | CLANG_ENABLE_OBJC_ARC = YES;
190 | CLANG_ENABLE_OBJC_WEAK = YES;
191 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
192 | CLANG_WARN_BOOL_CONVERSION = YES;
193 | CLANG_WARN_COMMA = YES;
194 | CLANG_WARN_CONSTANT_CONVERSION = YES;
195 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
196 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
197 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
198 | CLANG_WARN_EMPTY_BODY = YES;
199 | CLANG_WARN_ENUM_CONVERSION = YES;
200 | CLANG_WARN_INFINITE_RECURSION = YES;
201 | CLANG_WARN_INT_CONVERSION = YES;
202 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
203 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
204 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
205 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
206 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
207 | CLANG_WARN_STRICT_PROTOTYPES = YES;
208 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
209 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
210 | CLANG_WARN_UNREACHABLE_CODE = YES;
211 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
212 | CODE_SIGN_IDENTITY = "iPhone Developer";
213 | COPY_PHASE_STRIP = NO;
214 | CURRENT_PROJECT_VERSION = 1;
215 | DEBUG_INFORMATION_FORMAT = dwarf;
216 | ENABLE_STRICT_OBJC_MSGSEND = YES;
217 | ENABLE_TESTABILITY = YES;
218 | GCC_C_LANGUAGE_STANDARD = gnu11;
219 | GCC_DYNAMIC_NO_PIC = NO;
220 | GCC_NO_COMMON_BLOCKS = YES;
221 | GCC_OPTIMIZATION_LEVEL = 0;
222 | GCC_PREPROCESSOR_DEFINITIONS = (
223 | "DEBUG=1",
224 | "$(inherited)",
225 | );
226 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
227 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
228 | GCC_WARN_UNDECLARED_SELECTOR = YES;
229 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
230 | GCC_WARN_UNUSED_FUNCTION = YES;
231 | GCC_WARN_UNUSED_VARIABLE = YES;
232 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
233 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
234 | MTL_FAST_MATH = YES;
235 | ONLY_ACTIVE_ARCH = YES;
236 | SDKROOT = iphoneos;
237 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
238 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
239 | VERSIONING_SYSTEM = "apple-generic";
240 | VERSION_INFO_PREFIX = "";
241 | };
242 | name = Debug;
243 | };
244 | 22AEE38921A1056800295C85 /* Release */ = {
245 | isa = XCBuildConfiguration;
246 | buildSettings = {
247 | ALWAYS_SEARCH_USER_PATHS = NO;
248 | CLANG_ANALYZER_NONNULL = YES;
249 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
251 | CLANG_CXX_LIBRARY = "libc++";
252 | CLANG_ENABLE_MODULES = YES;
253 | CLANG_ENABLE_OBJC_ARC = YES;
254 | CLANG_ENABLE_OBJC_WEAK = YES;
255 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
256 | CLANG_WARN_BOOL_CONVERSION = YES;
257 | CLANG_WARN_COMMA = YES;
258 | CLANG_WARN_CONSTANT_CONVERSION = YES;
259 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
261 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
262 | CLANG_WARN_EMPTY_BODY = YES;
263 | CLANG_WARN_ENUM_CONVERSION = YES;
264 | CLANG_WARN_INFINITE_RECURSION = YES;
265 | CLANG_WARN_INT_CONVERSION = YES;
266 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
267 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
268 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
270 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
271 | CLANG_WARN_STRICT_PROTOTYPES = YES;
272 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
273 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
274 | CLANG_WARN_UNREACHABLE_CODE = YES;
275 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
276 | CODE_SIGN_IDENTITY = "iPhone Developer";
277 | COPY_PHASE_STRIP = NO;
278 | CURRENT_PROJECT_VERSION = 1;
279 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
280 | ENABLE_NS_ASSERTIONS = NO;
281 | ENABLE_STRICT_OBJC_MSGSEND = YES;
282 | GCC_C_LANGUAGE_STANDARD = gnu11;
283 | GCC_NO_COMMON_BLOCKS = YES;
284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
286 | GCC_WARN_UNDECLARED_SELECTOR = YES;
287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
288 | GCC_WARN_UNUSED_FUNCTION = YES;
289 | GCC_WARN_UNUSED_VARIABLE = YES;
290 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
291 | MTL_ENABLE_DEBUG_INFO = NO;
292 | MTL_FAST_MATH = YES;
293 | SDKROOT = iphoneos;
294 | SWIFT_COMPILATION_MODE = wholemodule;
295 | SWIFT_OPTIMIZATION_LEVEL = "-O";
296 | VALIDATE_PRODUCT = YES;
297 | VERSIONING_SYSTEM = "apple-generic";
298 | VERSION_INFO_PREFIX = "";
299 | };
300 | name = Release;
301 | };
302 | 22AEE38B21A1056800295C85 /* Debug */ = {
303 | isa = XCBuildConfiguration;
304 | buildSettings = {
305 | CLANG_ENABLE_MODULES = YES;
306 | CODE_SIGN_IDENTITY = "";
307 | CODE_SIGN_STYLE = Automatic;
308 | DEFINES_MODULE = YES;
309 | DEVELOPMENT_TEAM = 38R82CD868;
310 | DYLIB_COMPATIBILITY_VERSION = 1;
311 | DYLIB_CURRENT_VERSION = 1;
312 | DYLIB_INSTALL_NAME_BASE = "@rpath";
313 | INFOPLIST_FILE = SwiftAutoLayout/Info.plist;
314 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
315 | LD_RUNPATH_SEARCH_PATHS = (
316 | "$(inherited)",
317 | "@executable_path/Frameworks",
318 | "@loader_path/Frameworks",
319 | );
320 | PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftAutoLayout;
321 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
322 | SKIP_INSTALL = YES;
323 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
324 | SWIFT_VERSION = 4.2;
325 | TARGETED_DEVICE_FAMILY = "1,2";
326 | };
327 | name = Debug;
328 | };
329 | 22AEE38C21A1056800295C85 /* Release */ = {
330 | isa = XCBuildConfiguration;
331 | buildSettings = {
332 | CLANG_ENABLE_MODULES = YES;
333 | CODE_SIGN_IDENTITY = "";
334 | CODE_SIGN_STYLE = Automatic;
335 | DEFINES_MODULE = YES;
336 | DEVELOPMENT_TEAM = 38R82CD868;
337 | DYLIB_COMPATIBILITY_VERSION = 1;
338 | DYLIB_CURRENT_VERSION = 1;
339 | DYLIB_INSTALL_NAME_BASE = "@rpath";
340 | INFOPLIST_FILE = SwiftAutoLayout/Info.plist;
341 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
342 | LD_RUNPATH_SEARCH_PATHS = (
343 | "$(inherited)",
344 | "@executable_path/Frameworks",
345 | "@loader_path/Frameworks",
346 | );
347 | PRODUCT_BUNDLE_IDENTIFIER = it.swiftkick.SwiftAutoLayout;
348 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
349 | SKIP_INSTALL = YES;
350 | SWIFT_VERSION = 4.2;
351 | TARGETED_DEVICE_FAMILY = "1,2";
352 | };
353 | name = Release;
354 | };
355 | /* End XCBuildConfiguration section */
356 |
357 | /* Begin XCConfigurationList section */
358 | 22AEE37C21A1056800295C85 /* Build configuration list for PBXProject "SwiftAutoLayout" */ = {
359 | isa = XCConfigurationList;
360 | buildConfigurations = (
361 | 22AEE38821A1056800295C85 /* Debug */,
362 | 22AEE38921A1056800295C85 /* Release */,
363 | );
364 | defaultConfigurationIsVisible = 0;
365 | defaultConfigurationName = Release;
366 | };
367 | 22AEE38A21A1056800295C85 /* Build configuration list for PBXNativeTarget "SwiftAutoLayout" */ = {
368 | isa = XCConfigurationList;
369 | buildConfigurations = (
370 | 22AEE38B21A1056800295C85 /* Debug */,
371 | 22AEE38C21A1056800295C85 /* Release */,
372 | );
373 | defaultConfigurationIsVisible = 0;
374 | defaultConfigurationName = Release;
375 | };
376 | /* End XCConfigurationList section */
377 | };
378 | rootObject = 22AEE37921A1056800295C85 /* Project object */;
379 | }
380 |
--------------------------------------------------------------------------------
/SwiftAutoLayout.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/SwiftAutoLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/CGFloat+SystemSpacing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGFloat+SystemSpacing.swift
3 | // SwiftAutoLayout
4 | //
5 | // Created by Jake Sawyer on 11/27/18.
6 | // Copyright © 2018 SwiftKick Mobile. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public extension CGFloat {
12 | /**
13 | This value is only useful when using SwiftAutoLayout to define a constraint that should use system spacing.
14 | It is a nonsense value of `-99999` in all other scenarios.
15 |
16 | ```
17 | // Constrain the leading anchors of both `view` and `otherView` by the system-determined appropriate spacing.
18 | view.constrain(to: otherView).leading(constant: .systemSpacing)
19 | ```
20 | */
21 | @available(iOS 11, *)
22 | public static var systemSpacing: CGFloat { return -99999 }
23 | }
24 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/Constrainable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constrainable.swift
3 | // SwiftAutoLayout
4 | //
5 | // Created by Jake Sawyer on 8/30/18.
6 | // Copyright © 2018 SwiftKick Mobile. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | Definition of a constrainable instance (usually a `UIView` or `UILayoutGuide`) and its anchors.
13 | ```
14 | // add constraints to a view with vertical padding
15 | view.constrain(to: otherView).leadingTrailing().topBottom(.lessThanOrEqual, constant: 8)
16 | ```
17 | - NOTE: This protocol does not include `baseline` anchors.
18 | */
19 | public protocol Constrainable {
20 | var leadingAnchor: NSLayoutXAxisAnchor { get }
21 | var trailingAnchor: NSLayoutXAxisAnchor { get }
22 | var topAnchor: NSLayoutYAxisAnchor { get }
23 | var bottomAnchor: NSLayoutYAxisAnchor { get }
24 | var widthAnchor: NSLayoutDimension { get }
25 | var heightAnchor: NSLayoutDimension { get }
26 | var centerXAnchor: NSLayoutXAxisAnchor { get }
27 | var centerYAnchor: NSLayoutYAxisAnchor { get }
28 | }
29 |
30 | // MARK: - Declare UIView and UILayoutGuide to be constrainable
31 | extension UIView: Constrainable {}
32 | extension UILayoutGuide: Constrainable {}
33 |
34 | // MARK: - Constraint Builders
35 | public extension Constrainable {
36 | /**
37 | This method performs initial setup for constraining two `Constrainable`s together.
38 | In general, `UIView`s will have their autoresizing masks disabled, and will become children of the other `UIView` if they don't have one yet. Unowned `UILayoutGuide`s will have their owning views set.
39 |
40 | # Setup process:
41 | - If this is a `UIView`, its `translatesAutoresizingMaskIntoConstraints` will be disabled.
42 | - If this is a `UIView` and doesn't yet have a superview, and the supplied constrainable is also a `UIView`, this view will become a child of the supplied view.
43 | - If this is a `UIView` and doesn't yet have a superview, and the supplied constrainable is a `UILayoutGuide` with an owning view, this view will become the child of the supplied layout guide's owning view.
44 | - If this is a `UILayoutGuide` and doesn't yet have an owning view, and the supplied constrainable is a `UIView`, this layout guide will be added to the supplied view.
45 | - If this is a `UILayoutGuide` and doesn't yet have an owning view, and the supplied constrainable is also a `UILayoutGuide` with an owning view, this layout guide will add itself to the supplied layout guide's owning view.
46 |
47 | # Basic usage:
48 | ```
49 | // Constrain both leading anchors for `view` and `otherView` with an equal relationship, constant of 0, multiplier of 1, and required priority.
50 | // The constraint is activated by default.
51 | view.constrain(to: otherView).leading()
52 |
53 | // Constraint methods make heavy use of default arguments. You only need to specify arguments when setting specific values.
54 | view.constrain(to: otherView).width(.lessThanOrEqual, priority: .defaultLow)
55 |
56 | // Some constraints invert their anchors so your constants can always be positive. This includes trailing and bottom constraints.
57 | // In this example, `view` is inset inside `otherView` by 8 points from the top and 24 points from the bottom.
58 | view.constrain(to: otherView).top(constant: 8).bottom(constant: 24)
59 |
60 | // Chain method calls to create multiple constraints easily.
61 | view.constrain(to: otherView).top().bottom().width()
62 | ```
63 |
64 | # Advanced usage:
65 | ```
66 | // Helper methods simplify common scenarios by defining multiple constraints at the same time.
67 | view.constrain(to: otherView).centerXY().widthHeight()
68 |
69 | // Constrain a `view` to the `layoutMarginsGuide` of `otherView`, thus padding its edges inset from the safe areas and margins.
70 | view.constrain(to: otherView.layoutMarginsGuide).leadingTrailingTopBottom(constant: 32)
71 |
72 | // Access the constraint builder once the constraints are made, and grab the constraints
73 | let builder = view.constrain(to: otherView).leadingTrailing(constant: 16).topBottom(constant: 8)
74 | print(builder.constraints) // constraints are supplied in order of declaration. In this case: leading, trailing, top, bottom.
75 |
76 | // Create a highly customized constraint using system spacing, without activating it, and hold onto it for later use.
77 | let constraint = view.constrain(to: otherView).leading(.lessThanOrEqual, constant: .systemSpacing, multiplier: 0.5, priority: .defaultLow, activate: false).constraints.last!
78 | ```
79 | */
80 | @discardableResult
81 | func constrain(to constrainable: Constrainable) -> RelationalConstraintBuilder {
82 | return RelationalConstraintBuilder(first: self, second: constrainable)
83 | }
84 |
85 | /**
86 | This method performs initial setup for distributing two `Constrainable`s vertically and/or horizontally via constraints.
87 | `UIView`s will have their autoresizing masks disabled and are assumed to already have superviews.
88 | `UILayoutGuide`s are assumed to already have owning views.
89 |
90 | # Basic usage:
91 | ```
92 | // Constrain `view` horizontally after `otherView` via `otherView.trailingAnchor` and `view.leadingAnchor`.
93 | view.constrain(after: otherView).leadingTrailing()
94 | ```
95 | */
96 | func constrain(after constrainable: Constrainable) -> DistributiveConstraintBuilder {
97 | return DistributiveConstraintBuilder(before: constrainable, after: self)
98 | }
99 |
100 | /**
101 | This method performs initial setup for constraining a `Constrainable`'s width/height/aspect ratio.
102 | `UIView`s will have their autoresizing masks disabled.
103 |
104 | - NOTE: `CGFloat.systemSpacing` is not an acceptable constant here.
105 |
106 | # Basic usage:
107 | ```
108 | // Constrain `view`'s width to 50 points
109 | view.constrainSelf().width(constant: 50)
110 |
111 | // Constrain `view`'s width and height anchors to their intrinsic size at this moment
112 | view.constrainSelf().widthHeight(size: view.intrinsicContentSize)
113 | ```
114 | */
115 | func constrainSelf() -> SelfConstraintBuilder {
116 | return SelfConstraintBuilder(for: self)
117 | }
118 | }
119 |
120 | //MARK: - AnchorInfo
121 |
122 | /**
123 | Internal helper struct for supplying all required attributes for creating a constraint in one simple package.
124 | As a bonus, you can look up an `AnchorInfo` via `Constraiable.anchorInfo(xAxis/yAxis/dimension:)`
125 | */
126 | struct AnchorInfo {
127 | var item: Constrainable
128 | var attribute: NSLayoutConstraint.Attribute
129 | var anchor: NSLayoutAnchor
130 | }
131 |
132 |
133 | // MARK: - Anchor Infos on Constrainable
134 | extension Constrainable {
135 | var leadingAnchorInfo: AnchorInfo {
136 | return AnchorInfo(item: self, attribute: .leading, anchor: self.leadingAnchor)
137 | }
138 |
139 | var trailingAnchorInfo: AnchorInfo {
140 | return AnchorInfo(item: self, attribute: .trailing, anchor: self.trailingAnchor)
141 | }
142 |
143 | var topAnchorInfo: AnchorInfo {
144 | return AnchorInfo(item: self, attribute: .top, anchor: self.topAnchor)
145 | }
146 |
147 | var bottomAnchorInfo: AnchorInfo {
148 | return AnchorInfo(item: self, attribute: .bottom, anchor: self.bottomAnchor)
149 | }
150 |
151 | var widthAnchorInfo: AnchorInfo {
152 | return AnchorInfo(item: self, attribute: .width, anchor: self.widthAnchor)
153 | }
154 |
155 | var heightAnchorInfo: AnchorInfo {
156 | return AnchorInfo(item: self, attribute: .height, anchor: self.heightAnchor)
157 | }
158 |
159 | var centerXAnchorInfo: AnchorInfo {
160 | return AnchorInfo(item: self, attribute: .centerX, anchor: self.centerXAnchor)
161 | }
162 |
163 | var centerYAnchorInfo: AnchorInfo {
164 | return AnchorInfo(item: self, attribute: .centerY, anchor: self.centerYAnchor)
165 | }
166 | }
167 |
168 | // MARK: - Anchor Types
169 |
170 | /**
171 | Helper for writing custom constraints between two different `NSLayoutXAxisAnchor` anchors.
172 |
173 | ```
174 | // Constrain view's centerX anchor to another view's leading anchor
175 | view.constrain(to: otherView).xAxis(.centerX, to: .leading)
176 |
177 | // Modify the optional arguments to specify a more advanced constraint
178 | view.constrain(to: otherView).xAxis(.centerX, to: .leading, relation: .lessThanOrEqual, constant: 0, multiplier: 0.5, activate: false)
179 |
180 | // Use the constraint builder returned after each newly created constraint to access the constraints created in order
181 | let constraint = view.constrain(to: otherView).xAxis(.centerX, to: .leading, constant: 0, priority: .defaultHigh, activate: false).constraints.last!
182 | constraint.isActive = true // activate the constraint later
183 | ```
184 | */
185 | public enum XAxisAnchor {
186 | case leading
187 | case trailing
188 | case centerX
189 | }
190 |
191 | /**
192 | Helper for writing custom constraints between two different `NSLayoutYAxisAnchor` anchors.
193 |
194 | ```
195 | // Constrain view's centerY anchor to another view's bottom anchor
196 | view.constrain(to: otherView).yAxis(.centerY, to: .bottom)
197 |
198 | // Modify the optional arguments to specify a more advanced constraint
199 | view.constrain(to: otherView).yAxis(.centerY, to: .bottom, relation: .lessThanOrEqual, constant: 0, multiplier: 0.5, activate: false)
200 |
201 | // Use the constraint builder returned after each newly created constraint to access the constraints created in order
202 | let constraint = view.constrain(to: otherView).yAxis(.centerY, to: .bottom, constant: 0, priority: .defaultHigh, activate: false).constraints.last!
203 | constraint.isActive = true // activate the constraint later
204 | ```
205 | */
206 | public enum YAxisAnchor {
207 | case top
208 | case bottom
209 | case centerY
210 | }
211 |
212 | /**
213 | Helper for writing custom constraints between two different `NSLayoutDimension` anchors.
214 |
215 | ```
216 | // Constrain view's width anchor to another view's height anchor
217 | view.constrain(to: otherView).dimension(.width, to: .height)
218 |
219 | // Modify the optional arguments to specify a more advanced constraint
220 | view.constrain(to: otherView).dimension(.width, to: .height, relation: .lessThanOrEqual, constant: 0, multiplier: 0.5, activate: false)
221 |
222 | // Use the constraint builder returned after each newly created constraint to access the constraints created in order
223 | let constraint = view.constrain(to: otherView).dimension(.width, to: .height, constant: 0, priority: .defaultHigh, activate: false).constraints.last!
224 | constraint.isActive = true // activate the constraint later
225 | ```
226 | */
227 | public enum DimensionAnchor {
228 | case width
229 | case height
230 | }
231 |
232 |
233 | // MARK: - Getting AnchorInfos from enums
234 | extension Constrainable {
235 | func anchorInfo(xAxis anchorType: XAxisAnchor) -> AnchorInfo {
236 | switch anchorType {
237 | case .leading: return leadingAnchorInfo
238 | case .trailing: return trailingAnchorInfo
239 | case .centerX: return centerXAnchorInfo
240 | }
241 | }
242 |
243 | func anchorInfo(yAxis anchorType: YAxisAnchor) -> AnchorInfo {
244 | switch anchorType {
245 | case .top: return topAnchorInfo
246 | case .bottom: return bottomAnchorInfo
247 | case .centerY: return centerYAnchorInfo
248 | }
249 | }
250 |
251 | func anchorInfo(dimension anchorType: DimensionAnchor) -> AnchorInfo {
252 | switch anchorType {
253 | case .width: return widthAnchorInfo
254 | case .height: return heightAnchorInfo
255 | }
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/ConstraintBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConstraintBuilder.swift
3 | // SwiftAutoLayout
4 | //
5 | // Created by Jake Sawyer on 10/23/18.
6 | // Copyright © 2018 SwiftKick Mobile. All rights reserved.
7 | //
8 |
9 | // Look at all this code you didn't have to write!
10 |
11 | import Foundation
12 | import UIKit
13 |
14 | /**
15 | Protocol for defining constraint builders.
16 | All builders should be able to return their constraints in the order they've been built.
17 | */
18 | public protocol ConstraintBuilder {
19 | var constraints: [NSLayoutConstraint] { get }
20 | }
21 |
22 | // MARK: - Create Constraints
23 | extension ConstraintBuilder {
24 | /**
25 | Attempts to create a constraint between `first` and `second` anchors without activating it.
26 | - Note: Will inverse the `first` and `second` anchors if parameter `inverse` is true — useful for creating constraints relating to `trailingAnchor` or `bottomAnchor`s
27 | - Parameters:
28 | - first: The first anchor to constrain against the second.
29 | - second: The second anchor to which the first is constraining.
30 | - inverse: Optional — If true, the `first` and `second` anchors will be inverted when creating this constraint. Useful for keeping constants positive. Defaults to `false`.
31 | - Returns: The created constraint. This method does not activate the constraint.
32 | */
33 | func makeConstraint(first rawFirst: AnchorInfo,
34 | second rawSecond: AnchorInfo,
35 | relation: NSLayoutConstraint.Relation,
36 | constant: CGFloat,
37 | multiplier: CGFloat,
38 | priority: UILayoutPriority,
39 | activate: Bool,
40 | inverse: Bool = false) -> NSLayoutConstraint {
41 |
42 | let first = inverse ? rawSecond : rawFirst
43 | let second = inverse ? rawFirst : rawSecond
44 | let constraint: NSLayoutConstraint
45 | switch relation {
46 | case .equal:
47 | if #available(iOS 11, *),
48 | constant == .systemSpacing,
49 | let first = first.anchor as? NSLayoutXAxisAnchor,
50 | let second = second.anchor as? NSLayoutXAxisAnchor {
51 | // do not set constant
52 | constraint = first.constraint(equalToSystemSpacingAfter: second, multiplier: multiplier)
53 | } else if #available(iOS 11, *),
54 | constant == .systemSpacing,
55 | let first = first.anchor as? NSLayoutYAxisAnchor,
56 | let second = second.anchor as? NSLayoutYAxisAnchor {
57 | constraint = first.constraint(equalToSystemSpacingBelow: second, multiplier: multiplier)
58 | } else {
59 | constraint = NSLayoutConstraint(item: first.item,
60 | attribute: first.attribute,
61 | relatedBy: .equal,
62 | toItem: second.item,
63 | attribute: second.attribute,
64 | multiplier: multiplier,
65 | constant: constant)
66 | }
67 | case .lessThanOrEqual:
68 | if #available(iOS 11, *),
69 | constant == .systemSpacing,
70 | let first = first.anchor as? NSLayoutXAxisAnchor,
71 | let second = second.anchor as? NSLayoutXAxisAnchor {
72 | // do not set constant
73 | constraint = first.constraint(lessThanOrEqualToSystemSpacingAfter: second, multiplier: multiplier)
74 | } else if #available(iOS 11, *),
75 | constant == .systemSpacing,
76 | let first = first.anchor as? NSLayoutYAxisAnchor,
77 | let second = second.anchor as? NSLayoutYAxisAnchor {
78 | constraint = first.constraint(lessThanOrEqualToSystemSpacingBelow: second, multiplier: multiplier)
79 | } else {
80 | constraint = NSLayoutConstraint(item: first.item,
81 | attribute: first.attribute,
82 | relatedBy: .lessThanOrEqual,
83 | toItem: second.item,
84 | attribute: second.attribute,
85 | multiplier: multiplier,
86 | constant: constant)
87 | }
88 | case .greaterThanOrEqual:
89 | if #available(iOS 11, *),
90 | constant == .systemSpacing,
91 | let first = first.anchor as? NSLayoutXAxisAnchor,
92 | let second = second.anchor as? NSLayoutXAxisAnchor {
93 | // do not set constant
94 | constraint = first.constraint(greaterThanOrEqualToSystemSpacingAfter: second, multiplier: multiplier)
95 | } else if #available(iOS 11, *),
96 | constant == .systemSpacing,
97 | let first = first.anchor as? NSLayoutYAxisAnchor,
98 | let second = second.anchor as? NSLayoutYAxisAnchor {
99 | constraint = first.constraint(greaterThanOrEqualToSystemSpacingBelow: second, multiplier: multiplier)
100 | } else {
101 | constraint = NSLayoutConstraint(item: first.item,
102 | attribute: first.attribute,
103 | relatedBy: .greaterThanOrEqual,
104 | toItem: second.item,
105 | attribute: second.attribute,
106 | multiplier: multiplier,
107 | constant: constant)
108 | }
109 | }
110 | constraint.priority = priority
111 | if activate {
112 | constraint.isActive = true
113 | }
114 | return constraint
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/DistributiveConstraintBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DistributiveConstraintBuilder.swift
3 | // SwiftAutoLayout
4 | //
5 | // Created by Jake Sawyer on 11/13/18.
6 | // Copyright © 2018 SwiftKick Mobile. All rights reserved.
7 | //
8 |
9 | // Look at all this code you didn't have to write!
10 |
11 | import Foundation
12 | import UIKit
13 |
14 | /**
15 | A constraint builder for laying out a `Constrainable` horizontally or vertically after another.
16 | */
17 | public class DistributiveConstraintBuilder: ConstraintBuilder {
18 |
19 | /// The constraints created by this builder, in the order they were made.
20 | public internal(set) var constraints = [NSLayoutConstraint]()
21 |
22 | let beforeConstrainable: Constrainable
23 | let afterConstrainable: Constrainable
24 |
25 | /**
26 | Supplied constrainables, if they are `UIView`s, will have their `translatesAutoresizingMaskIntoConstraints` set to false.
27 | */
28 | init(before beforeConstrainable: Constrainable, after afterConstrainable: Constrainable) {
29 | self.beforeConstrainable = beforeConstrainable
30 | self.afterConstrainable = afterConstrainable
31 |
32 | for view in [beforeConstrainable, afterConstrainable].compactMap({ $0 as? UIView }) {
33 | view.translatesAutoresizingMaskIntoConstraints = false
34 | }
35 | }
36 | }
37 |
38 | // MARK: - Make Constraints
39 | public extension DistributiveConstraintBuilder {
40 | /**
41 | Horizontally distribute the before and after constrainables via their leading and trailing anchors.
42 | - NOTE: Creates a single constraint.
43 | - Parameters:
44 | - relation: The relationship between the anchors. Defaults to `.equal`
45 | - constant: The constant for the constraint. Defaults to 0
46 | - multiplier: The multiplier for the constraint. Defaults to 1
47 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
48 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
49 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
50 | */
51 | @discardableResult
52 | func leadingTrailing(_ relation: NSLayoutConstraint.Relation = .equal,
53 | constant: CGFloat = 0,
54 | multiplier: CGFloat = 1,
55 | priority: UILayoutPriority = .required,
56 | activate: Bool = true) -> DistributiveConstraintBuilder {
57 |
58 | let constraint = makeConstraint(first: beforeConstrainable.trailingAnchorInfo,
59 | second: afterConstrainable.leadingAnchorInfo,
60 | relation: relation,
61 | constant: constant,
62 | multiplier: multiplier,
63 | priority: priority,
64 | activate: activate,
65 | inverse: true)
66 |
67 | constraints.append(constraint)
68 | return self
69 | }
70 |
71 | /**
72 | Vertically distribute the before and after constrainables via their top and bottom anchors.
73 | - NOTE: Creates a single constraint.
74 | - Parameters:
75 | - relation: The relationship between the anchors. Defaults to `.equal`
76 | - constant: The constant for the constraint. Defaults to 0
77 | - multiplier: The multiplier for the constraint. Defaults to 1
78 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
79 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
80 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
81 | */
82 | @discardableResult
83 | func topBottom(_ relation: NSLayoutConstraint.Relation = .equal,
84 | constant: CGFloat = 0,
85 | multiplier: CGFloat = 1,
86 | priority: UILayoutPriority = .required,
87 | activate: Bool = true) -> DistributiveConstraintBuilder {
88 |
89 | let constraint = makeConstraint(first: beforeConstrainable.bottomAnchorInfo,
90 | second: afterConstrainable.topAnchorInfo,
91 | relation: relation,
92 | constant: constant,
93 | multiplier: multiplier,
94 | priority: priority,
95 | activate: activate,
96 | inverse: true)
97 |
98 | constraints.append(constraint)
99 | return self
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/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.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/RelationalConstraintBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RelationalConstraintBuilder.swift
3 | // SwiftAutoLayout
4 | //
5 | // Created by Jake Sawyer on 10/23/18.
6 | // Copyright © 2018 SwiftKick Mobile. All rights reserved.
7 | //
8 |
9 | // Look at all this code you didn't have to write!
10 |
11 | import Foundation
12 | import UIKit
13 |
14 | /**
15 | Creates constraints between two `Constrainable`s, usually by matching anchors.
16 | It can do custom constraints as well via its `xAxis(_:to:)`, `yAxis(_:to:)`, and `dimension(_:to:)` methods.
17 | */
18 | public class RelationalConstraintBuilder: ConstraintBuilder {
19 |
20 | /// The constraints created by this builder, in the order they were made.
21 | public internal(set) var constraints = [NSLayoutConstraint]()
22 |
23 | let firstConstrainable: Constrainable
24 | let secondConstrainable: Constrainable
25 |
26 | /**
27 | If the first constrainable doesn't have a parent or owning view, the second will become it.
28 | If the first constrainable is a view, its `translatesAutoresizingMaskIntoConstraints` will be disabled.
29 | */
30 | init(first firstConstrainable: Constrainable, second secondConstrainable: Constrainable) {
31 | self.firstConstrainable = firstConstrainable
32 | self.secondConstrainable = secondConstrainable
33 |
34 | UIView.performWithoutAnimation {
35 | if let view = firstConstrainable as? UIView {
36 | view.translatesAutoresizingMaskIntoConstraints = false
37 | if view.superview == nil {
38 | if let otherView = secondConstrainable as? UIView {
39 | otherView.addSubview(view)
40 | } else if let otherLayoutGuide = secondConstrainable as? UILayoutGuide, let otherView = otherLayoutGuide.owningView {
41 | otherView.addSubview(view)
42 | }
43 | }
44 | } else if let layoutGuide = firstConstrainable as? UILayoutGuide, layoutGuide.owningView == nil {
45 | if let otherView = secondConstrainable as? UIView {
46 | otherView.addLayoutGuide(layoutGuide)
47 | } else if let otherGuide = secondConstrainable as? UILayoutGuide, let otherView = otherGuide.owningView {
48 | otherView.addLayoutGuide(layoutGuide)
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | // MARK: - Edge Constraints
56 | public extension RelationalConstraintBuilder {
57 | /**
58 | Constrains both leading anchors together.
59 | - NOTE: Creates a single constraint.
60 | - Parameters:
61 | - relation: The relationship between the anchors. Defaults to `.equal`
62 | - constant: The constant for the constraint. Defaults to 0
63 | - multiplier: The multiplier for the constraint. Defaults to 1
64 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
65 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
66 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
67 | */
68 | @discardableResult
69 | func leading(_ relation: NSLayoutConstraint.Relation = .equal,
70 | constant: CGFloat = 0,
71 | multiplier: CGFloat = 1,
72 | priority: UILayoutPriority = .required,
73 | activate: Bool = true) -> RelationalConstraintBuilder {
74 |
75 | let constraint = makeConstraint(first: firstConstrainable.leadingAnchorInfo,
76 | second: secondConstrainable.leadingAnchorInfo,
77 | relation: relation,
78 | constant: constant,
79 | multiplier: multiplier,
80 | priority: priority,
81 | activate: activate)
82 |
83 | constraints.append(constraint)
84 | return self
85 | }
86 |
87 | /**
88 | Constrains both trailing anchors together.
89 | - NOTE: Creates a single constraint.
90 | - Parameters:
91 | - relation: The relationship between the anchors. Defaults to `.equal`
92 | - constant: The constant for the constraint. Defaults to 0
93 | - multiplier: The multiplier for the constraint. Defaults to 1
94 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
95 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
96 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
97 | */
98 | @discardableResult
99 | func trailing(_ relation: NSLayoutConstraint.Relation = .equal,
100 | constant: CGFloat = 0,
101 | multiplier: CGFloat = 1,
102 | priority: UILayoutPriority = .required,
103 | activate: Bool = true) -> RelationalConstraintBuilder {
104 |
105 | let constraint = makeConstraint(first: firstConstrainable.trailingAnchorInfo,
106 | second: secondConstrainable.trailingAnchorInfo,
107 | relation: relation,
108 | constant: constant,
109 | multiplier: multiplier,
110 | priority: priority,
111 | activate: activate,
112 | inverse: true)
113 |
114 | constraints.append(constraint)
115 | return self
116 | }
117 |
118 | /**
119 | Constrains both leading and trailing anchors together with the same criteria.
120 | - NOTE: Creates two constraints.
121 | - Parameters:
122 | - relation: The relationship between the anchors. Defaults to `.equal`
123 | - constant: The constant for the constraint. Defaults to 0
124 | - multiplier: The multiplier for the constraint. Defaults to 1
125 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
126 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
127 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
128 | */
129 | @discardableResult
130 | func leadingTrailing(_ relation: NSLayoutConstraint.Relation = .equal,
131 | constant: CGFloat = 0,
132 | multiplier: CGFloat = 1,
133 | priority: UILayoutPriority = .required,
134 | activate: Bool = true) -> RelationalConstraintBuilder {
135 |
136 | leading(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
137 | trailing(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
138 | return self
139 | }
140 |
141 | /**
142 | Constrains both top anchors together.
143 | - NOTE: Creates a single constraint.
144 | - Parameters:
145 | - relation: The relationship between the anchors. Defaults to `.equal`
146 | - constant: The constant for the constraint. Defaults to 0
147 | - multiplier: The multiplier for the constraint. Defaults to 1
148 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
149 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
150 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
151 | */
152 | @discardableResult
153 | func top(_ relation: NSLayoutConstraint.Relation = .equal,
154 | constant: CGFloat = 0,
155 | multiplier: CGFloat = 1,
156 | priority: UILayoutPriority = .required,
157 | activate: Bool = true) -> RelationalConstraintBuilder {
158 |
159 | constraints.append(makeConstraint(first: firstConstrainable.topAnchorInfo, second: secondConstrainable.topAnchorInfo, relation: relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate))
160 | return self
161 | }
162 |
163 | /**
164 | Constrains both bottom anchors together.
165 | - NOTE: Creates a single constraint.
166 | - Parameters:
167 | - relation: The relationship between the anchors. Defaults to `.equal`
168 | - constant: The constant for the constraint. Defaults to 0
169 | - multiplier: The multiplier for the constraint. Defaults to 1
170 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
171 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
172 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
173 | */
174 | @discardableResult
175 | func bottom(_ relation: NSLayoutConstraint.Relation = .equal,
176 | constant: CGFloat = 0,
177 | multiplier: CGFloat = 1,
178 | priority: UILayoutPriority = .required,
179 | activate: Bool = true) -> RelationalConstraintBuilder {
180 |
181 | constraints.append(makeConstraint(first: firstConstrainable.bottomAnchorInfo, second: secondConstrainable.bottomAnchorInfo, relation: relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate, inverse: true))
182 | return self
183 | }
184 |
185 | /**
186 | Constrains both top and bottom anchors together with the same criteria.
187 | - NOTE: Creates two constraints.
188 | - Parameters:
189 | - relation: The relationship between the anchors. Defaults to `.equal`
190 | - constant: The constant for the constraint. Defaults to 0
191 | - multiplier: The multiplier for the constraint. Defaults to 1
192 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
193 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
194 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
195 | */
196 | @discardableResult
197 | func topBottom(_ relation: NSLayoutConstraint.Relation = .equal,
198 | constant: CGFloat = 0,
199 | multiplier: CGFloat = 1,
200 | priority: UILayoutPriority = .required,
201 | activate: Bool = true) -> RelationalConstraintBuilder {
202 |
203 | top(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
204 | bottom(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
205 | return self
206 | }
207 |
208 | /**
209 | Constrains both leading, trailing, top, and bottom anchors together with the same criteria.
210 | - NOTE: Creates four constraints.
211 | - Parameters:
212 | - relation: The relationship between the anchors. Defaults to `.equal`
213 | - constant: The constant for the constraint. Defaults to 0
214 | - multiplier: The multiplier for the constraint. Defaults to 1
215 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
216 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
217 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
218 | */
219 | @discardableResult
220 | func leadingTrailingTopBottom(_ relation: NSLayoutConstraint.Relation = .equal,
221 | constant: CGFloat = 0,
222 | multiplier: CGFloat = 1,
223 | priority: UILayoutPriority = .required,
224 | activate: Bool = true) -> RelationalConstraintBuilder {
225 |
226 | leadingTrailing(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
227 | topBottom(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
228 | return self
229 | }
230 |
231 | }
232 |
233 | // MARK: - Spacing Constraints
234 | public extension RelationalConstraintBuilder {
235 |
236 | /**
237 | Constrains both centerX anchors together.
238 | - NOTE: Creates a single constraint.
239 | - Parameters:
240 | - relation: The relationship between the anchors. Defaults to `.equal`
241 | - constant: The constant for the constraint. Defaults to 0
242 | - multiplier: The multiplier for the constraint. Defaults to 1
243 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
244 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
245 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
246 | */
247 | @discardableResult
248 | func centerX(_ relation: NSLayoutConstraint.Relation = .equal,
249 | constant: CGFloat = 0,
250 | multiplier: CGFloat = 1,
251 | priority: UILayoutPriority = .required,
252 | activate: Bool = true) -> RelationalConstraintBuilder {
253 |
254 | constraints.append(makeConstraint(first: firstConstrainable.centerXAnchorInfo, second: secondConstrainable.centerXAnchorInfo, relation: relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate))
255 | return self
256 | }
257 |
258 | /**
259 | Constrains both centerY anchors together.
260 | - NOTE: Creates a single constraint.
261 | - Parameters:
262 | - relation: The relationship between the anchors. Defaults to `.equal`
263 | - constant: The constant for the constraint. Defaults to 0
264 | - multiplier: The multiplier for the constraint. Defaults to 1
265 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
266 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
267 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
268 | */
269 | @discardableResult
270 | func centerY(_ relation: NSLayoutConstraint.Relation = .equal,
271 | constant: CGFloat = 0,
272 | multiplier: CGFloat = 1,
273 | priority: UILayoutPriority = .required,
274 | activate: Bool = true) -> RelationalConstraintBuilder {
275 |
276 | constraints.append(makeConstraint(first: firstConstrainable.centerYAnchorInfo, second: secondConstrainable.centerYAnchorInfo, relation: relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate))
277 | return self
278 | }
279 |
280 | /**
281 | Constrains both centerX and centerY anchors together with the same criteria.
282 | - NOTE: Creates two constraints.
283 | - Parameters:
284 | - relation: The relationship between the anchors. Defaults to `.equal`
285 | - constant: The constant for the constraint. Defaults to 0
286 | - multiplier: The multiplier for the constraint. Defaults to 1
287 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
288 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
289 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
290 | */
291 | @discardableResult
292 | func centerXY(_ relation: NSLayoutConstraint.Relation = .equal,
293 | constant: CGFloat = 0,
294 | multiplier: CGFloat = 1,
295 | priority: UILayoutPriority = .required,
296 | activate: Bool = true) -> RelationalConstraintBuilder {
297 |
298 | centerX(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
299 | centerY(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
300 | return self
301 | }
302 |
303 | }
304 |
305 | // MARK: - Dimension Constraints
306 | public extension RelationalConstraintBuilder {
307 | /**
308 | Constrains both width anchors together.
309 | - NOTE: Creates a single constraint.
310 | - Parameters:
311 | - relation: The relationship between the anchors. Defaults to `.equal`
312 | - constant: The constant for the constraint. Defaults to 0
313 | - multiplier: The multiplier for the constraint. Defaults to 1
314 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
315 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
316 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
317 | */
318 | @discardableResult
319 | func width(_ relation: NSLayoutConstraint.Relation = .equal,
320 | constant: CGFloat = 0,
321 | multiplier: CGFloat = 1,
322 | priority: UILayoutPriority = .required,
323 | activate: Bool = true) -> RelationalConstraintBuilder {
324 |
325 | constraints.append(makeConstraint(first: firstConstrainable.widthAnchorInfo, second: secondConstrainable.widthAnchorInfo, relation: relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate))
326 | return self
327 | }
328 |
329 | /**
330 | Constrains both height anchors together.
331 | - NOTE: Creates a single constraint.
332 | - Parameters:
333 | - relation: The relationship between the anchors. Defaults to `.equal`
334 | - constant: The constant for the constraint. Defaults to 0
335 | - multiplier: The multiplier for the constraint. Defaults to 1
336 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
337 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
338 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
339 | */
340 | @discardableResult
341 | func height(_ relation: NSLayoutConstraint.Relation = .equal,
342 | constant: CGFloat = 0,
343 | multiplier: CGFloat = 1,
344 | priority: UILayoutPriority = .required,
345 | activate: Bool = true) -> RelationalConstraintBuilder {
346 |
347 | constraints.append(makeConstraint(first: firstConstrainable.heightAnchorInfo, second: secondConstrainable.heightAnchorInfo, relation: relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate))
348 | return self
349 | }
350 |
351 | /**
352 | Constrains both width and height anchors together with the same criteria.
353 | - NOTE: Creates two constraints.
354 | - Parameters:
355 | - relation: The relationship between the anchors. Defaults to `.equal`
356 | - constant: The constant for the constraint. Defaults to 0
357 | - multiplier: The multiplier for the constraint. Defaults to 1
358 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
359 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
360 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
361 | */
362 | @discardableResult
363 | func widthHeight(_ relation: NSLayoutConstraint.Relation = .equal,
364 | constant: CGFloat = 0,
365 | multiplier: CGFloat = 1,
366 | priority: UILayoutPriority = .required,
367 | activate: Bool = true) -> RelationalConstraintBuilder {
368 |
369 | width(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
370 | height(relation, constant: constant, multiplier: multiplier, priority: priority, activate: activate)
371 | return self
372 | }
373 | }
374 |
375 | // MARK: - Custom Constraints
376 | public extension RelationalConstraintBuilder {
377 |
378 | /**
379 | Method for building custom constraints between two different NSLayoutXAnchor anchors.
380 | If you want to constrain the same anchors across two constrainables, just use their named method. IE, `view.constrain(to: otherView).leading()`
381 |
382 | - NOTE: Creates a single constraint.
383 | - Parameters:
384 | - firstAnchor: This constrainable's NSLayoutXAnchor that will be constrained to the other constrainable's anchor.
385 | - secondAnchor: The other constrainable's NSLayoutXAnchor that will be constrained to our constrainable's anchor.
386 | - relation: The relationship between the anchors. Defaults to `.equal`
387 | - constant: The constant for the constraint. Defaults to 0
388 | - multiplier: The multiplier for the constraint. Defaults to 1
389 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
390 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
391 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
392 |
393 | ```
394 | // Constrain view's centerX anchor to another view's leading anchor
395 | view.constrain(to: otherView).xAxis(.centerX, to: .leading)
396 |
397 | // Modify the optional arguments to specify a more advanced constraint
398 | view.constrain(to: otherView).xAxis(.centerX, to: .leading, relation: .lessThanOrEqual, constant: 0, multiplier: 0.5, activate: false)
399 |
400 | // Use the constraint builder returned after each newly created constraint to access the constraints created in order
401 | let constraint = view.constrain(to: otherView).xAxis(.centerX, to: .leading, constant: 0, priority: .defaultHigh, activate: false).constraints.last!
402 | constraint.isActive = true // activate the constraint later
403 | ```
404 | */
405 | @discardableResult
406 | func xAxis(_ firstAnchor: XAxisAnchor,
407 | to secondAnchor: XAxisAnchor,
408 | relation: NSLayoutConstraint.Relation = .equal,
409 | constant: CGFloat = 0,
410 | multiplier: CGFloat = 1,
411 | priority: UILayoutPriority = .required,
412 | activate: Bool = true) -> RelationalConstraintBuilder {
413 |
414 | let constraint = makeConstraint(first: firstConstrainable.anchorInfo(xAxis: firstAnchor),
415 | second: secondConstrainable.anchorInfo(xAxis: secondAnchor),
416 | relation: relation,
417 | constant: constant,
418 | multiplier: multiplier,
419 | priority: priority,
420 | activate: activate)
421 |
422 | constraints.append(constraint)
423 | return self
424 | }
425 |
426 | /**
427 | Method for building custom constraints between two different NSLayoutYAnchor anchors.
428 | If you want to constrain the same anchors across two constrainables, just use their named method. IE, `view.constrain(to: otherView).top()`
429 |
430 | - NOTE: Creates a single constraint.
431 | - Parameters:
432 | - firstAnchor: This constrainable's NSLayoutYAnchor that will be constrained to the other constrainable's anchor.
433 | - secondAnchor: The other constrainable's NSLayoutYAnchor that will be constrained to our constrainable's anchor.
434 | - relation: The relationship between the anchors. Defaults to `.equal`
435 | - constant: The constant for the constraint. Defaults to 0
436 | - multiplier: The multiplier for the constraint. Defaults to 1
437 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
438 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
439 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
440 |
441 | ```
442 | // Constrain view's centerY anchor to another view's bottom anchor
443 | view.constrain(to: otherView).yAxis(.centerY, to: .bottom)
444 |
445 | // Modify the optional arguments to specify a more advanced constraint
446 | view.constrain(to: otherView).yAxis(.centerY, to: .bottom, relation: .lessThanOrEqual, constant: 0, multiplier: 0.5, activate: false)
447 |
448 | // Use the constraint builder returned after each newly created constraint to access the constraints created in order
449 | let constraint = view.constrain(to: otherView).yAxis(.centerY, to: .bottom, constant: 0, priority: .defaultHigh, activate: false).constraints.last!
450 | constraint.isActive = true // activate the constraint later
451 | ```
452 | */
453 | @discardableResult
454 | func yAxis(_ firstAnchor: YAxisAnchor,
455 | to secondAnchor: YAxisAnchor,
456 | relation: NSLayoutConstraint.Relation = .equal,
457 | constant: CGFloat = 0,
458 | multiplier: CGFloat = 1,
459 | priority: UILayoutPriority = .required,
460 | activate: Bool = true) -> RelationalConstraintBuilder {
461 |
462 | let constraint = makeConstraint(first: firstConstrainable.anchorInfo(yAxis: firstAnchor),
463 | second: secondConstrainable.anchorInfo(yAxis: secondAnchor),
464 | relation: relation,
465 | constant: constant,
466 | multiplier: multiplier,
467 | priority: priority,
468 | activate: activate)
469 |
470 | constraints.append(constraint)
471 | return self
472 | }
473 |
474 | /**
475 | Method for building custom constraints between two different NSLayoutDimension anchors.
476 | If you want to constrain the same anchors across two constrainables, just use their named method. IE, `view.constrain(to: otherView).width()`
477 |
478 | - NOTE: Creates a single constraint.
479 | - Parameters:
480 | - firstAnchor: This constrainable's NSLayoutDimension anchor that will be constrained to the other constrainable's anchor.
481 | - secondAnchor: The other constrainable's NSLayoutDimension anchor that will be constrained to our constrainable's anchor.
482 | - relation: The relationship between the anchors. Defaults to `.equal`
483 | - constant: The constant for the constraint. Defaults to 0
484 | - multiplier: The multiplier for the constraint. Defaults to 1
485 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
486 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
487 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
488 |
489 | ```
490 | // Constrain view's width anchor to another view's height anchor
491 | view.constrain(to: otherView).dimension(.width, to: .height)
492 |
493 | // Modify the optional arguments to specify a more advanced constraint
494 | view.constrain(to: otherView).dimension(.width, to: .height, relation: .lessThanOrEqual, constant: 0, multiplier: 0.5, activate: false)
495 |
496 | // Use the constraint builder returned after each newly created constraint to access the constraints created in order
497 | let constraint = view.constrain(to: otherView).dimension(.width, to: .height, constant: 0, priority: .defaultHigh, activate: false).constraints.last!
498 | constraint.isActive = true // activate the constraint later
499 | ```
500 | */
501 | @discardableResult
502 | func dimension(_ firstAnchor: DimensionAnchor,
503 | to secondAnchor: DimensionAnchor,
504 | relation: NSLayoutConstraint.Relation = .equal,
505 | constant: CGFloat = 0,
506 | multiplier: CGFloat = 1,
507 | priority: UILayoutPriority = .required,
508 | activate: Bool = true) -> RelationalConstraintBuilder {
509 |
510 | let constraint = makeConstraint(first: firstConstrainable.anchorInfo(dimension: firstAnchor),
511 | second: secondConstrainable.anchorInfo(dimension: secondAnchor),
512 | relation: relation,
513 | constant: constant,
514 | multiplier: multiplier,
515 | priority: priority,
516 | activate: activate)
517 |
518 | constraints.append(constraint)
519 | return self
520 | }
521 |
522 | }
523 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/SelfConstraintBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelfConstraintBuilder.swift
3 | // SwiftAutoLayout
4 | //
5 | // Created by Jake Sawyer on 10/23/18.
6 | // Copyright © 2018 SwiftKick Mobile. All rights reserved.
7 | //
8 |
9 | // Look at all this code you didn't have to write!
10 |
11 | import Foundation
12 |
13 | /**
14 | Creates constraints that are self-defining, like width, height, and aspect ratios.
15 | */
16 | public class SelfConstraintBuilder: ConstraintBuilder {
17 |
18 | /// The constraints created by this builder, in the order they were made.
19 | public internal(set) var constraints = [NSLayoutConstraint]()
20 |
21 | let constrainable: Constrainable
22 |
23 | /**
24 | Views will have their `translatesAutoresizingMaskIntoConstraints` disabled.
25 | */
26 | init(for constrainable: Constrainable) {
27 | self.constrainable = constrainable
28 | if let view = constrainable as? UIView {
29 | view.translatesAutoresizingMaskIntoConstraints = false
30 | }
31 | }
32 |
33 | /// Helper method for defining self constraints instead of constraints between two `Constrainable`s.
34 | func makeDimensionConstraint(anchor: NSLayoutDimension,
35 | relation: NSLayoutConstraint.Relation,
36 | constant rawConstant: CGFloat,
37 | priority: UILayoutPriority,
38 | activate: Bool) -> NSLayoutConstraint {
39 |
40 | var constant = rawConstant
41 | if #available(iOS 11, *), constant == .systemSpacing {
42 | assertionFailure("[SwiftAutoLayout] Unsupported usage of `CGFloat.systemSpacing` with `SelfConstraintBuilder`!")
43 | constant = 8
44 | print("[SwiftAutoLayout] Using `\(constant)` as a placeholder value instead of `.systemSpacing` in `SelfConstraintBuilder`!")
45 | }
46 | let result: NSLayoutConstraint
47 | switch relation {
48 | case .equal:
49 | result = anchor.constraint(equalToConstant: constant)
50 | case .lessThanOrEqual:
51 | result = anchor.constraint(lessThanOrEqualToConstant: constant)
52 | case .greaterThanOrEqual:
53 | result = anchor.constraint(greaterThanOrEqualToConstant: constant)
54 | }
55 | result.priority = priority
56 | if activate {
57 | result.isActive = true
58 | }
59 | return result
60 | }
61 | }
62 |
63 | // MARK: - Make Constraints
64 | public extension SelfConstraintBuilder {
65 | /**
66 | Constrains width anchor to a constant.
67 | - NOTE: Creates a single constraint.
68 | - Parameters:
69 | - relation: The relationship between the anchors. Defaults to `.equal`
70 | - constant: The constant for the constraint. Defaults to 0
71 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
72 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
73 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
74 | */
75 | @discardableResult
76 | public func width(_ relation: NSLayoutConstraint.Relation = .equal,
77 | constant: CGFloat = 0,
78 | priority: UILayoutPriority = .required,
79 | activate: Bool = true) -> SelfConstraintBuilder {
80 |
81 | let constraint = makeDimensionConstraint(anchor: constrainable.widthAnchor,
82 | relation: relation,
83 | constant: constant,
84 | priority: priority,
85 | activate: activate)
86 |
87 | constraints.append(constraint)
88 | return self
89 | }
90 |
91 | /**
92 | Constrains height anchor to a constant.
93 | - NOTE: Creates a single constraint.
94 | - Parameters:
95 | - relation: The relationship between the anchors. Defaults to `.equal`
96 | - constant: The constant for the constraint. Defaults to 0
97 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
98 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
99 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
100 | */
101 | @discardableResult
102 | public func height(_ relation: NSLayoutConstraint.Relation = .equal,
103 | constant: CGFloat = 0,
104 | priority: UILayoutPriority = .required,
105 | activate: Bool = true) -> SelfConstraintBuilder {
106 |
107 | let constraint = makeDimensionConstraint(anchor: constrainable.heightAnchor,
108 | relation: relation,
109 | constant: constant,
110 | priority: priority,
111 | activate: activate)
112 |
113 | constraints.append(constraint)
114 | return self
115 | }
116 |
117 | /**
118 | Constrains width and height anchors individually to the same constant.
119 | - NOTE: Creates two constraints.
120 | - Parameters:
121 | - relation: The relationship between the anchors. Defaults to `.equal`
122 | - constant: The constant for the constraint. Defaults to 0
123 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
124 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
125 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
126 | */
127 | @discardableResult
128 | public func widthHeight(_ relation: NSLayoutConstraint.Relation = .equal,
129 | constant: CGFloat = 0,
130 | priority: UILayoutPriority = .required,
131 | activate: Bool = true) -> SelfConstraintBuilder {
132 |
133 | width(relation, constant: constant, priority: priority, activate: activate)
134 | height(relation, constant: constant, priority: priority, activate: activate)
135 | return self
136 | }
137 |
138 | /**
139 | Constrains width and height anchors individually to the CGSize's width and height respectively.
140 | - NOTE: Creates two constraints.
141 | - Parameters:
142 | - relation: The relationship between the anchors. Defaults to `.equal`
143 | - constant: The constant for the constraint. Defaults to 0
144 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
145 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
146 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
147 | */
148 | @discardableResult
149 | public func widthHeight(_ relation: NSLayoutConstraint.Relation = .equal,
150 | size: CGSize = .zero,
151 | priority: UILayoutPriority = .required,
152 | activate: Bool = true) -> SelfConstraintBuilder {
153 |
154 | width(relation, constant: size.width, priority: priority, activate: activate)
155 | height(relation, constant: size.height, priority: priority, activate: activate)
156 | return self
157 | }
158 |
159 | /**
160 | Constrains the height anchor to the width anchor with an aspect ratio multiplier.
161 | - NOTE: Creates a single constraint.
162 | - Parameters:
163 | - relation: The relationship between the anchors. Defaults to `.equal`
164 | - constant: The constant for the constraint. Defaults to 0
165 | - priority: The priority for the constraint. Defaults to `.required`. Note that `.required` constraints must be activated and cannot be deactivated. Specify a lower value for dynamically activated constraints.
166 | - activate: Whether or not this constraint should be activated now. Disable this if you want to activate the constraint later.
167 | - Returns: The constraint builder. Access any constraints built so far in the order declared via its `constraints` property.
168 | */
169 | @discardableResult
170 | public func aspectRatio(_ aspectRatio: CGFloat,
171 | priority: UILayoutPriority = .required,
172 | activate: Bool = true) -> SelfConstraintBuilder {
173 |
174 | let constraint = constrainable.heightAnchor.constraint(equalTo: constrainable.widthAnchor, multiplier: aspectRatio)
175 | constraint.priority = priority
176 | if activate {
177 | constraint.isActive = true
178 | }
179 | constraints.append(constraint)
180 | return self
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/SwiftAutoLayout/SwiftAutoLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftAutoLayout.h
3 | // SwiftAutoLayout
4 | //
5 | // Created by Timothy Moose on 11/17/18.
6 | // Copyright © 2018 it.swiftkick. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SwiftAutoLayout.
12 | FOUNDATION_EXPORT double SwiftAutoLayoutVersionNumber;
13 |
14 | //! Project version string for SwiftAutoLayout.
15 | FOUNDATION_EXPORT const unsigned char SwiftAutoLayoutVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------