mirror of https://github.com/SnapKit/SnapKit
commit
d44a9bebd2
349
README.md
349
README.md
|
@ -1,349 +1,34 @@
|
||||||
|
<img src="http://snapkit.io/images/banner.png" alt="" />
|
||||||
|
|
||||||
SnapKit
|
SnapKit is a DSL to make Auto Layout easy on both iOS and OS X.
|
||||||
|
|
||||||
====
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/SnapKit/SnapKit.svg)](https://travis-ci.org/SnapKit/SnapKit)
|
[![Build Status](https://travis-ci.org/SnapKit/SnapKit.svg)](https://travis-ci.org/SnapKit/SnapKit)
|
||||||
|
|
||||||
SnapKit is a light-weight layout framework which wraps AutoLayout with a nicer syntax. SnapKit 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. SnapKit supports both iOS and OS X.
|
|
||||||
|
|
||||||
> SnapKit uses some Swift-only features like function overloading, so it cannot be used from Objective-C. Because of this we’ve chosen to swap prefixes from Masonry’s `mas_` to `snp_` so you can use both Masonry and SnapKit in the same project.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
* iOS 7.0+ / Mac OS X 10.9+
|
|
||||||
* Xcode 6.1
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
> **Embedded frameworks require a minimum deployment target of iOS 8 or OS X Mavericks.**
|
|
||||||
|
|
||||||
### CocoaPods
|
|
||||||
|
|
||||||
[CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects.
|
|
||||||
|
|
||||||
CocoaPods 0.36 adds supports for Swift and embedded frameworks. You can install it with the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gem install cocoapods
|
|
||||||
```
|
|
||||||
|
|
||||||
To integrate SnapKit into your Xcode project using CocoaPods, specify it in your `Podfile`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
source 'https://github.com/CocoaPods/Specs.git'
|
|
||||||
platform :ios, '8.0'
|
|
||||||
use_frameworks!
|
|
||||||
|
|
||||||
pod 'SnapKit', '~> 0.10.0'
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pod install
|
|
||||||
```
|
|
||||||
|
|
||||||
### Carthage
|
|
||||||
|
|
||||||
Carthage is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.
|
|
||||||
|
|
||||||
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew update
|
|
||||||
$ brew install carthage
|
|
||||||
```
|
|
||||||
|
|
||||||
To integrate SnapKit into your Xcode project using Carthage, specify it in your `Cartfile`:
|
|
||||||
|
|
||||||
```
|
|
||||||
github "SnapKit/SnapKit" >= 0.10.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manually
|
|
||||||
|
|
||||||
If you prefer not to use either of the aforementioned dependency managers, you can integrate SnapKit into your project manually.
|
|
||||||
|
|
||||||
### Embedded Framework
|
|
||||||
|
|
||||||
- Add SnapKit as a [submodule](http://git-scm.com/docs/git-submodule) by opening the Terminal, `cd`-ing into your top-level project directory, and entering the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git submodule add https://github.com/SnapKit/SnapKit.git
|
|
||||||
```
|
|
||||||
|
|
||||||
- Open the `SnapKit` folder, and drag `SnapKit.xcodeproj` into the file navigator of your app project.
|
|
||||||
- In Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar.
|
|
||||||
- Ensure that the deployment target of SnapKit.framework matches that of the application target.
|
|
||||||
- In the tab bar at the top of that window, open the "Build Phases" panel.
|
|
||||||
- Expand the "Target Dependencies" group, and add `SnapKit.framework`.
|
|
||||||
- Click on the `+` button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `SnapKit.framework`.
|
|
||||||
|
|
||||||
## 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
|
```swift
|
||||||
let superview = self;
|
import SnapKit
|
||||||
|
|
||||||
let view1 = UIView()
|
class MyViewController: UIViewController {
|
||||||
view1.setTranslatesAutoresizingMaskIntoConstraints(false)
|
|
||||||
view1.backgroundColor = UIColor.greenColor()
|
|
||||||
superview.addSubview(view1)
|
|
||||||
|
|
||||||
let padding = UIEdgeInsetsMake(10, 10, 10, 10)
|
lazy var box = UIView()
|
||||||
|
|
||||||
superview.addConstraints([
|
override func viewDidLoad() {
|
||||||
NSLayoutConstraint(
|
super.viewDidLoad()
|
||||||
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!
|
self.view.addSubview(box)
|
||||||
|
box.snp_makeConstraints { (make) -> Void in
|
||||||
Heres the same constraints created using ConstraintMaker
|
make.width.height.equalTo(50)
|
||||||
|
make.center.equalTo(self.view)
|
||||||
```swift
|
|
||||||
let padding = UIEdgeInsetsMake(10, 10, 10, 10)
|
|
||||||
|
|
||||||
view1.snp_makeConstraints { (make) -> Void in
|
|
||||||
make.top.equalTo(superview.snp_top).offset(padding.top)
|
|
||||||
make.left.equalTo(superview.snp_left).offset(padding.left)
|
|
||||||
make.bottom.equalTo(superview.snp_bottom).offset(-padding.bottom)
|
|
||||||
make.right.equalTo(superview.snp_right).offset(-padding.right)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Or even shorter
|
|
||||||
|
|
||||||
```swift
|
|
||||||
view1.snp_makeConstraints { (make) -> Void in
|
|
||||||
make.edges.equalTo(superview).insets(padding)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Also note in the first example we had to add the constraints to the superview `superview.addConstraints`.
|
|
||||||
SnapKit however will automagically add constraints to the appropriate view.
|
|
||||||
|
|
||||||
SnapKit 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 SnapKit 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.equalTo(42)
|
|
||||||
make.height.equalTo(20)
|
|
||||||
make.size.equalTo(CGSizeMake(50, 100))
|
|
||||||
make.edges.equalTo(UIEdgeInsetsMake(10, 0, 10, 0))
|
|
||||||
make.left.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).priorityLow();
|
|
||||||
|
|
||||||
make.top.equalTo(label.snp_top).priority(600);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Composition, composition, composition
|
|
||||||
|
|
||||||
SnapKit 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.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 SnapKit 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).offset(padding.top).constraint
|
|
||||||
make.left.equalTo(superview).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 SnapKit.
|
|
||||||
|
|
||||||
```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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Code Snippets
|
## Resources
|
||||||
|
|
||||||
Copy the included code snippets to ``~/Library/Developer/Xcode/UserData/CodeSnippets`` to write your snap closures at lightning speed!
|
* [Documentation](http://snapkit.io/docs/)
|
||||||
|
* [F.A.Q.](http://snapkit.io/faq/)
|
||||||
|
|
||||||
`snp_make` -> `<view>.snp_makeConstraints { make in <code> }`
|
## License
|
||||||
|
|
||||||
`snp_remake` -> `<view>.snp_remakeConstraints { make in <code> }`
|
MIT license. See the `LICENSE` file for details.
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
* Eye candy
|
|
||||||
* Example projects
|
|
||||||
* Tests
|
|
||||||
|
|
Loading…
Reference in New Issue