Authored by Sam Symons

Update the documentation to include the changes made in 0.5.

The documentation has been updating to include details about the changes
made in 0.5. There is also a new section in the iOS example project to
demonstrate mas_remakeConstraints.
... ... @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
114413091924B6EE008E702E /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 114413081924B6EE008E702E /* Default-568h@2x.png */; };
3DB1CAD5184538E200E91FC5 /* MASExampleArrayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB1CAD4184538E200E91FC5 /* MASExampleArrayView.m */; };
4BEB55B61957394E008C862B /* MASExampleRemakeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BEB55B51957394E008C862B /* MASExampleRemakeView.m */; };
6C87DADA5AB046D9A3181A65 /* libPods-Masonry iOS Examples.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BDC1B8303EED42A2B01B94B1 /* libPods-Masonry iOS Examples.a */; };
DD175E6A182639FB0099129A /* MASExampleUpdateView.m in Sources */ = {isa = PBXBuildFile; fileRef = DD175E69182639FB0099129A /* MASExampleUpdateView.m */; };
DD32C3FD18E8BFF6001F6AD2 /* MASExampleAttributeChainingView.m in Sources */ = {isa = PBXBuildFile; fileRef = DD32C3FC18E8BFF6001F6AD2 /* MASExampleAttributeChainingView.m */; };
... ... @@ -34,6 +35,8 @@
114413081924B6EE008E702E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
3DB1CAD3184538E200E91FC5 /* MASExampleArrayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASExampleArrayView.h; sourceTree = "<group>"; };
3DB1CAD4184538E200E91FC5 /* MASExampleArrayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASExampleArrayView.m; sourceTree = "<group>"; };
4BEB55B41957394E008C862B /* MASExampleRemakeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASExampleRemakeView.h; sourceTree = "<group>"; };
4BEB55B51957394E008C862B /* MASExampleRemakeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASExampleRemakeView.m; sourceTree = "<group>"; };
B086DD7D31DD4B49ADC08504 /* Pods-Masonry iOS Examples.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Masonry iOS Examples.xcconfig"; path = "../Pods/Pods-Masonry iOS Examples.xcconfig"; sourceTree = "<group>"; };
BDC1B8303EED42A2B01B94B1 /* libPods-Masonry iOS Examples.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Masonry iOS Examples.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DD175E68182639FB0099129A /* MASExampleUpdateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASExampleUpdateView.h; sourceTree = "<group>"; };
... ... @@ -157,6 +160,8 @@
DDDF60CB181915E300BF7B8B /* MASExampleLabelView.m */,
DD175E68182639FB0099129A /* MASExampleUpdateView.h */,
DD175E69182639FB0099129A /* MASExampleUpdateView.m */,
4BEB55B41957394E008C862B /* MASExampleRemakeView.h */,
4BEB55B51957394E008C862B /* MASExampleRemakeView.m */,
DD9B4D33183CC980002BF408 /* MASExampleScrollView.h */,
DD9B4D34183CC980002BF408 /* MASExampleScrollView.m */,
3DB1CAD3184538E200E91FC5 /* MASExampleArrayView.h */,
... ... @@ -293,6 +298,7 @@
DD52F255179CADC0005CD195 /* MASExampleViewController.m in Sources */,
DDF3875C179D648D00178773 /* MASExampleAnimatedView.m in Sources */,
DD7CC17617ACE990007A469E /* MASExampleDebuggingView.m in Sources */,
4BEB55B61957394E008C862B /* MASExampleRemakeView.m in Sources */,
DD9B4D35183CC980002BF408 /* MASExampleScrollView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
... ...
... ... @@ -15,6 +15,7 @@
#import "MASExampleDebuggingView.h"
#import "MASExampleLabelView.h"
#import "MASExampleUpdateView.h"
#import "MASExampleRemakeView.h"
#import "MASExampleScrollView.h"
#import "MASExampleLayoutGuideViewController.h"
#import "MASExampleArrayView.h"
... ... @@ -41,6 +42,8 @@ static NSString * const kMASCellReuseIdentifier = @"kMASCellReuseIdentifier";
viewClass:MASExampleBasicView.class],
[[MASExampleViewController alloc] initWithTitle:@"Update Constraints"
viewClass:MASExampleUpdateView.class],
[[MASExampleViewController alloc] initWithTitle:@"Remake Constraints"
viewClass:MASExampleRemakeView.class],
[[MASExampleViewController alloc] initWithTitle:@"Using Constants"
viewClass:MASExampleConstantsView.class],
[[MASExampleViewController alloc] initWithTitle:@"Composite Edges"
... ...
//
// MASExampleRemakeView.h
// Masonry iOS Examples
//
// Created by Sam Symons on 2014-06-22.
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MASExampleRemakeView : UIView
@end
... ...
//
// MASExampleRemakeView.m
// Masonry iOS Examples
//
// Created by Sam Symons on 2014-06-22.
// Copyright (c) 2014 Jonas Budelmann. All rights reserved.
//
#import "MASExampleRemakeView.h"
@interface MASExampleRemakeView ()
@property (nonatomic, strong) UIButton *movingButton;
@property (nonatomic, assign) BOOL topLeft;
- (void)toggleButtonPosition;
@end
@implementation MASExampleRemakeView
- (id)init {
self = [super init];
if (!self) return nil;
self.movingButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.movingButton setTitle:@"Move Me!" forState:UIControlStateNormal];
self.movingButton.layer.borderColor = UIColor.greenColor.CGColor;
self.movingButton.layer.borderWidth = 3;
[self.movingButton addTarget:self action:@selector(toggleButtonPosition) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.movingButton];
self.topLeft = YES;
// make sure updateConstraints gets called
[self setNeedsUpdateConstraints];
return self;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
[self.movingButton remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(100));
make.height.equalTo(@(100));
if (self.topLeft) {
make.left.equalTo(self.topLeft).with.offset(10);
make.top.equalTo(self.top).with.offset(10);
}
else {
make.bottom.equalTo(self.bottom).with.offset(-10);
make.right.equalTo(self.right).with.offset(-10);
}
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
- (void)toggleButtonPosition {
self.topLeft = !self.topLeft;
// tell constraints they need updating
[self setNeedsUpdateConstraints];
// update constraints now so we can animate the change
[self updateConstraintsIfNeeded];
[UIView animateWithDuration:0.4 animations:^{
[self layoutIfNeeded];
}];
}
@end
... ...
#Masonry [![Build Status](https://travis-ci.org/cloudkite/Masonry.svg?branch=master)](https://travis-ci.org/cloudkite/Masonry) [![Coverage Status](https://coveralls.io/repos/cloudkite/Masonry/badge.png?branch=master)](https://coveralls.io/r/cloudkite/Masonry?branch=master)
#Masonry [![Build Status](https://travis-ci.org/Masonry/Masonry.svg?branch=master)](https://travis-ci.org/Masonry/Masonry) [![Coverage Status](https://coveralls.io/repos/cloudkite/Masonry/badge.png?branch=master)](https://coveralls.io/r/cloudkite/Masonry?branch=master)
Masonry is a light-weight layout framework which wraps AutoLayout with a nicer syntax. Masonry 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.
Masonry supports iOS and Mac OSX.
Masonry supports iOS and Mac OS X.
For examples take a look at the **Masonry iOS Examples** project in the Masonry workspace. You will need to run `pod install` after downloading.
## Whats wrong with NSLayoutConstraints?
## 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
... ... @@ -36,8 +36,8 @@ UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
... ... @@ -45,7 +45,7 @@ UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
... ... @@ -57,8 +57,8 @@ UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
]];
```
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.
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!
... ... @@ -74,7 +74,8 @@ UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
```
Or ever shorter
Or even shorter
```obj-c
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
... ... @@ -102,19 +103,19 @@ These three equality constraints accept one argument which can be any of the fol
make.centerX.lessThanOrEqualTo(view2.mas_left);
```
MASViewAttribute | NSLayoutAttribute
------------------------- | --------------------------
view.mas_left | NSLayoutAttributeLeft
view.mas_right | NSLayoutAttributeRight
view.mas_top | NSLayoutAttributeTop
view.mas_bottom | NSLayoutAttributeBottom
view.mas_leading | NSLayoutAttributeLeading
view.mas_trailing | NSLayoutAttributeTrailing
view.mas_width | NSLayoutAttributeWidth
view.mas_height | NSLayoutAttributeHeight
view.mas_centerX | NSLayoutAttributeCenterX
view.mas_centerY | NSLayoutAttributeCenterY
view.mas_baseline | NSLayoutAttributeBaseline
MASViewAttribute | NSLayoutAttribute
------------------------- | --------------------------
view.mas_left | NSLayoutAttributeLeft
view.mas_right | NSLayoutAttributeRight
view.mas_top | NSLayoutAttributeTop
view.mas_bottom | NSLayoutAttributeBottom
view.mas_leading | NSLayoutAttributeLeading
view.mas_trailing | NSLayoutAttributeTrailing
view.mas_width | NSLayoutAttributeWidth
view.mas_height | NSLayoutAttributeHeight
view.mas_centerX | NSLayoutAttributeCenterX
view.mas_centerY | NSLayoutAttributeCenterY
view.mas_baseline | NSLayoutAttributeBaseline
#### 2. UIView/NSView
... ... @@ -142,11 +143,22 @@ So if you pass a NSNumber for these attributes Masonry will turn these into cons
make.left.lessThanOrEqualTo(@10)
```
Instead of using NSNumber, you can use primitives and structs to build your constraints, like so:
```obj-c
make.top.mas_equalTo(42);
make.height.mas_equalTo(20);
make.size.mas_equalTo(CGSizeMake(50, 100));
make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
```
By default, macros which support [autoboxing](https://en.wikipedia.org/wiki/Autoboxing#Autoboxing) are prefixed with `mas_`. Unprefixed versions are available by defining `MAS_SHORTHAND_GLOBAL` before importing Masonry.
#### 4. NSArray
An array of a mixture of any of the previous types
```obj-c
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);
````
... ... @@ -187,7 +199,7 @@ make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
```obj-c
// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)
make.size.greaterThanOrEqualTo(titleLabel)
// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
... ... @@ -196,16 +208,24 @@ make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
#### center
```obj-c
// make centerX and centerY = button1
make.center.equalTo(button1)
make.center.equalTo(button1)
// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
```
You can chain view attributes for increased readability:
```obj-c
// 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 Masonry there are two common approaches for updating constraints.
In Masonry 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.
... ... @@ -231,7 +251,7 @@ You could also reference multiple constraints by storing them away in an array.
#### 2. mas_updateConstraints
Alternatively if you are only updating the constant value of the constraint you can use the convience method `mas_updateConstraints` instead of `mas_makeConstraints`
```
```obj-c
// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
... ... @@ -248,13 +268,35 @@ Alternatively if you are only updating the constant value of the constraint you
}
```
You can find more detailed examples of both approaches in the **Masonry iOS Examples** project.
### 3. mas_remakeConstraints
`mas_updateConstraints` is useful for updating a set of constraints, but doing anything beyond updating constant values can get exhausting. That's where `mas_remakeConstraints` comes in.
`mas_remakeConstraints` is similar to `mas_updateConstraints`, but instead of updating constant values, it will remove all of its contraints before installing them again. This lets you provide different constraints without having to keep around references to ones which you want to remove.
```obj-c
- (void)changeButtonPosition {
[self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(self.buttonSize.width));
make.height.equalTo(@(self.buttonSize.height));
if (topLeft) {
make.top.equalTo(@10);
make.left.equalTo(@10);
} else {
make.bottom.equalTo(self.view.mas_bottom).with.offset(-10);
make.right.equalTo(self.view.mas_right).with.offset(-10);
}
}];
}
```
You can find more detailed examples of all three approaches in the **Masonry iOS Examples** project.
## When the ^&*!@ hits the fan!
Laying out your views doesn't always goto plan. So when things literally go pear shaped, you don't want to be looking at console output like this:
```
```obj-c
Unable to simultaneously satisfy constraints.....blah blah blah....
(
"<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>",
... ... @@ -263,16 +305,16 @@ Unable to simultaneously satisfy constraints.....blah blah blah....
"<NSLayoutConstraint:0x7189560 V:|-(1)-[UILabel:0x7186980] (Names: '|':MASExampleDebuggingView:0x7186560 )>"
)
Will attempt to recover by breaking constraint
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7189ac0 V:[UILabel:0x7186980(>=5000)]>
```
Masonry adds a category to NSLayoutConstraint which overrides the default implementation of `- (NSString *)description`.
Now you can give meaningful names to views and constraints, and also easily pick out the constraints created by Masonry.
which means your console output can now look like this:
which means your console output can now look like this:
```
```obj-c
Unable to simultaneously satisfy constraints......blah blah blah....
(
"<NSAutoresizingMaskLayoutConstraint:0x8887740 MASExampleDebuggingView:superview.height == 416>",
... ... @@ -281,7 +323,7 @@ Unable to simultaneously satisfy constraints......blah blah blah....
"<MASLayoutConstraint:ConflictingConstraint[0] UILabel:messageLabel.top == MASExampleDebuggingView:superview.top + 1>"
)
Will attempt to recover by breaking constraint
Will attempt to recover by breaking constraint
<MASLayoutConstraint:ConstantConstraint UILabel:messageLabel.height >= 5000>
```
... ...