Disposable UI with the Interface Builder and Autolayout
Bohdan Orlov
iOS Developer @Badoo
Reusable UI
- customisable
- written in a code
- very generic
- built for the future
- requires a lot of time
Disposable UI
- not reusable
- made in the Interface Builder
- very specific
- built to be scrapped
- fast to build
Disposable UI
- not all tasks can be done in the IB
- logic is spread between UI files and code
- Its fast - less code has to be written.
- less effort put to IB files so its easier to say "good bye"
- One glance to IB file gives general idea of UI
- able to achieve anything
- everything in one place
- code has to be written
- code has to be supported
- code will be scrapped and you will feel bad, its only matter of time
- UI is "invisible" until whole code is read
In a code
In the IB
Building disposable UI in the IB
- Reasons to use Autolayout
- Common issues adopting Autolayout in the IB
- Getting the most of IB using Size Classes
- Principle of least astonishment (surprise) using the IB
- IB tips and tricks
- Working with Autolayout in a code
- Debugging Autolayout
Agenda
Reasons to use Autolayout
- We can't hardcode frames in the IB anymore
- We have system wide font settings (Dynamic Type)
- Internationalisation (right to left)
- Layout is expected to be adaptive (more than a simple stretching)
- Autosized cells (if performance is acceptable)
- WatchKit apps require Storyboards.
To memory of -setFrame: and CGRectDivide()
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2140997/2013-10-07-001.png)
Common issues adopting Autolayout in the IB
- Ambiguous constraints
- Constraint priorities
- Constraints to Margins
- Equal space between views
- Views group centring
- Intrinsic content size
- Multiline UILabel
- Content hugging priority
- Content compression resistance priority
Ambiguous constraints
- Don't press "Add Missing constraints" button
- understand the reason
- add constraints manually
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141075/debug_info_view.png)
Red circle:
Yellow triangle:
- "Update frames" is our choice
- Other options might update your constraints in unexpected way
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141098/Screen_Shot_2016-01-17_at_20.08.37.png)
Getting rid of:
Ambiguous constraints
Adding constaraints manually
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141144/constraint_examples_2x.png)
There are multiple ways to achieve same result with different constraints
Inequality constraint ambiguity
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141160/Screen_Shot_2016-01-17_at_20.30.59.png)
We can resolve this by adding additional constraints with priority less than required.
For example we can say:
View.width = width @ 750
Constraint priorities
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000;
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750;
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250;
// the priority level with which the view wants to conform to the target size.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141541/BMAInitialChatBozoFooter.storyboard___Edited_2016-01-17_23-41-43.png)
- Its possible to have multiple constraint for same views/attributes
- First constraint is not required thus if there is not enough space it won't be satisfied
Constraints to Margins
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2145080/Screen_Shot_2016-01-19_at_00.33.55.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2145084/dont_preserve_superview_margin.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2145087/preserve_superview_margins.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2145091/I5dXj.png)
Equal space between views
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141189/Screen_Shot_2016-01-17_at_20.51.29.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141191/Screen_Shot_2016-01-17_at_20.53.54.png)
For iOS 9+ we should use UIStackView
Constraints can be setup only on view attributes, but not other constraints.
We have to use dummy spacing views, with equal width constraint and 0 leading/trailing to neighbours
Views group centring
For iOS 9+ we should use UIStackView
Constraints can be setup only on view attributes, but not other constraints.
We have to use dummy container view
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141235/Screen_Shot_2016-01-17_at_20.51.38.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141236/Screen_Shot_2016-01-17_at_20.54.01.png)
Intrinsic content size
- Content-defined size which allows to skip setting explicit constraint for width and/or height
- UILabel and UIButton have intrinsic content size for both width and height
-
Override for custom/frame based views, invalidate it when appropriate
-
You can use UIViewNoIntrinsicMetric for width or height
-
Intrinsic content size of a view refers to its alignment rect, not to its frame.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141288/Screen_Shot_2016-01-17_at_21.47.21.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141311/AL_H_intrinsic_size_placeholder_2x.png)
Multiline UILabel
-
The intrinsic content size of multiline UILabel depends on the width of the lines, which is yet to be determined when solving the constraints.
- (void)layoutSubviews {
[super layoutSubviews];
myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
[super layoutSubviews];
}
- preferredMaxLayoutWidth, specifies the maximum line width for calculating the intrinsic content size.
- Supposed to work without extra code in iOS 8+
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141352/images.jpeg)
Content hugging priority
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141475/Screen_Shot_2016-01-17_at_23.06.57.png)
Content compression resistance priority
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2141484/Screen_Shot_2016-01-17_at_23.07.12.png)
- Only take effect for views which define an intrinsic content size.
- Translated into constraints implicitly
H:[label(<=100@250)]
V:[label(<=30@250)]
H:[label(>=100@750)]
V:[label(>=30@750)]
Getting the most of IB using
Size Classes
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144750/iphone01_size_class_v_2x.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144751/iphone01_size_class_h_2x.png)
Size Classes depends on:
- device
- orientation
- special cases like split view
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144796/iphone02_size_class_h_2x.png)
<- iPhone 6 Plus is exception
iPhones' Size Classes
Selecting a Size Class in
Interface Builder
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144845/sc_size_tool_compact_regular_2x.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144847/sc_size_tool_any_any_2x.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144848/sc_size_tool_compact_any_2x.png)
- Changing size or position of views
-
Adding or removing views
-
Adding or removing constraints
-
Changing the font in labels, fields, text views, and buttons
Changes available for different size classes
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144861/SC_H_constraint_sizeclass_popup_2x.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144866/SC_H_new_constant_item_2x.png)
Principle of least astonishment using the IB
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144909/Screen_Shot_2016-01-18_at_23.25.27.png)
Counterexamples
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144940/Screen_Shot_2016-01-18_at_23.37.19.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144952/Screen_Shot_2016-01-18_at_23.57.40.png)
Principle of least astonishment using the IB
Examples
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144955/Screen_Shot_2016-01-18_at_23.56.10.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144959/Screen_Shot_2016-01-18_at_23.56.00.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144963/Screen_Shot_2016-01-18_at_23.40.25.png)
Dos:
- Put dummy values into labels/buttons
- Put dummy background color
- Put dummy image into UIViews
- Use IBDesignable and IBInspectable
Don'ts:
- Don't use User Defined Runtime Attributes
- Don't override values in code with same value.
- Avoid removing views at runtime, prefer deactivating constraints
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144993/Screen_Shot_2016-01-19_at_00.03.57.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2144962/Screen_Shot_2016-01-18_at_23.55.00.png)
Principle of least astonishment using the IB
IB tips and tricks
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2148830/sizeToFitContentImage.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2148844/sizeToFitContentView.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2148845/holdOption.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2148876/xcode-open-file.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2148878/5a4fc-control_select.png)
SHIFT + OPT + LMB
SHIFT + CTRL + LMB
IB tips and tricks
^ + OPT
Top level objects in Scene Dock
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187509/Screen_Shot_2016-01-30_at_19.06.24.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187510/Monosnap_2016-01-30_19-09-01.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187513/Screen_Shot_2016-01-13_at_22.36.33.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187514/Screen_Shot_2016-01-13_at_22.37.00.png)
Drag with CTRL to view
<+ SHIFT
to add multiple
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187917/Monosnap_2016-01-30_22-48-10.png)
^ LMB + LMB
IB tips and tricks
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2149079/IBInspectable-CCS.gif)
@IBInspectable / @IBDesignable
IB tips and tricks
Simulated metrics
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2149127/screen-shot-2014-09-22-at-17-43-15.png)
Strong Outlets (again)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187526/Insert_Connection_2016-01-30_19-24-34.png)
Сanvas configuration
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187839/Screen_Shot_2016-01-30_at_21.50.47.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187907/Screen_Shot_2016-01-30_at_22.35.14.png)
Multiple nav bar items
IB tips and tricks
Сanvas configuration
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187839/Screen_Shot_2016-01-30_at_21.50.47.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2187907/Screen_Shot_2016-01-30_at_22.35.14.png)
Multiple nav bar items
Working with Autolayout
in a code
- Don't work with Autolayout in code
- Don't add constraints in code
- Don't be naive about Visual Format Language
- Don't apply transform to views with top/left/bottom/right constraints
- Don't change constraints inLayoutSubviews
- NSLayoutConstraint has only one* mutable property - constant.
- Activate/deactivate constraints instead of adding/removing
- Minimise amount of constraints with outlets
- Beware of what you animating
NSLayoutAnchor/UILayoutGuide available since iOS 9
Working with Autolayout
in a code
Animation
// Ensures that all pending layout operations have been completed
[containerView layoutIfNeeded];
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
// Forces the layout of the subtree animation block and captures all of the frame changes
[containerView layoutIfNeeded];
}];
Opting out from Autolayout
override func layoutSubviews() {
mySubview.frame = CGRectMake(...)
}
Working with Autolayout
in a code
- (void)updateConstraints {
[self removeConstraints:self.imageViewConstraints];
self.imageViewConstraints = [self createConstraints];
[self addConstraints:self.imageViewConstraints];
[super updateConstraints];
}
Updating/Adding constraints
Baseline alignment
- (UIView *)viewForBaselineLayout
{
// create the view if not exists, start with rect zero
if (!self.baselineView) {
...
}
int integerBaseline = ...
// update the frame of the view
self.baselineView.frame = CGRectMake(0, 0, self.bounds.size.width, (float)integerBaseline);
return self.baselineView;
}
Working with Autolayout
in a code
activateConstraints: instead of addConstraints:
translatesAutoresizingMaskIntoConstraints
- YES for any view you programmatically create.
- NO If you add views in Interface Builder
- Automatically adds the constraints to the correct view.
systemLayoutSizeFittingSize:
- CGSize UILayoutFittingCompressedSize
- CGSize UILayoutFittingExpandedSize
- UILayoutPriorityFittingSizeLevel = 50
Working with Autolayout
in a code
Alignment rects
- - alignmentRectForFrame:
- - frameForAlignmentRect:
- - alignmentRectInsets
![](https://s3.amazonaws.com/media-p.slid.es/uploads/160123/images/2189758/kKXN1.png)
Debugging Autolayout
Use identifiers
- Constraints identifiers
- Views identifiers
- UILayoutGuides identifiers
Debugging methods
- constraintsAffectingLayoutForAxis:
- hasAmbiguousLayout
- exerciseAmbiguityInLayout
*<UIWindow:0x7fe781d25a80> - AMBIGUOUS LAYOUT
| *<UIView:0x7fe781d29db0>
| | *<UIButton:0x7fe781d2a170>
| | | <UIButtonLabel:0x7fe781c107d0>
| | *<UISlider:0x7fe781d2b9b0>
| | | <UIView:0x7fe781d37fe0>
| | | | <UIImageView:0x7fe781d3a780>
| | | <UIImageView:0x7fe781d3ac50>
| | | <UIImageView:0x7fe781d3e590>
| | | | <UIImageView:0x7fe781d39a00>
| | *<_UILayoutGuide:0x7fe781d2c4b0> - AMBIGUOUS LAYOUT
| | *<_UILayoutGuide:0x7fe781d2cda0> - AMBIGUOUS LAYOUT
expr ((UIView *)0x7731880).backgroundColor = [UIColor purpleColor]
po [[UIWindow keyWindow] _autolayoutTrace];
Storyboards
- Merges
- No DI via initialiser
- Segues (Unwind segues)
- Space to grow your screen naturally
- Refactoring with Storyboard References
DEMO
IB
By borlov
IB
- 1,267