Merge branch 'release/preview'

This commit is contained in:
Robert Payne 2014-07-29 12:40:12 +12:00
commit ab94bd95e3
12 changed files with 1028 additions and 500 deletions

4
CHANGELOG.md Normal file
View File

@ -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.

23
LICENSE Normal file
View File

@ -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.

267
README.md Normal file
View File

@ -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&rsquo;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

View File

@ -10,13 +10,11 @@
DD03E3151981B52F00E0DE94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD03E30F1981B52F00E0DE94 /* AppDelegate.swift */; };
DD03E3171981B52F00E0DE94 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD03E3121981B52F00E0DE94 /* Images.xcassets */; };
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 */; };
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 */; };
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 */
/* Begin PBXContainerItemProxy section */
@ -48,15 +46,13 @@
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>"; };
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>"; };
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>"; };
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; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@ -91,11 +87,9 @@
DD03E31A1981B62D00E0DE94 /* Snappy */ = {
isa = PBXGroup;
children = (
DD03E31F1981B70D00E0DE94 /* CompositeConstraint.swift */,
EEC6EB4D1985370500C27B12 /* LayoutConstraint.swift */,
DD03E3211981B71C00E0DE94 /* Constraint.swift */,
DD03E3231981B72A00E0DE94 /* ConstraintMaker.swift */,
DD03E3251981B78D00E0DE94 /* ViewAttribute.swift */,
DD03E3271981B79C00E0DE94 /* ViewConstraint.swift */,
DD03E3291981B7BF00E0DE94 /* View.swift */,
);
path = Snappy;
@ -230,12 +224,10 @@
files = (
DD03E32A1981B7BF00E0DE94 /* View.swift in Sources */,
DD03E3221981B71C00E0DE94 /* Constraint.swift in Sources */,
EEC6EB4E1985370500C27B12 /* LayoutConstraint.swift in Sources */,
DD03E3241981B72A00E0DE94 /* ConstraintMaker.swift in Sources */,
DD03E3191981B52F00E0DE94 /* ViewController.swift in Sources */,
DD03E3281981B79C00E0DE94 /* ViewConstraint.swift in Sources */,
DD03E3201981B70D00E0DE94 /* CompositeConstraint.swift in Sources */,
DD03E3151981B52F00E0DE94 /* AppDelegate.swift in Sources */,
DD03E3261981B78D00E0DE94 /* ViewAttribute.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -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() {
}
}

View File

@ -2,60 +2,549 @@
// Constraint.swift
// Snappy
//
// Created by Jonas Budelmann on 25/07/14.
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
// 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
@class_protocol protocol ConstraintDelegate {
func constraint(constraint: Constraint, shouldBeReplacedWithConstraint replacementConstraint: Constraint)
func constraint(constraint: Constraint?, addConstraintWithLayoutAttribute layoutAttribute: NSLayoutAttribute) -> Constraint
/**
* ConstraintAttributes is an options set that maps to NSLayoutAttributes.
*/
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 {
weak var delegate: Delegate { get set }
var left: Constraint { get }
var top: Constraint { get }
var right: Constraint { get }
var bottom: Constraint { get }
var leading: Constraint { get }
var trailing: Constraint { get }
var width: Constraint { get }
var height: Constraint { get }
var centerX: Constraint { get }
var centerY: Constraint { get }
var baseline: Constraint { get }
var and: Constraint { get }
var with: Constraint { get }
func addConstraint(NSLayoutAttribute) -> Constraint
func equalTo(attr: Any) -> Constraint
func greaterThanOrEqualTo(attr: Any) -> Constraint
func lessThanOrEqualTo(attr: Any) -> Constraint
func insets(insets: UIEdgeInsets) -> Constraint
func offset(offset: Any) -> Constraint
func multipliedBy(multiplier: Float) -> Constraint
func dividedBy(divider: Float) -> Constraint
func priority(priority: UILayoutPriority) -> Constraint
func priorityLow() -> Constraint
func priorityMedium() -> Constraint
func priorityHigh() -> Constraint
func install()
var layoutRelation: NSLayoutRelation {
get {
switch(self) {
case .LessThanOrEqualTo:
return .LessThanOrEqual
case .GreaterThanOrEqualTo:
return .GreaterThanOrEqual
default:
return .Equal
}
}
}
}
/**
* ConstraintItem is a class that is used while building constraints.
*/
class ConstraintItem {
init(view: View?, attributes: ConstraintAttributes) {
self.view = view
self.attributes = attributes
}
internal weak var view: View?
internal var attributes: ConstraintAttributes
}
/**
* Constraint is a single item that defines all the properties for a single ConstraintMaker chain
*/
class Constraint {
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 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);
}
}

View File

@ -2,91 +2,89 @@
// ConstraintMaker.swift
// Snappy
//
// Created by Jonas Budelmann on 25/07/14.
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
// 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
class ConstraintMaker: ConstraintDelegate {
var constraints = [Constraint]()
weak var view: View?
/**
* ConstraintMaker is the maker in snappy that gets all constraints kickstarted
*/
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) {
self.view = view
}
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) }
internal weak var view: View?
internal var constraints = Array<Constraint>()
//TODO
var edges: Constraint { return addConstraints(.Top, .Left, .Bottom, .Right) }
var size: Constraint { return addConstraints(.Width, .Height) }
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
internal func addConstraint(attributes: ConstraintAttributes) -> Constraint {
let item = ConstraintItem(view: self.view, attributes: attributes)
let constraint = Constraint(fromItem: item)
self.constraints.append(constraint)
return constraint
}
func constraint(constraint: Constraint, shouldBeReplacedWithConstraint replacementConstraint: Constraint) {
var index: Int?
for (i, c) in enumerate(self.constraints) {
if (c === constraint) {
index = i
internal class func makeConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
view.setTranslatesAutoresizingMaskIntoConstraints(false)
let maker = ConstraintMaker(view: view)
block(make: maker)
var layoutConstraints: Array<LayoutConstraint> = []
for constraint in maker.constraints {
layoutConstraints += constraint.install()
}
LayoutConstraint.setLayoutConstraints(layoutConstraints, installedOnView: view)
}
if (index) {
self.constraints[index!] = replacementConstraint
}
}
internal class func remakeConstraints(view: View, block: (make: ConstraintMaker) -> ()) {
view.setTranslatesAutoresizingMaskIntoConstraints(false)
let maker = ConstraintMaker(view: view)
block(make: maker)
func constraint(constraint: Constraint?, addConstraintWithLayoutAttribute layoutAttribute: NSLayoutAttribute) -> Constraint {
var viewAttribute = ViewAttribute(view: self.view, layoutAttribute: layoutAttribute)
var newConstraint = ViewConstraint(view: self.view!, firstViewAttribute: viewAttribute)
var layoutConstraints: Array<LayoutConstraint> = LayoutConstraint.layoutConstraintsInstalledOnView(view)
for existingLayoutConstraint in layoutConstraints {
existingLayoutConstraint.constraint?.uninstall()
}
layoutConstraints = []
if let viewConstraint = constraint as? ViewConstraint {
//replace with composite constraint
var children = [viewConstraint, newConstraint]
var compositeConstraint = CompositeConstraint(children: children)
compositeConstraint.delegate = self
self.constraint(viewConstraint, shouldBeReplacedWithConstraint:compositeConstraint);
return compositeConstraint
for constraint in maker.constraints {
layoutConstraints += constraint.install()
}
if (!constraint) {
newConstraint.delegate = self
self.constraints.append(newConstraint)
}
return newConstraint
LayoutConstraint.setLayoutConstraints(layoutConstraints, installedOnView: view)
}
}

View File

@ -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 = ""

View File

@ -2,50 +2,80 @@
// View.swift
// Snappy
//
// Created by Jonas Budelmann on 25/07/14.
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
// Copyright (c) 2011-2014 Masonry Team - https://github.com/Masonry
//
import Foundation
// 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
typealias View = UIView
extension View {
var mas_left: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Left) }
var mas_top: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Top) }
var mas_right: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Right) }
var mas_bottom: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Bottom) }
var mas_leading: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Leading) }
var mas_trailing: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Trailing) }
var mas_width: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Width) }
var mas_height: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Height) }
var mas_centerX: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .CenterX) }
var mas_centerY: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .CenterY) }
var mas_baseline: ViewAttribute { return ViewAttribute(view: self, layoutAttribute: .Baseline) }
#if SNP_SHORTHAND
var left: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Left) }
var top: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Top) }
var right: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Right) }
var bottom: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Bottom) }
var leading: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Leading) }
var trailing: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Trailing) }
var width: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Width) }
var height: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Height) }
var centerX: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.CenterX) }
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) -> ()) {
self.setTranslatesAutoresizingMaskIntoConstraints(false)
let constraintMaker: ConstraintMaker = ConstraintMaker(view: self)
block(make: constraintMaker)
constraintMaker.install()
var edges: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Edges) }
var size: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Size) }
var center: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Center) }
func makeConstraints(block: (maker: ConstraintMaker) -> ()) {
ConstraintMaker.makeConstraints(self, block: block)
}
func mas_closestCommonSuperview(view: View) -> View {
var closestCommonSuperview: View? = nil
func remakeConstraints(block: (maker: ConstraintMaker) -> ()) {
ConstraintMaker.remakeConstraints(self, block: block)
}
var secondViewSuperview: View = view
while (!closestCommonSuperview && secondViewSuperview != nil) {
var firstViewSuperview: View = self
while (!closestCommonSuperview && firstViewSuperview != nil) {
if (secondViewSuperview == firstViewSuperview) {
closestCommonSuperview = secondViewSuperview
#else
var snp_left: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Left) }
var snp_top: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Top) }
var snp_right: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Right) }
var snp_bottom: ConstraintItem { return ConstraintItem(view: self, attributes: ConstraintAttributes.Bottom) }
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
}
secondViewSuperview = secondViewSuperview.superview
}
return closestCommonSuperview!
func snp_remakeConstraints(block: (maker: ConstraintMaker) -> ()) {
ConstraintMaker.remakeConstraints(self, block: block)
}
#endif
}

View File

@ -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
}

View File

@ -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() {
}
}

View File

@ -15,7 +15,7 @@ class ViewController: UIViewController {
let superview: UIView = self.view
let view1 = UIView()
let view1 = UIView(frame: CGRectZero)
view1.backgroundColor = UIColor.greenColor()
view1.layer.borderColor = UIColor.blackColor().CGColor
view1.layer.borderWidth = 2
@ -35,34 +35,23 @@ class ViewController: UIViewController {
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)
make.right.equalTo(view2.mas_left).insets(padding)
make.width.equalTo(view2.mas_width)
make.height.equalTo([view2, view3])
view1.snp_makeConstraints { make in
make.top.and.left.equalTo(CGPointZero).insets(padding)
make.size.equalTo(CGSizeMake(100, 50))
}
view2.mas_makeConstraints { make in
// chain attributes
make.top.and.right.equalTo(superview).insets(padding)
make.left.equalTo(view1.mas_right).insets(padding)
make.bottom.equalTo(view3.mas_top).insets(padding)
make.width.equalTo(view1.mas_width)
make.height.equalTo([view1, view3])
view2.snp_makeConstraints { make in
make.centerX.equalTo(view1.snp_centerX).offset(CGPointMake(50, 0))
make.top.equalTo(view1.snp_bottom).offset(50)
make.width.equalTo(view1.snp_height)
make.height.equalTo(view1.snp_width)
}
view3.mas_makeConstraints { make in
make.top.equalTo(view1.mas_bottom).insets(padding)
// chain attributes
make.left.right.and.bottom.equalTo(superview).insets(padding)
make.height.equalTo([view1, view2])
view3.snp_makeConstraints { make in
make.width.height.greaterThanOrEqualTo(view1)
make.width.height.greaterThanOrEqualTo(view2)
make.center.equalTo(superview)
}
}