mirror of https://github.com/SnapKit/SnapKit
Preview Release
This commit is contained in:
parent
6212ee9575
commit
6078e04895
|
@ -0,0 +1,4 @@
|
||||||
|
Preview
|
||||||
|
=======
|
||||||
|
|
||||||
|
Snappy is currently in preview and regular Changelogs will be provided as it comes out of preview into production quality code.
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// Constraint.swift
|
||||||
|
// Snappy
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011-2014 Masonry Team - https://github.com/Masonry
|
||||||
|
//
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// 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.
|
|
@ -0,0 +1,267 @@
|
||||||
|
# Snappy
|
||||||
|
|
||||||
|
Snappy is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Snappy has its own layout DSL which provides a chainable way of describing your NSLayoutConstraints which results in layout code that is more concise and readable.
|
||||||
|
Snappy supports iOS and Mac OS X.
|
||||||
|
|
||||||
|
## What's wrong with NSLayoutConstraints?
|
||||||
|
|
||||||
|
Under the hood Auto Layout is a powerful and flexible way of organising and laying out your views. However creating constraints from code is verbose and not very descriptive.
|
||||||
|
Imagine a simple example in which you want to have a view fill its superview but inset by 10 pixels on every side
|
||||||
|
```swift
|
||||||
|
let superview = self;
|
||||||
|
|
||||||
|
let view1 = UIView()
|
||||||
|
view1.setTranslatesAutoresizingMaskIntoConstraints(false)
|
||||||
|
view1.backgroundColor = UIColor.greenColor()
|
||||||
|
superview.addSubview(view1)
|
||||||
|
|
||||||
|
let padding = UIEdgeInsetsMake(10, 10, 10, 10)
|
||||||
|
|
||||||
|
superview.addConstraints([
|
||||||
|
NSLayoutConstraint(
|
||||||
|
item: view1,
|
||||||
|
attribute: NSLayoutAttribute.Top,
|
||||||
|
relatedBy: NSLayoutRelation.Equal,
|
||||||
|
toItem: superview,
|
||||||
|
attribute: NSLayoutAttribute.Top,
|
||||||
|
multiplier: 1.0,
|
||||||
|
constant: padding.top
|
||||||
|
),
|
||||||
|
NSLayoutConstraint(
|
||||||
|
item: view1,
|
||||||
|
attribute: NSLayoutAttribute.Left,
|
||||||
|
relatedBy: NSLayoutRelation.Equal,
|
||||||
|
toItem: superview,
|
||||||
|
attribute: NSLayoutAttribute.Left,
|
||||||
|
multiplier: 1.0,
|
||||||
|
constant: padding.left
|
||||||
|
),
|
||||||
|
NSLayoutConstraint(
|
||||||
|
item: view1,
|
||||||
|
attribute: NSLayoutAttribute.Bottom,
|
||||||
|
relatedBy: NSLayoutRelation.Equal,
|
||||||
|
toItem: superview,
|
||||||
|
attribute: NSLayoutAttribute.Bottom,
|
||||||
|
multiplier: 1.0,
|
||||||
|
constant: -padding.bottom
|
||||||
|
),
|
||||||
|
NSLayoutConstraint(
|
||||||
|
item: view1,
|
||||||
|
attribute: NSLayoutAttribute.Right,
|
||||||
|
relatedBy: NSLayoutRelation.Equal,
|
||||||
|
toItem: superview,
|
||||||
|
attribute: NSLayoutAttribute.Right,
|
||||||
|
multiplier: 1.0,
|
||||||
|
constant: -padding.right
|
||||||
|
)
|
||||||
|
])
|
||||||
|
```
|
||||||
|
Even with such a simple example the code needed is quite verbose and quickly becomes unreadable when you have more than 2 or 3 views.
|
||||||
|
Another option is to use Visual Format Language (VFL), which is a bit less long winded.
|
||||||
|
However the ASCII type syntax has its own pitfalls and its also a bit harder to animate as `NSLayoutConstraint.constraintsWithVisualFormat` returns an array.
|
||||||
|
|
||||||
|
## Prepare to meet your Maker!
|
||||||
|
|
||||||
|
Heres the same constraints created using ConstraintMaker
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let padding = UIEdgeInsetsMake(10, 10, 10, 10)
|
||||||
|
|
||||||
|
view1.snp_makeConstraints { make in
|
||||||
|
make.top.equalTo(superview.snp_top).with.offset(padding.top) // with is an optional semantic filler
|
||||||
|
make.left.equalTo(superview.snp_left).with.offset(padding.left)
|
||||||
|
make.bottom.equalTo(superview.snp_bottom).with.offset(-padding.bottom)
|
||||||
|
make.right.equalTo(superview.snp_right).with.offset(-padding.right)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Or even shorter
|
||||||
|
|
||||||
|
```swift
|
||||||
|
view1.snp_makeConstraints { make in
|
||||||
|
make.edges.equalTo(superview).with.insets(padding)
|
||||||
|
return // this return is a fix for implicit returns in Swift and is only required for single line constraints
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Also note in the first example we had to add the constraints to the superview `superview.addConstraints`.
|
||||||
|
Snappy however will automagically add constraints to the appropriate view.
|
||||||
|
|
||||||
|
Snappy will also call `view1.setTranslatesAutoresizingMaskIntoConstraints(false)` for you.
|
||||||
|
|
||||||
|
## Not all things are created equal
|
||||||
|
|
||||||
|
> `.equalTo` equivalent to **NSLayoutRelation.Equal**
|
||||||
|
|
||||||
|
> `.lessThanOrEqualTo` equivalent to **NSLayoutRelation.LessThanOrEqual**
|
||||||
|
|
||||||
|
> `.greaterThanOrEqualTo` equivalent to **NSLayoutRelation.GreaterThanOrEqual**
|
||||||
|
|
||||||
|
These three equality constraints accept one argument which can be any of the following:
|
||||||
|
|
||||||
|
#### 1. ViewAttribute
|
||||||
|
|
||||||
|
```swift
|
||||||
|
make.centerX.lessThanOrEqualTo(view2.snp_left)
|
||||||
|
```
|
||||||
|
|
||||||
|
ViewAttribute | NSLayoutAttribute
|
||||||
|
------------------------- | --------------------------
|
||||||
|
view.snp_left | NSLayoutAttribute.Left
|
||||||
|
view.snp_right | NSLayoutAttribute.Right
|
||||||
|
view.snp_top | NSLayoutAttribute.Top
|
||||||
|
view.snp_bottom | NSLayoutAttribute.Bottom
|
||||||
|
view.snp_leading | NSLayoutAttribute.Leading
|
||||||
|
view.snp_trailing | NSLayoutAttribute.Trailing
|
||||||
|
view.snp_width | NSLayoutAttribute.Width
|
||||||
|
view.snp_height | NSLayoutAttribute.Height
|
||||||
|
view.snp_centerX | NSLayoutAttribute.CenterX
|
||||||
|
view.snp_centerY | NSLayoutAttribute.CenterY
|
||||||
|
view.snp_baseline | NSLayoutAttribute.Baseline
|
||||||
|
|
||||||
|
#### 2. UIView/NSView
|
||||||
|
|
||||||
|
if you want view.left to be greater than or equal to label.left :
|
||||||
|
```swift
|
||||||
|
// these two constraints are exactly the same
|
||||||
|
make.left.greaterThanOrEqualTo(label)
|
||||||
|
make.left.greaterThanOrEqualTo(label.snp_left)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Strict Checks
|
||||||
|
|
||||||
|
Auto Layout allows width and height to be set to constant values.
|
||||||
|
if you want to set view to have a minimum and maximum width you could pass a primitive to the equality blocks:
|
||||||
|
```swift
|
||||||
|
// width >= 200 && width <= 400
|
||||||
|
make.width.greaterThanOrEqualTo(200)
|
||||||
|
make.width.lessThanOrEqualTo(400)
|
||||||
|
```
|
||||||
|
|
||||||
|
However Auto Layout does not allow alignment attributes such as left, right, centerY etc to be set to constant values.
|
||||||
|
So if you pass a primitive for these attributes Snappy will turn these into constraints relative to the view’s superview ie:
|
||||||
|
```swift
|
||||||
|
// creates view.left = view.superview.left + 10
|
||||||
|
make.left.lessThanOrEqualTo(10)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use other primitives and structs to build your constraints, like so:
|
||||||
|
```swift
|
||||||
|
make.top.snp_equalTo(42)
|
||||||
|
make.height.snp_equalTo(20)
|
||||||
|
make.size.snp_equalTo(CGSizeMake(50, 100))
|
||||||
|
make.edges.snp_equalTo(UIEdgeInsetsMake(10, 0, 10, 0))
|
||||||
|
make.left.snp_equalTo(view).offset(UIEdgeInsetsMake(10, 0, 10, 0))
|
||||||
|
```
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
## Learn to prioritize
|
||||||
|
|
||||||
|
> `.prority` allows you to specify an exact priority
|
||||||
|
|
||||||
|
> `.priorityHigh` equivalent to **UILayoutPriority.DefaultHigh**
|
||||||
|
|
||||||
|
> `.priorityMedium` is half way between high and low
|
||||||
|
|
||||||
|
> `.priorityLow` equivalent to **UILayoutPriority.DefaultLow**
|
||||||
|
|
||||||
|
Priorities are can be tacked on to the end of a constraint chain like so:
|
||||||
|
```swift
|
||||||
|
make.left.greaterThanOrEqualTo(label.snp_left).with.priorityLow();
|
||||||
|
|
||||||
|
make.top.equalTo(label.snp_top).with.priority(600);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Composition, composition, composition
|
||||||
|
|
||||||
|
Snappy also gives you a few convenience methods which create multiple constraints at the same time.
|
||||||
|
|
||||||
|
#### edges
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// make top, left, bottom, right equal view2
|
||||||
|
make.edges.equalTo(view2);
|
||||||
|
|
||||||
|
// make top = superview.top + 5, left = superview.left + 10,
|
||||||
|
// bottom = superview.bottom - 15, right = superview.right - 20
|
||||||
|
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### size
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// make width and height greater than or equal to titleLabel
|
||||||
|
make.size.greaterThanOrEqualTo(titleLabel)
|
||||||
|
|
||||||
|
// make width = superview.width + 100, height = superview.height - 50
|
||||||
|
make.size.equalTo(superview).offset(CGSizeMake(100, -50))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### center
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// make centerX and centerY = button1
|
||||||
|
make.center.equalTo(button1)
|
||||||
|
|
||||||
|
// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
|
||||||
|
make.center.equalTo(superview).offset(CGPointMake(-5, 10))
|
||||||
|
```
|
||||||
|
|
||||||
|
You can chain view attributes for increased readability:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// All edges but the top should equal those of the superview
|
||||||
|
make.left.right.and.bottom.equalTo(superview)
|
||||||
|
make.top.equalTo(otherView)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hold on for dear life
|
||||||
|
|
||||||
|
Sometimes you need modify existing constraints in order to animate or remove/replace constraints.
|
||||||
|
In Snappy there are a few different approaches to updating constraints.
|
||||||
|
|
||||||
|
#### 1. References
|
||||||
|
You can hold on to a reference of a particular constraint by assigning the result of a constraint make expression to a local variable or a class property.
|
||||||
|
You could also reference multiple constraints by storing them away in an array.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
|
||||||
|
var topConstraint: Constraint? = nil
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
// when making constraints
|
||||||
|
view1.snp_makeConstraints { make in
|
||||||
|
self.topConstraint = make.top.equalTo(superview).with.offset(padding.top)
|
||||||
|
make.left.equalTo(superview).with.offset(padding.left)
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
// then later you can call
|
||||||
|
self.topConstraint.uninstall()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. snp_remakeConstraints
|
||||||
|
|
||||||
|
`snp_remakeConstraints` is similar to `snp_makeConstraints`, but will first remove all existing constraints installed by Snappy.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
func changeButtonPosition() {
|
||||||
|
self.button.snp_remakeConstraints { make in
|
||||||
|
make.size.equalTo(self.buttonSize)
|
||||||
|
|
||||||
|
if topLeft {
|
||||||
|
make.top.left.equalTo(10)
|
||||||
|
} else {
|
||||||
|
make.bottom.equalTo(self.view).offset(-10)
|
||||||
|
make.right.equalTo(self.view).offset(-10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
* Eye candy
|
||||||
|
* Example projects
|
||||||
|
* Tests
|
|
@ -10,13 +10,11 @@
|
||||||
DD03E3151981B52F00E0DE94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E30F1981B52F00E0DE94 /* AppDelegate.swift */; };
|
DD03E3151981B52F00E0DE94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E30F1981B52F00E0DE94 /* AppDelegate.swift */; };
|
||||||
DD03E3171981B52F00E0DE94 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD03E3121981B52F00E0DE94 /* Images.xcassets */; };
|
DD03E3171981B52F00E0DE94 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD03E3121981B52F00E0DE94 /* Images.xcassets */; };
|
||||||
DD03E3191981B52F00E0DE94 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3141981B52F00E0DE94 /* ViewController.swift */; };
|
DD03E3191981B52F00E0DE94 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3141981B52F00E0DE94 /* ViewController.swift */; };
|
||||||
DD03E3201981B70D00E0DE94 /* CompositeConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E31F1981B70D00E0DE94 /* CompositeConstraint.swift */; };
|
|
||||||
DD03E3221981B71C00E0DE94 /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3211981B71C00E0DE94 /* Constraint.swift */; };
|
DD03E3221981B71C00E0DE94 /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3211981B71C00E0DE94 /* Constraint.swift */; };
|
||||||
DD03E3241981B72A00E0DE94 /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */; };
|
DD03E3241981B72A00E0DE94 /* ConstraintMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */; };
|
||||||
DD03E3261981B78D00E0DE94 /* ViewAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3251981B78D00E0DE94 /* ViewAttribute.swift */; };
|
|
||||||
DD03E3281981B79C00E0DE94 /* ViewConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3271981B79C00E0DE94 /* ViewConstraint.swift */; };
|
|
||||||
DD03E32A1981B7BF00E0DE94 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3291981B7BF00E0DE94 /* View.swift */; };
|
DD03E32A1981B7BF00E0DE94 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E3291981B7BF00E0DE94 /* View.swift */; };
|
||||||
DDC9FDAE1981B4DD009612C7 /* SnappyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC9FDAD1981B4DD009612C7 /* SnappyTests.swift */; };
|
DDC9FDAE1981B4DD009612C7 /* SnappyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC9FDAD1981B4DD009612C7 /* SnappyTests.swift */; };
|
||||||
|
EEC6EB4E1985370500C27B12 /* LayoutConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC6EB4D1985370500C27B12 /* LayoutConstraint.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -48,15 +46,13 @@
|
||||||
DD03E3121981B52F00E0DE94 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
DD03E3121981B52F00E0DE94 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||||
DD03E3131981B52F00E0DE94 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
DD03E3131981B52F00E0DE94 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
DD03E3141981B52F00E0DE94 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
DD03E3141981B52F00E0DE94 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||||
DD03E31F1981B70D00E0DE94 /* CompositeConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompositeConstraint.swift; sourceTree = "<group>"; };
|
|
||||||
DD03E3211981B71C00E0DE94 /* Constraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constraint.swift; sourceTree = "<group>"; };
|
DD03E3211981B71C00E0DE94 /* Constraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constraint.swift; sourceTree = "<group>"; };
|
||||||
DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintMaker.swift; sourceTree = "<group>"; };
|
DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstraintMaker.swift; sourceTree = "<group>"; };
|
||||||
DD03E3251981B78D00E0DE94 /* ViewAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewAttribute.swift; sourceTree = "<group>"; };
|
|
||||||
DD03E3271981B79C00E0DE94 /* ViewConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewConstraint.swift; sourceTree = "<group>"; };
|
|
||||||
DD03E3291981B7BF00E0DE94 /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
DD03E3291981B7BF00E0DE94 /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
||||||
DDC9FD951981B4DD009612C7 /* Snappy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Snappy.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
DDC9FD951981B4DD009612C7 /* Snappy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Snappy.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DDC9FDA71981B4DD009612C7 /* SnappyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SnappyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
DDC9FDA71981B4DD009612C7 /* SnappyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SnappyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
DDC9FDAD1981B4DD009612C7 /* SnappyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnappyTests.swift; sourceTree = "<group>"; };
|
DDC9FDAD1981B4DD009612C7 /* SnappyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnappyTests.swift; sourceTree = "<group>"; };
|
||||||
|
EEC6EB4D1985370500C27B12 /* LayoutConstraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutConstraint.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -91,11 +87,9 @@
|
||||||
DD03E31A1981B62D00E0DE94 /* Snappy */ = {
|
DD03E31A1981B62D00E0DE94 /* Snappy */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DD03E31F1981B70D00E0DE94 /* CompositeConstraint.swift */,
|
EEC6EB4D1985370500C27B12 /* LayoutConstraint.swift */,
|
||||||
DD03E3211981B71C00E0DE94 /* Constraint.swift */,
|
DD03E3211981B71C00E0DE94 /* Constraint.swift */,
|
||||||
DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */,
|
DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */,
|
||||||
DD03E3251981B78D00E0DE94 /* ViewAttribute.swift */,
|
|
||||||
DD03E3271981B79C00E0DE94 /* ViewConstraint.swift */,
|
|
||||||
DD03E3291981B7BF00E0DE94 /* View.swift */,
|
DD03E3291981B7BF00E0DE94 /* View.swift */,
|
||||||
);
|
);
|
||||||
path = Snappy;
|
path = Snappy;
|
||||||
|
@ -230,12 +224,10 @@
|
||||||
files = (
|
files = (
|
||||||
DD03E32A1981B7BF00E0DE94 /* View.swift in Sources */,
|
DD03E32A1981B7BF00E0DE94 /* View.swift in Sources */,
|
||||||
DD03E3221981B71C00E0DE94 /* Constraint.swift in Sources */,
|
DD03E3221981B71C00E0DE94 /* Constraint.swift in Sources */,
|
||||||
|
EEC6EB4E1985370500C27B12 /* LayoutConstraint.swift in Sources */,
|
||||||
DD03E3241981B72A00E0DE94 /* ConstraintMaker.swift in Sources */,
|
DD03E3241981B72A00E0DE94 /* ConstraintMaker.swift in Sources */,
|
||||||
DD03E3191981B52F00E0DE94 /* ViewController.swift in Sources */,
|
DD03E3191981B52F00E0DE94 /* ViewController.swift in Sources */,
|
||||||
DD03E3281981B79C00E0DE94 /* ViewConstraint.swift in Sources */,
|
|
||||||
DD03E3201981B70D00E0DE94 /* CompositeConstraint.swift in Sources */,
|
|
||||||
DD03E3151981B52F00E0DE94 /* AppDelegate.swift in Sources */,
|
DD03E3151981B52F00E0DE94 /* AppDelegate.swift in Sources */,
|
||||||
DD03E3261981B78D00E0DE94 /* ViewAttribute.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
//
|
|
||||||
// CompositeConstraint.swift
|
|
||||||
// Snappy
|
|
||||||
//
|
|
||||||
// Created by Jonas Budelmann on 25/07/14.
|
|
||||||
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class CompositeConstraint: Constraint, ConstraintDelegate {
|
|
||||||
var children = [Constraint]()
|
|
||||||
weak var delegate: ConstraintDelegate?
|
|
||||||
|
|
||||||
init(children: [Constraint]) {
|
|
||||||
self.children = children
|
|
||||||
for constraint in children {
|
|
||||||
constraint.delegate = self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func constraint(constraint: Constraint, shouldBeReplacedWithConstraint replacementConstraint: Constraint) {
|
|
||||||
var index: Int?
|
|
||||||
for (i, c) in enumerate(self.children) {
|
|
||||||
if (c === constraint) {
|
|
||||||
index = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index) {
|
|
||||||
self.children[index!] = replacementConstraint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func constraint(constraint: Constraint?, addConstraintWithLayoutAttribute layoutAttribute: NSLayoutAttribute) -> Constraint {
|
|
||||||
var newConstraint = self.delegate!.constraint(self, addConstraintWithLayoutAttribute: layoutAttribute)
|
|
||||||
newConstraint.delegate = self
|
|
||||||
self.children.append(newConstraint)
|
|
||||||
return newConstraint
|
|
||||||
}
|
|
||||||
|
|
||||||
var left: Constraint { return addConstraint(.Left) }
|
|
||||||
var top: Constraint { return addConstraint(.Top) }
|
|
||||||
var right: Constraint { return addConstraint(.Right) }
|
|
||||||
var bottom: Constraint { return addConstraint(.Bottom) }
|
|
||||||
var leading: Constraint { return addConstraint(.Leading) }
|
|
||||||
var trailing: Constraint { return addConstraint(.Trailing) }
|
|
||||||
var width: Constraint { return addConstraint(.Width) }
|
|
||||||
var height: Constraint { return addConstraint(.Height) }
|
|
||||||
var centerX: Constraint { return addConstraint(.CenterX) }
|
|
||||||
var centerY: Constraint { return addConstraint(.CenterY) }
|
|
||||||
var baseline: Constraint { return addConstraint(.Baseline) }
|
|
||||||
|
|
||||||
var and: Constraint { return self }
|
|
||||||
var with: Constraint { return self }
|
|
||||||
|
|
||||||
func addConstraint(NSLayoutAttribute) -> Constraint {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalTo(attr: Any) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func greaterThanOrEqualTo(attr: Any) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func lessThanOrEqualTo(attr: Any) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func offset(offset: Any) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func insets(insets: UIEdgeInsets) -> Constraint {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
func multipliedBy(multiplier: Float) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func dividedBy(divider: Float) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priority(priority: UILayoutPriority) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priorityLow() -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priorityMedium() -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priorityHigh() -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func install() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,60 +2,549 @@
|
||||||
// Constraint.swift
|
// Constraint.swift
|
||||||
// Snappy
|
// Snappy
|
||||||
//
|
//
|
||||||
// Created by Jonas Budelmann on 25/07/14.
|
// Copyright (c) 2011-2014 Masonry Team - https://github.com/Masonry
|
||||||
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
|
|
||||||
//
|
//
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@class_protocol protocol ConstraintDelegate {
|
/**
|
||||||
func constraint(constraint: Constraint, shouldBeReplacedWithConstraint replacementConstraint: Constraint)
|
* ConstraintAttributes is an options set that maps to NSLayoutAttributes.
|
||||||
func constraint(constraint: Constraint?, addConstraintWithLayoutAttribute layoutAttribute: NSLayoutAttribute) -> Constraint
|
*/
|
||||||
|
struct ConstraintAttributes: RawOptionSet {
|
||||||
|
|
||||||
|
var value: UInt
|
||||||
|
|
||||||
|
init(_ value: UInt) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
func toRaw() -> UInt { return self.value }
|
||||||
|
func getLogicValue() -> Bool { return self.value != 0 }
|
||||||
|
static func fromRaw(raw: UInt) -> ConstraintAttributes? { return self(raw) }
|
||||||
|
static func fromMask(raw: UInt) -> ConstraintAttributes { return self(raw) }
|
||||||
|
static func convertFromNilLiteral() -> ConstraintAttributes { return self(0) }
|
||||||
|
|
||||||
|
static var None: ConstraintAttributes { return self(0) }
|
||||||
|
static var Left: ConstraintAttributes { return self(1) }
|
||||||
|
static var Top: ConstraintAttributes { return self(2) }
|
||||||
|
static var Right: ConstraintAttributes { return self(4) }
|
||||||
|
static var Bottom: ConstraintAttributes { return self(8) }
|
||||||
|
static var Leading: ConstraintAttributes { return self(16) }
|
||||||
|
static var Trailing: ConstraintAttributes { return self(32) }
|
||||||
|
static var Width: ConstraintAttributes { return self(64) }
|
||||||
|
static var Height: ConstraintAttributes { return self(128) }
|
||||||
|
static var CenterX: ConstraintAttributes { return self(256) }
|
||||||
|
static var CenterY: ConstraintAttributes { return self(512) }
|
||||||
|
static var Baseline: ConstraintAttributes { return self(1024) }
|
||||||
|
|
||||||
|
static var Edges: ConstraintAttributes { return self(15) }
|
||||||
|
static var Size: ConstraintAttributes { return self(192) }
|
||||||
|
static var Center: ConstraintAttributes { return self(768) }
|
||||||
|
|
||||||
|
var layoutAttributes:Array<NSLayoutAttribute> {
|
||||||
|
var attrs: Array<NSLayoutAttribute> = []
|
||||||
|
if (self & ConstraintAttributes.Left) {
|
||||||
|
attrs.append(.Left)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Top) {
|
||||||
|
attrs.append(.Top)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Right) {
|
||||||
|
attrs.append(.Right)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Bottom) {
|
||||||
|
attrs.append(.Bottom)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Leading) {
|
||||||
|
attrs.append(.Leading)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Trailing) {
|
||||||
|
attrs.append(.Trailing)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Width) {
|
||||||
|
attrs.append(.Width)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Height ){
|
||||||
|
attrs.append(.Height)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.CenterX) {
|
||||||
|
attrs.append(.CenterX)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.CenterY) {
|
||||||
|
attrs.append(.CenterY)
|
||||||
|
}
|
||||||
|
if (self & ConstraintAttributes.Baseline) {
|
||||||
|
attrs.append(.Baseline)
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@assignment func += (inout left: ConstraintAttributes, right: ConstraintAttributes) {
|
||||||
|
left = (left | right)
|
||||||
|
}
|
||||||
|
@assignment func -= (inout left: ConstraintAttributes, right: ConstraintAttributes) {
|
||||||
|
left = ConstraintAttributes(left.toRaw() & ~right.toRaw())
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias Delegate = ConstraintDelegate?
|
/**
|
||||||
|
* ConstraintRelation is an Int enum that maps to NSLayoutRelation.
|
||||||
|
*/
|
||||||
|
enum ConstraintRelation: Int {
|
||||||
|
case Equal = 1, LessThanOrEqualTo, GreaterThanOrEqualTo
|
||||||
|
|
||||||
@class_protocol protocol Constraint {
|
var layoutRelation: NSLayoutRelation {
|
||||||
weak var delegate: Delegate { get set }
|
get {
|
||||||
|
switch(self) {
|
||||||
var left: Constraint { get }
|
case .LessThanOrEqualTo:
|
||||||
var top: Constraint { get }
|
return .LessThanOrEqual
|
||||||
var right: Constraint { get }
|
case .GreaterThanOrEqualTo:
|
||||||
var bottom: Constraint { get }
|
return .GreaterThanOrEqual
|
||||||
var leading: Constraint { get }
|
default:
|
||||||
var trailing: Constraint { get }
|
return .Equal
|
||||||
var width: Constraint { get }
|
}
|
||||||
var height: Constraint { get }
|
}
|
||||||
var centerX: Constraint { get }
|
}
|
||||||
var centerY: Constraint { get }
|
}
|
||||||
var baseline: Constraint { get }
|
|
||||||
|
/**
|
||||||
var and: Constraint { get }
|
* ConstraintItem is a class that is used while building constraints.
|
||||||
var with: Constraint { get }
|
*/
|
||||||
|
class ConstraintItem {
|
||||||
func addConstraint(NSLayoutAttribute) -> Constraint
|
|
||||||
|
init(view: View?, attributes: ConstraintAttributes) {
|
||||||
func equalTo(attr: Any) -> Constraint
|
self.view = view
|
||||||
|
self.attributes = attributes
|
||||||
func greaterThanOrEqualTo(attr: Any) -> Constraint
|
}
|
||||||
|
|
||||||
func lessThanOrEqualTo(attr: Any) -> Constraint
|
internal weak var view: View?
|
||||||
|
internal var attributes: ConstraintAttributes
|
||||||
func insets(insets: UIEdgeInsets) -> Constraint
|
}
|
||||||
|
|
||||||
func offset(offset: Any) -> Constraint
|
/**
|
||||||
|
* Constraint is a single item that defines all the properties for a single ConstraintMaker chain
|
||||||
func multipliedBy(multiplier: Float) -> Constraint
|
*/
|
||||||
|
class Constraint {
|
||||||
func dividedBy(divider: Float) -> Constraint
|
var left: Constraint { return addConstraint(ConstraintAttributes.Left) }
|
||||||
|
var top: Constraint { return addConstraint(ConstraintAttributes.Top) }
|
||||||
func priority(priority: UILayoutPriority) -> Constraint
|
var right: Constraint { return addConstraint(ConstraintAttributes.Right) }
|
||||||
|
var bottom: Constraint { return addConstraint(ConstraintAttributes.Bottom) }
|
||||||
func priorityLow() -> Constraint
|
var leading: Constraint { return addConstraint(ConstraintAttributes.Leading) }
|
||||||
|
var trailing: Constraint { return addConstraint(ConstraintAttributes.Trailing) }
|
||||||
func priorityMedium() -> Constraint
|
var width: Constraint { return addConstraint(ConstraintAttributes.Width) }
|
||||||
|
var height: Constraint { return addConstraint(ConstraintAttributes.Height) }
|
||||||
func priorityHigh() -> Constraint
|
var centerX: Constraint { return addConstraint(ConstraintAttributes.CenterX) }
|
||||||
|
var centerY: Constraint { return addConstraint(ConstraintAttributes.CenterY) }
|
||||||
func install()
|
var baseline: Constraint { return addConstraint(ConstraintAttributes.Baseline) }
|
||||||
|
|
||||||
|
var and: Constraint { return self }
|
||||||
|
var with: Constraint { return self }
|
||||||
|
|
||||||
|
// MARK: initializer
|
||||||
|
|
||||||
|
init(fromItem: ConstraintItem) {
|
||||||
|
self.fromItem = fromItem
|
||||||
|
self.toItem = ConstraintItem(view: nil, attributes: ConstraintAttributes.None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: equalTo
|
||||||
|
|
||||||
|
func equalTo(other: ConstraintItem) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .Equal)
|
||||||
|
}
|
||||||
|
func equalTo(other: View) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .Equal)
|
||||||
|
}
|
||||||
|
func equalTo(other: Float) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .Equal)
|
||||||
|
}
|
||||||
|
func equalTo(other: Int) -> Constraint {
|
||||||
|
return constrainTo(Float(other), relation: .Equal)
|
||||||
|
}
|
||||||
|
func equalTo(other: CGSize) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .Equal)
|
||||||
|
}
|
||||||
|
func equalTo(other: CGPoint) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .Equal)
|
||||||
|
}
|
||||||
|
func equalTo(other: UIEdgeInsets) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .Equal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: lessThanOrEqualTo
|
||||||
|
|
||||||
|
func lessThanOrEqualTo(other: ConstraintItem) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func lessThanOrEqualTo(other: View) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func lessThanOrEqualTo(other: Float) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func lessThanOrEqualTo(other: Int) -> Constraint {
|
||||||
|
return constrainTo(Float(other), relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func lessThanOrEqualTo(other: CGSize) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func lessThanOrEqualTo(other: CGPoint) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func lessThanOrEqualTo(other: UIEdgeInsets) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .LessThanOrEqualTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: greaterThanOrEqualTo
|
||||||
|
|
||||||
|
func greaterThanOrEqualTo(other: ConstraintItem) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func greaterThanOrEqualTo(other: View) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func greaterThanOrEqualTo(other: Float) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func greaterThanOrEqualTo(other: Int) -> Constraint {
|
||||||
|
return constrainTo(Float(other), relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func greaterThanOrEqualTo(other: CGSize) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func greaterThanOrEqualTo(other: CGPoint) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
func greaterThanOrEqualTo(other: UIEdgeInsets) -> Constraint {
|
||||||
|
return constrainTo(other, relation: .GreaterThanOrEqualTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: multiplier
|
||||||
|
|
||||||
|
func multipliedBy(amount: Float) -> Constraint {
|
||||||
|
self.multiplier = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
func dividedBy(amount: Float) -> Constraint {
|
||||||
|
self.multiplier = 1.0 / amount;
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: priority
|
||||||
|
|
||||||
|
func priority(priority: Float) -> Constraint {
|
||||||
|
self.priority = priority
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
func priorityRequired() -> Constraint {
|
||||||
|
return priority(1000.0)
|
||||||
|
}
|
||||||
|
func priorityHigh() -> Constraint {
|
||||||
|
return priority(750.0)
|
||||||
|
}
|
||||||
|
func priorityLow() -> Constraint {
|
||||||
|
return priority(250.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: offset
|
||||||
|
|
||||||
|
func offset(amount: Float) -> Constraint {
|
||||||
|
self.offset = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
func offset(amount: Int) -> Constraint {
|
||||||
|
self.offset = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
func offset(amount: CGPoint) -> Constraint {
|
||||||
|
self.offset = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
func offset(amount: CGSize) -> Constraint {
|
||||||
|
self.offset = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
func offset(amount: UIEdgeInsets) -> Constraint {
|
||||||
|
self.offset = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: insets
|
||||||
|
|
||||||
|
func insets(amount: UIEdgeInsets) -> Constraint {
|
||||||
|
self.insets = amount
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: install
|
||||||
|
|
||||||
|
func install() -> Array<LayoutConstraint> {
|
||||||
|
var installOnView: View? = nil
|
||||||
|
if self.toItem.view {
|
||||||
|
installOnView = Constraint.closestCommonSuperviewFromView(self.fromItem.view, toView: self.toItem.view)
|
||||||
|
if !installOnView {
|
||||||
|
NSException(name: "Cannot Install Constraint", reason: "No common superview between views", userInfo: nil).raise()
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
installOnView = self.fromItem.view?.superview
|
||||||
|
if !installOnView {
|
||||||
|
NSException(name: "Cannot Install Constraint", reason: "Missing superview", userInfo: nil).raise()
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var layoutConstraints: Array<LayoutConstraint> = []
|
||||||
|
let layoutFromAttributes = self.fromItem.attributes.layoutAttributes
|
||||||
|
let layoutToAttributes = self.toItem.attributes.layoutAttributes
|
||||||
|
|
||||||
|
// get layout from
|
||||||
|
let layoutFrom: View? = self.fromItem.view
|
||||||
|
|
||||||
|
// get layout to
|
||||||
|
let layoutTo: View? = self.toItem.view
|
||||||
|
|
||||||
|
// get layout relation
|
||||||
|
let layoutRelation: NSLayoutRelation = (self.relation) ? self.relation!.layoutRelation : .Equal
|
||||||
|
|
||||||
|
for layoutFromAttribute in layoutFromAttributes {
|
||||||
|
// get layout to attribute
|
||||||
|
let layoutToAttribute = (layoutToAttributes.count > 0) ? layoutToAttributes[0] : layoutFromAttribute
|
||||||
|
|
||||||
|
// get layout constant
|
||||||
|
var layoutConstant: CGFloat = layoutToAttribute.snp_constantForValue(self.constant)
|
||||||
|
layoutConstant += layoutToAttribute.snp_offsetForValue(self.offset)
|
||||||
|
|
||||||
|
// create layout constraint
|
||||||
|
let layoutConstraint = LayoutConstraint(
|
||||||
|
item: layoutFrom,
|
||||||
|
attribute: layoutFromAttribute,
|
||||||
|
relatedBy: layoutRelation,
|
||||||
|
toItem: layoutTo,
|
||||||
|
attribute: layoutToAttribute,
|
||||||
|
multiplier: CGFloat(self.multiplier),
|
||||||
|
constant: layoutConstant)
|
||||||
|
|
||||||
|
// set priority
|
||||||
|
layoutConstraint.priority = self.priority
|
||||||
|
|
||||||
|
// set constraint
|
||||||
|
layoutConstraint.constraint = self
|
||||||
|
|
||||||
|
layoutConstraints.append(layoutConstraint)
|
||||||
|
}
|
||||||
|
|
||||||
|
installOnView?.addConstraints(layoutConstraints)
|
||||||
|
|
||||||
|
self.installedOnView = installOnView
|
||||||
|
return layoutConstraints
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: uninstall
|
||||||
|
|
||||||
|
func uninstall() {
|
||||||
|
if let view = self.installedOnView {
|
||||||
|
var installedConstraints = view.constraints()
|
||||||
|
var constraintsToRemove: Array<LayoutConstraint> = []
|
||||||
|
for installedConstraint in installedConstraints {
|
||||||
|
if let layoutConstraint = installedConstraint as? LayoutConstraint {
|
||||||
|
if layoutConstraint.constraint === self {
|
||||||
|
constraintsToRemove.append(layoutConstraint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constraintsToRemove.count > 0 {
|
||||||
|
view.removeConstraints(constraintsToRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.installedOnView = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: private
|
||||||
|
|
||||||
|
private let fromItem: ConstraintItem
|
||||||
|
private var toItem: ConstraintItem
|
||||||
|
private var relation: ConstraintRelation?
|
||||||
|
private var constant: Any?
|
||||||
|
private var multiplier: Float = 1.0
|
||||||
|
private var priority: Float = 1000.0
|
||||||
|
private var offset: Any?
|
||||||
|
private var insets: UIEdgeInsets = UIEdgeInsetsZero
|
||||||
|
|
||||||
|
private weak var installedOnView: View?
|
||||||
|
|
||||||
|
private func addConstraint(attributes: ConstraintAttributes) -> Constraint {
|
||||||
|
if !self.relation {
|
||||||
|
self.fromItem.attributes += attributes
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
private func constrainTo(other: ConstraintItem, relation: ConstraintRelation) -> Constraint {
|
||||||
|
if other.attributes != ConstraintAttributes.None {
|
||||||
|
var toLayoutAttributes = other.attributes.layoutAttributes
|
||||||
|
if toLayoutAttributes.count > 1 {
|
||||||
|
var fromLayoutAttributes = self.fromItem.attributes.layoutAttributes
|
||||||
|
if toLayoutAttributes != fromLayoutAttributes {
|
||||||
|
NSException(name: "Invalid Constraint", reason: "Cannot constrain to multiple non identical attributes", userInfo: nil).raise()
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
other.attributes = ConstraintAttributes.None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.toItem = other
|
||||||
|
self.relation = relation
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
private func constrainTo(other: View, relation: ConstraintRelation) -> Constraint {
|
||||||
|
return constrainTo(ConstraintItem(view: other, attributes: ConstraintAttributes.None), relation: relation)
|
||||||
|
}
|
||||||
|
private func constrainTo(other: Float, relation: ConstraintRelation) -> Constraint {
|
||||||
|
self.constant = other
|
||||||
|
return constrainTo(ConstraintItem(view: nil, attributes: ConstraintAttributes.None), relation: relation)
|
||||||
|
}
|
||||||
|
private func constrainTo(other: CGSize, relation: ConstraintRelation) -> Constraint {
|
||||||
|
self.constant = other
|
||||||
|
return constrainTo(ConstraintItem(view: nil, attributes: ConstraintAttributes.None), relation: relation)
|
||||||
|
}
|
||||||
|
private func constrainTo(other: CGPoint, relation: ConstraintRelation) -> Constraint {
|
||||||
|
self.constant = other
|
||||||
|
return constrainTo(ConstraintItem(view: nil, attributes: ConstraintAttributes.None), relation: relation)
|
||||||
|
}
|
||||||
|
private func constrainTo(other: UIEdgeInsets, relation: ConstraintRelation) -> Constraint {
|
||||||
|
self.constant = other
|
||||||
|
return constrainTo(ConstraintItem(view: nil, attributes: ConstraintAttributes.None), relation: relation)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class func closestCommonSuperviewFromView(fromView: View?, toView: View?) -> View? {
|
||||||
|
var closestCommonSuperview: View?
|
||||||
|
var secondViewSuperview: View? = toView
|
||||||
|
while !closestCommonSuperview && secondViewSuperview {
|
||||||
|
var firstViewSuperview = fromView
|
||||||
|
while !closestCommonSuperview && firstViewSuperview {
|
||||||
|
if secondViewSuperview == firstViewSuperview {
|
||||||
|
closestCommonSuperview = secondViewSuperview
|
||||||
|
}
|
||||||
|
firstViewSuperview = firstViewSuperview?.superview
|
||||||
|
}
|
||||||
|
secondViewSuperview = secondViewSuperview?.superview
|
||||||
|
}
|
||||||
|
return closestCommonSuperview
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension NSLayoutAttribute {
|
||||||
|
|
||||||
|
func snp_offsetForValue(value: Any?) -> CGFloat {
|
||||||
|
// Float
|
||||||
|
if let float = value as? Float {
|
||||||
|
return CGFloat(float)
|
||||||
|
}
|
||||||
|
// Int
|
||||||
|
else if let int = value as? Int {
|
||||||
|
return CGFloat(int)
|
||||||
|
}
|
||||||
|
// CGFloat
|
||||||
|
else if let float = value as? CGFloat {
|
||||||
|
return float
|
||||||
|
}
|
||||||
|
// CGSize
|
||||||
|
else if let size = value as? CGSize {
|
||||||
|
if self == .Width {
|
||||||
|
return size.width
|
||||||
|
} else if self == .Height {
|
||||||
|
return size.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CGPoint
|
||||||
|
else if let point = value as? CGPoint {
|
||||||
|
if self == .Left || self == .CenterX {
|
||||||
|
return point.x
|
||||||
|
} else if self == .Top || self == .CenterY {
|
||||||
|
return point.y
|
||||||
|
} else if self == .Right {
|
||||||
|
return -point.x
|
||||||
|
} else if self == .Bottom {
|
||||||
|
return -point.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// UIEdgeInsets
|
||||||
|
else if let insets = value as? UIEdgeInsets {
|
||||||
|
if self == .Left {
|
||||||
|
return insets.left
|
||||||
|
} else if self == .Top {
|
||||||
|
return insets.top
|
||||||
|
} else if self == .Right {
|
||||||
|
return -insets.right
|
||||||
|
} else if self == .Bottom {
|
||||||
|
return -insets.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CGFloat(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func snp_constantForValue(value: Any?) -> CGFloat {
|
||||||
|
// Float
|
||||||
|
if let float = value as? Float {
|
||||||
|
return CGFloat(float)
|
||||||
|
}
|
||||||
|
// Int
|
||||||
|
else if let int = value as? Int {
|
||||||
|
return CGFloat(int)
|
||||||
|
}
|
||||||
|
// CGFloat
|
||||||
|
else if let float = value as? CGFloat {
|
||||||
|
return float
|
||||||
|
}
|
||||||
|
// CGSize
|
||||||
|
else if let size = value as? CGSize {
|
||||||
|
if self == .Width {
|
||||||
|
return size.width
|
||||||
|
} else if self == .Height {
|
||||||
|
return size.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CGPoint
|
||||||
|
else if let point = value as? CGPoint {
|
||||||
|
if self == .Left || self == .CenterX {
|
||||||
|
return point.x
|
||||||
|
} else if self == .Top || self == .CenterY {
|
||||||
|
return point.y
|
||||||
|
} else if self == .Right {
|
||||||
|
return point.x
|
||||||
|
} else if self == .Bottom {
|
||||||
|
return point.y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// UIEdgeInsets
|
||||||
|
else if let insets = value as? UIEdgeInsets {
|
||||||
|
if self == .Left {
|
||||||
|
return insets.left
|
||||||
|
} else if self == .Top {
|
||||||
|
return insets.top
|
||||||
|
} else if self == .Right {
|
||||||
|
return insets.right
|
||||||
|
} else if self == .Bottom {
|
||||||
|
return insets.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CGFloat(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,91 +2,89 @@
|
||||||
// ConstraintMaker.swift
|
// ConstraintMaker.swift
|
||||||
// Snappy
|
// Snappy
|
||||||
//
|
//
|
||||||
// Created by Jonas Budelmann on 25/07/14.
|
// Copyright (c) 2011-2014 Masonry Team - https://github.com/Masonry
|
||||||
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
|
|
||||||
//
|
//
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class ConstraintMaker: ConstraintDelegate {
|
/**
|
||||||
var constraints = [Constraint]()
|
* ConstraintMaker is the maker in snappy that gets all constraints kickstarted
|
||||||
weak var view: View?
|
*/
|
||||||
|
class ConstraintMaker {
|
||||||
|
var left: Constraint { return addConstraint(ConstraintAttributes.Left) }
|
||||||
|
var top: Constraint { return addConstraint(ConstraintAttributes.Top) }
|
||||||
|
var right: Constraint { return addConstraint(ConstraintAttributes.Right) }
|
||||||
|
var bottom: Constraint { return addConstraint(ConstraintAttributes.Bottom) }
|
||||||
|
var leading: Constraint { return addConstraint(ConstraintAttributes.Leading) }
|
||||||
|
var trailing: Constraint { return addConstraint(ConstraintAttributes.Trailing) }
|
||||||
|
var width: Constraint { return addConstraint(ConstraintAttributes.Width) }
|
||||||
|
var height: Constraint { return addConstraint(ConstraintAttributes.Height) }
|
||||||
|
var centerX: Constraint { return addConstraint(ConstraintAttributes.CenterX) }
|
||||||
|
var centerY: Constraint { return addConstraint(ConstraintAttributes.CenterY) }
|
||||||
|
var baseline: Constraint { return addConstraint(ConstraintAttributes.Baseline) }
|
||||||
|
|
||||||
|
var edges: Constraint { return addConstraint(ConstraintAttributes.Edges) }
|
||||||
|
var size: Constraint { return addConstraint(ConstraintAttributes.Size) }
|
||||||
|
var center: Constraint { return addConstraint(ConstraintAttributes.Center) }
|
||||||
|
|
||||||
init(view: View) {
|
init(view: View) {
|
||||||
self.view = view
|
self.view = view
|
||||||
}
|
}
|
||||||
|
|
||||||
var left: Constraint { return addConstraint(.Left) }
|
internal weak var view: View?
|
||||||
var top: Constraint { return addConstraint(.Top) }
|
internal var constraints = Array<Constraint>()
|
||||||
var right: Constraint { return addConstraint(.Right) }
|
|
||||||
var bottom: Constraint { return addConstraint(.Bottom) }
|
|
||||||
var leading: Constraint { return addConstraint(.Leading) }
|
|
||||||
var trailing: Constraint { return addConstraint(.Trailing) }
|
|
||||||
var width: Constraint { return addConstraint(.Width) }
|
|
||||||
var height: Constraint { return addConstraint(.Height) }
|
|
||||||
var centerX: Constraint { return addConstraint(.CenterX) }
|
|
||||||
var centerY: Constraint { return addConstraint(.CenterY) }
|
|
||||||
var baseline: Constraint { return addConstraint(.Baseline) }
|
|
||||||
|
|
||||||
//TODO
|
internal func addConstraint(attributes: ConstraintAttributes) -> Constraint {
|
||||||
var edges: Constraint { return addConstraints(.Top, .Left, .Bottom, .Right) }
|
let item = ConstraintItem(view: self.view, attributes: attributes)
|
||||||
var size: Constraint { return addConstraints(.Width, .Height) }
|
let constraint = Constraint(fromItem: item)
|
||||||
var center: Constraint { return addConstraints(.CenterX, .CenterY) }
|
|
||||||
|
|
||||||
func install() {
|
|
||||||
for constraint in constraints {
|
|
||||||
constraint.install()
|
|
||||||
}
|
|
||||||
self.constraints.removeAll(keepCapacity: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addConstraint(layoutAttribute: NSLayoutAttribute) -> Constraint {
|
|
||||||
return constraint(nil, addConstraintWithLayoutAttribute: layoutAttribute)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func addConstraints(layoutAttributes: NSLayoutAttribute...) -> Constraint {
|
|
||||||
var children = layoutAttributes.map({ (attr: NSLayoutAttribute) -> Constraint in
|
|
||||||
var viewAttribute = ViewAttribute(view: self.view, layoutAttribute: attr)
|
|
||||||
return ViewConstraint(view: self.view!, firstViewAttribute: viewAttribute)
|
|
||||||
})
|
|
||||||
|
|
||||||
var constraint = CompositeConstraint(children: children)
|
|
||||||
constraint.delegate = self
|
|
||||||
self.constraints.append(constraint)
|
self.constraints.append(constraint)
|
||||||
|
|
||||||
return constraint
|
return constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
func constraint(constraint: Constraint, shouldBeReplacedWithConstraint replacementConstraint: Constraint) {
|
internal class func makeConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
|
||||||
var index: Int?
|
view.setTranslatesAutoresizingMaskIntoConstraints(false)
|
||||||
for (i, c) in enumerate(self.constraints) {
|
let maker = ConstraintMaker(view: view)
|
||||||
if (c === constraint) {
|
block(make: maker)
|
||||||
index = i
|
|
||||||
|
var layoutConstraints: Array<LayoutConstraint> = []
|
||||||
|
for constraint in maker.constraints {
|
||||||
|
layoutConstraints += constraint.install()
|
||||||
}
|
}
|
||||||
|
LayoutConstraint.setLayoutConstraints(layoutConstraints, installedOnView: view)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index) {
|
internal class func remakeConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
|
||||||
self.constraints[index!] = replacementConstraint
|
view.setTranslatesAutoresizingMaskIntoConstraints(false)
|
||||||
}
|
let maker = ConstraintMaker(view: view)
|
||||||
}
|
block(make: maker)
|
||||||
|
|
||||||
func constraint(constraint: Constraint?, addConstraintWithLayoutAttribute layoutAttribute: NSLayoutAttribute) -> Constraint {
|
var layoutConstraints: Array<LayoutConstraint> = LayoutConstraint.layoutConstraintsInstalledOnView(view)
|
||||||
var viewAttribute = ViewAttribute(view: self.view, layoutAttribute: layoutAttribute)
|
for existingLayoutConstraint in layoutConstraints {
|
||||||
var newConstraint = ViewConstraint(view: self.view!, firstViewAttribute: viewAttribute)
|
existingLayoutConstraint.constraint?.uninstall()
|
||||||
|
}
|
||||||
|
layoutConstraints = []
|
||||||
|
|
||||||
if let viewConstraint = constraint as? ViewConstraint {
|
for constraint in maker.constraints {
|
||||||
//replace with composite constraint
|
layoutConstraints += constraint.install()
|
||||||
var children = [viewConstraint, newConstraint]
|
|
||||||
var compositeConstraint = CompositeConstraint(children: children)
|
|
||||||
compositeConstraint.delegate = self
|
|
||||||
self.constraint(viewConstraint, shouldBeReplacedWithConstraint:compositeConstraint);
|
|
||||||
return compositeConstraint
|
|
||||||
}
|
}
|
||||||
if (!constraint) {
|
LayoutConstraint.setLayoutConstraints(layoutConstraints, installedOnView: view)
|
||||||
newConstraint.delegate = self
|
|
||||||
self.constraints.append(newConstraint)
|
|
||||||
}
|
|
||||||
return newConstraint
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// LayoutConstraint.swift
|
||||||
|
// Snappy
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011-2014 Masonry Team - https://github.com/Masonry
|
||||||
|
//
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LayoutConstraint is a subclass of NSLayoutConstraint to assist Snappy and also provide better debugging
|
||||||
|
*/
|
||||||
|
class LayoutConstraint: NSLayoutConstraint {
|
||||||
|
|
||||||
|
// internal
|
||||||
|
|
||||||
|
internal var constraint: Constraint?
|
||||||
|
|
||||||
|
internal class func layoutConstraintsInstalledOnView(view: View) -> Array<LayoutConstraint> {
|
||||||
|
var constraints = objc_getAssociatedObject(view, &layoutConstraintsInstalledOnViewKey) as? Array<LayoutConstraint>
|
||||||
|
if constraints {
|
||||||
|
return constraints!
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
internal class func setLayoutConstraints(layoutConstraints: Array<LayoutConstraint>, installedOnView view: View) {
|
||||||
|
objc_setAssociatedObject(view, &layoutConstraintsInstalledOnViewKey, layoutConstraints, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var layoutConstraintsInstalledOnViewKey = ""
|
|
@ -2,50 +2,80 @@
|
||||||
// View.swift
|
// View.swift
|
||||||
// Snappy
|
// Snappy
|
||||||
//
|
//
|
||||||
// Created by Jonas Budelmann on 25/07/14.
|
// Copyright (c) 2011-2014 Masonry Team - https://github.com/Masonry
|
||||||
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
|
|
||||||
//
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
import Foundation
|
// 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:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
typealias View = UIView
|
typealias View = UIView
|
||||||
|
|
||||||
extension View {
|
extension View {
|
||||||
var mas_left: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Left) }
|
#if SNP_SHORTHAND
|
||||||
var mas_top: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Top) }
|
var left: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Left) }
|
||||||
var mas_right: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Right) }
|
var top: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Top) }
|
||||||
var mas_bottom: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Bottom) }
|
var right: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Right) }
|
||||||
var mas_leading: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Leading) }
|
var bottom: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Bottom) }
|
||||||
var mas_trailing: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Trailing) }
|
var leading: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Leading) }
|
||||||
var mas_width: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Width) }
|
var trailing: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Trailing) }
|
||||||
var mas_height: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Height) }
|
var width: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Width) }
|
||||||
var mas_centerX: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .CenterX) }
|
var height: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Height) }
|
||||||
var mas_centerY: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .CenterY) }
|
var centerX: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.CenterX) }
|
||||||
var mas_baseline: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Baseline) }
|
var centerY: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.CenterY) }
|
||||||
|
var baseline: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Baseline) }
|
||||||
|
|
||||||
func mas_makeConstraints(block: (make: ConstraintMaker) -> ()) {
|
var edges: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Edges) }
|
||||||
self.setTranslatesAutoresizingMaskIntoConstraints(false)
|
var size: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Size) }
|
||||||
let constraintMaker: ConstraintMaker = ConstraintMaker(view: self)
|
var center: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Center) }
|
||||||
block(make: constraintMaker)
|
|
||||||
constraintMaker.install()
|
func makeConstraints(block: (maker: ConstraintMaker) -> ()) {
|
||||||
|
ConstraintMaker.makeConstraints(self, block: block)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mas_closestCommonSuperview(view: View) -> View {
|
func remakeConstraints(block: (maker: ConstraintMaker) -> ()) {
|
||||||
var closestCommonSuperview: View? = nil
|
ConstraintMaker.remakeConstraints(self, block: block)
|
||||||
|
}
|
||||||
|
|
||||||
var secondViewSuperview: View = view
|
#else
|
||||||
while (!closestCommonSuperview && secondViewSuperview != nil) {
|
var snp_left: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Left) }
|
||||||
var firstViewSuperview: View = self
|
var snp_top: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Top) }
|
||||||
while (!closestCommonSuperview && firstViewSuperview != nil) {
|
var snp_right: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Right) }
|
||||||
if (secondViewSuperview == firstViewSuperview) {
|
var snp_bottom: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Bottom) }
|
||||||
closestCommonSuperview = secondViewSuperview
|
var snp_leading: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Leading) }
|
||||||
|
var snp_trailing: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Trailing) }
|
||||||
|
var snp_width: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Width) }
|
||||||
|
var snp_height: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Height) }
|
||||||
|
var snp_centerX: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.CenterX) }
|
||||||
|
var snp_centerY: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.CenterY) }
|
||||||
|
var snp_baseline: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Baseline) }
|
||||||
|
|
||||||
|
var snp_edges: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Edges) }
|
||||||
|
var snp_size: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Size) }
|
||||||
|
var snp_center: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Center) }
|
||||||
|
|
||||||
|
func snp_makeConstraints(block: (maker: ConstraintMaker) -> ()) {
|
||||||
|
ConstraintMaker.makeConstraints(self, block: block)
|
||||||
}
|
}
|
||||||
firstViewSuperview = firstViewSuperview.superview
|
|
||||||
}
|
func snp_remakeConstraints(block: (maker: ConstraintMaker) -> ()) {
|
||||||
secondViewSuperview = secondViewSuperview.superview
|
ConstraintMaker.remakeConstraints(self, block: block)
|
||||||
}
|
|
||||||
return closestCommonSuperview!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
//
|
|
||||||
// ViewAttribute.swift
|
|
||||||
// Snappy
|
|
||||||
//
|
|
||||||
// Created by Jonas Budelmann on 25/07/14.
|
|
||||||
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
struct ViewAttribute {
|
|
||||||
weak var view: View?
|
|
||||||
var layoutAttribute: NSLayoutAttribute
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
//
|
|
||||||
// ViewConstraint.swift
|
|
||||||
// Snappy
|
|
||||||
//
|
|
||||||
// Created by Jonas Budelmann on 25/07/14.
|
|
||||||
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class ViewConstraint: Constraint {
|
|
||||||
weak var view: View?
|
|
||||||
|
|
||||||
var firstViewAttribute: ViewAttribute
|
|
||||||
var secondViewAttribute: ViewAttribute?
|
|
||||||
|
|
||||||
var layoutPriority = 1000.0// UILayoutPriorityRequired gives error?!
|
|
||||||
var layoutMultiplier = 1.0
|
|
||||||
var layoutConstraint: NSLayoutConstraint?
|
|
||||||
var layoutRelation: NSLayoutRelation?
|
|
||||||
var layoutConstant: Float
|
|
||||||
|
|
||||||
weak var delegate: ConstraintDelegate?
|
|
||||||
|
|
||||||
init(view: View, firstViewAttribute: ViewAttribute) {
|
|
||||||
self.view = view;
|
|
||||||
self.firstViewAttribute = firstViewAttribute;
|
|
||||||
|
|
||||||
self.layoutPriority = 1000.0// UILayoutPriorityRequired gives error?!
|
|
||||||
self.layoutMultiplier = 1.0
|
|
||||||
self.layoutConstant = 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
var left: Constraint { return addConstraint(.Left) }
|
|
||||||
var top: Constraint { return addConstraint(.Top) }
|
|
||||||
var right: Constraint { return addConstraint(.Right) }
|
|
||||||
var bottom: Constraint { return addConstraint(.Bottom) }
|
|
||||||
var leading: Constraint { return addConstraint(.Leading) }
|
|
||||||
var trailing: Constraint { return addConstraint(.Trailing) }
|
|
||||||
var width: Constraint { return addConstraint(.Width) }
|
|
||||||
var height: Constraint { return addConstraint(.Height) }
|
|
||||||
var centerX: Constraint { return addConstraint(.CenterX) }
|
|
||||||
var centerY: Constraint { return addConstraint(.CenterY) }
|
|
||||||
var baseline: Constraint { return addConstraint(.Baseline) }
|
|
||||||
|
|
||||||
var and: Constraint { return self }
|
|
||||||
var with: Constraint { return self }
|
|
||||||
|
|
||||||
func addConstraint(attr: NSLayoutAttribute) -> Constraint {
|
|
||||||
if (self.layoutRelation) {
|
|
||||||
//TODO use assert
|
|
||||||
println("Attributes should be chained before defining the constraint relation")
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.delegate!.constraint(self, addConstraintWithLayoutAttribute:attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func equality(relation: NSLayoutRelation, attr: Any) -> Constraint {
|
|
||||||
layoutRelation = relation
|
|
||||||
|
|
||||||
switch attr {
|
|
||||||
case let view as View:
|
|
||||||
secondViewAttribute = ViewAttribute(view: firstViewAttribute.view, layoutAttribute: firstViewAttribute.layoutAttribute)
|
|
||||||
case let offset as Float:
|
|
||||||
layoutConstant = offset
|
|
||||||
case let number as NSNumber:
|
|
||||||
layoutConstant = number
|
|
||||||
case let viewAttribute as ViewAttribute:
|
|
||||||
secondViewAttribute = viewAttribute
|
|
||||||
case let size as CGSize:
|
|
||||||
sizeOffset(size)
|
|
||||||
case let point as CGPoint:
|
|
||||||
centerOffset(point)
|
|
||||||
case let inset as UIEdgeInsets:
|
|
||||||
insets(inset)
|
|
||||||
default:
|
|
||||||
println("unsupported value: \(attr)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) {
|
|
||||||
// CGPoint point;
|
|
||||||
// [value getValue:&point];
|
|
||||||
// self.centerOffset = point;
|
|
||||||
// } else if (strcmp(value.objCType, @encode(CGSize)) == 0) {
|
|
||||||
// CGSize size;
|
|
||||||
// [value getValue:&size];
|
|
||||||
// self.sizeOffset = size;
|
|
||||||
// } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) {
|
|
||||||
// MASEdgeInsets insets;
|
|
||||||
// [value getValue:&insets];
|
|
||||||
// self.insets = insets;
|
|
||||||
// } else {
|
|
||||||
// NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
private func sizeOffset(size: CGSize) {
|
|
||||||
switch (firstViewAttribute.layoutAttribute) {
|
|
||||||
case .Width:
|
|
||||||
layoutConstant = Float(size.width);
|
|
||||||
break;
|
|
||||||
case .Height:
|
|
||||||
layoutConstant = Float(size.height);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func centerOffset(point: CGPoint) {
|
|
||||||
switch (firstViewAttribute.layoutAttribute) {
|
|
||||||
case .CenterX:
|
|
||||||
layoutConstant = Float(point.x);
|
|
||||||
break;
|
|
||||||
case .CenterY:
|
|
||||||
layoutConstant = Float(point.y);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func insets(insets: UIEdgeInsets) -> Constraint {
|
|
||||||
switch (firstViewAttribute.layoutAttribute) {
|
|
||||||
case .Left:
|
|
||||||
layoutConstant = Float(insets.left);
|
|
||||||
break;
|
|
||||||
case .Top:
|
|
||||||
layoutConstant = Float(insets.top);
|
|
||||||
break;
|
|
||||||
case .Bottom:
|
|
||||||
layoutConstant = Float(-insets.bottom);
|
|
||||||
break;
|
|
||||||
case .Right:
|
|
||||||
layoutConstant = Float(-insets.right);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalTo(attr: Any) -> Constraint {
|
|
||||||
return equality(.Equal, attr: attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func greaterThanOrEqualTo(attr: Any) -> Constraint {
|
|
||||||
return equality(.GreaterThanOrEqual, attr: attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lessThanOrEqualTo(attr: Any) -> Constraint {
|
|
||||||
return equality(.LessThanOrEqual, attr: attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func insets(insets: Any) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func offset(offset: Any) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func multipliedBy(multiplier: Float) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func dividedBy(divider: Float) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priority(priority: UILayoutPriority) -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priorityLow() -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priorityMedium() -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func priorityHigh() -> Constraint {
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func install() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ class ViewController: UIViewController {
|
||||||
|
|
||||||
let superview: UIView = self.view
|
let superview: UIView = self.view
|
||||||
|
|
||||||
let view1 = UIView()
|
let view1 = UIView(frame: CGRectZero)
|
||||||
view1.backgroundColor = UIColor.greenColor()
|
view1.backgroundColor = UIColor.greenColor()
|
||||||
view1.layer.borderColor = UIColor.blackColor().CGColor
|
view1.layer.borderColor = UIColor.blackColor().CGColor
|
||||||
view1.layer.borderWidth = 2
|
view1.layer.borderWidth = 2
|
||||||
|
@ -35,34 +35,23 @@ class ViewController: UIViewController {
|
||||||
|
|
||||||
let padding = UIEdgeInsets(top: 15, left: 10, bottom: 15, right: 10)
|
let padding = UIEdgeInsets(top: 15, left: 10, bottom: 15, right: 10)
|
||||||
|
|
||||||
view1.mas_makeConstraints { make in
|
|
||||||
make.top.and.left.greaterThanOrEqualTo(superview).insets(padding)
|
|
||||||
|
|
||||||
make.bottom.equalTo(view3.mas_top).insets(padding)
|
view1.snp_makeConstraints { make in
|
||||||
make.right.equalTo(view2.mas_left).insets(padding)
|
make.top.and.left.equalTo(CGPointZero).insets(padding)
|
||||||
make.width.equalTo(view2.mas_width)
|
make.size.equalTo(CGSizeMake(100, 50))
|
||||||
|
|
||||||
make.height.equalTo([view2, view3])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
view2.mas_makeConstraints { make in
|
view2.snp_makeConstraints { make in
|
||||||
// chain attributes
|
make.centerX.equalTo(view1.snp_centerX).offset(CGPointMake(50, 0))
|
||||||
make.top.and.right.equalTo(superview).insets(padding)
|
make.top.equalTo(view1.snp_bottom).offset(50)
|
||||||
|
make.width.equalTo(view1.snp_height)
|
||||||
make.left.equalTo(view1.mas_right).insets(padding)
|
make.height.equalTo(view1.snp_width)
|
||||||
make.bottom.equalTo(view3.mas_top).insets(padding)
|
|
||||||
make.width.equalTo(view1.mas_width)
|
|
||||||
|
|
||||||
make.height.equalTo([view1, view3])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
view3.mas_makeConstraints { make in
|
view3.snp_makeConstraints { make in
|
||||||
make.top.equalTo(view1.mas_bottom).insets(padding)
|
make.width.height.greaterThanOrEqualTo(view1)
|
||||||
|
make.width.height.greaterThanOrEqualTo(view2)
|
||||||
// chain attributes
|
make.center.equalTo(superview)
|
||||||
make.left.right.and.bottom.equalTo(superview).insets(padding)
|
|
||||||
|
|
||||||
make.height.equalTo([view1, view2])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue