├── .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 | [![Twitter: @TimothyMoose](https://img.shields.io/badge/contact-@TimothyMoose-blue.svg?style=flat)](https://twitter.com/TimothyMoose) 4 | [![Version](https://img.shields.io/cocoapods/v/SwiftAutoLayout.svg?style=flat)](http://cocoadocs.org/docsets/SwiftAutoLayout) 5 | [![License](https://img.shields.io/cocoapods/l/SwiftAutoLayout.svg?style=flat)](http://cocoadocs.org/docsets/SwiftAutoLayout) 6 | [![Platform](https://img.shields.io/cocoapods/p/SwiftAutoLayout.svg?style=flat)](http://cocoadocs.org/docsets/SwiftAutoLayout) 7 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](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 | --------------------------------------------------------------------------------