Skip to content

Commit d5bf136

Browse files
author
Luc Dion
authored
Merge pull request #40 from mirego/performance_documentation
Performance documentation
2 parents 75e5a26 + b7008c4 commit d5bf136

9 files changed

+221
-39
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<h1 align="center" style="color: #376C9D; font-family: Arial Black, Gadget, sans-serif; font-size: 1.5em">PinLayout Benchmark Source code</h1>
2+
3+
PinLayout's layout code is concise, clean and doesn't contain any computation compared to Manual Layouting source code.
4+
5+
6+
#### Manual layouting benchmark source code
7+
8+
9+
```
10+
override func layoutSubviews() {
11+
super.layoutSubviews()
12+
13+
optionsLabel.frame = CGRect(x: bounds.width-optionsLabel.frame.width, y: 0,
14+
width: optionsLabel.frame.width, height: optionsLabel.frame.height)
15+
actionLabel.frame = CGRect(x: 0, y: 0, width: bounds.width-optionsLabel.frame.width, height: 0)
16+
actionLabel.sizeToFit()
17+
18+
posterImageView.frame = CGRect(x: 0, y: actionLabel.frame.bottom,
19+
width: posterImageView.frame.width, height: 0)
20+
posterImageView.sizeToFit()
21+
22+
let contentInsets = UIEdgeInsets(top: 0, left: 1, bottom: 2, right: 3)
23+
let posterLabelWidth = bounds.width-posterImageView.frame.width - contentInsets.left -
24+
contentInsets.right
25+
posterNameLabel.frame = CGRect(x: posterImageView.frame.right + contentInsets.left,
26+
y: posterImageView.frame.origin.y + contentInsets.top,
27+
width: posterLabelWidth, height: 0)
28+
posterNameLabel.sizeToFit()
29+
30+
let spacing: CGFloat = 1
31+
posterHeadlineLabel.frame = CGRect(x: posterImageView.frame.right + contentInsets.left,
32+
y: posterNameLabel.frame.bottom + spacing,
33+
width: posterLabelWidth, height: 0)
34+
posterHeadlineLabel.sizeToFit()
35+
36+
posterTimeLabel.frame = CGRect(x: posterImageView.frame.right + contentInsets.left,
37+
y: posterHeadlineLabel.frame.bottom + spacing, width: posterLabelWidth,
38+
height: 0)
39+
posterTimeLabel.sizeToFit()
40+
41+
posterCommentLabel.frame = CGRect(x: 0, y: max(posterImageView.frame.bottom,
42+
posterTimeLabel.frame.bottom +
43+
contentInsets.bottom),
44+
width: frame.width, height: 0)
45+
posterCommentLabel.sizeToFit()
46+
47+
contentImageView.frame = CGRect(x: frame.width/2 - contentImageView.frame.width/2,
48+
y: posterCommentLabel.frame.bottom, width: frame.width, height: 0)
49+
contentImageView.sizeToFit()
50+
51+
contentTitleLabel.frame = CGRect(x: 0, y: contentImageView.frame.bottom, width: frame.width, height: 0)
52+
contentTitleLabel.sizeToFit()
53+
54+
contentDomainLabel.frame = CGRect(x: 0, y: contentTitleLabel.frame.bottom, width: frame.width, height: 0)
55+
contentDomainLabel.sizeToFit()
56+
57+
likeLabel.frame = CGRect(x: 0, y: contentDomainLabel.frame.bottom, width: 0, height: 0)
58+
likeLabel.sizeToFit()
59+
60+
commentLabel.sizeToFit()
61+
commentLabel.frame = CGRect(x: frame.width/2-commentLabel.frame.width/2,
62+
y: contentDomainLabel.frame.bottom,
63+
width: commentLabel.frame.width, height: commentLabel.frame.height)
64+
65+
shareLabel.sizeToFit()
66+
shareLabel.frame = CGRect(x: frame.width-shareLabel.frame.width, y: contentDomainLabel.frame.bottom,
67+
width: shareLabel.frame.width, height: shareLabel.frame.height)
68+
69+
actorImageView.frame = CGRect(x: 0, y: likeLabel.frame.bottom, width: 0, height: 0)
70+
actorImageView.sizeToFit()
71+
72+
actorCommentLabel.frame = CGRect(x: actorImageView.frame.right, y: likeLabel.frame.bottom,
73+
width: frame.width-actorImageView.frame.width, height: 0)
74+
actorCommentLabel.sizeToFit()
75+
}
76+
```
77+
78+
### PinLayout benchmark source code
79+
80+
```
81+
override func layoutSubviews() {
82+
super.layoutSubviews()
83+
84+
let hMargin: CGFloat = 8
85+
let vMargin: CGFloat = 2
86+
87+
optionsLabel.pin.topRight().margin(hMargin)
88+
actionLabel.pin.topLeft().margin(hMargin)
89+
90+
posterImageView.pin.below(of: actionLabel, aligned: .left).marginTop(10)
91+
posterNameLabel.pin.right(of: posterImageView, aligned: .top).margin(-6, 6).right(hMargin).sizeToFit()
92+
posterHeadlineLabel.pin.below(of: posterNameLabel, aligned: .left).right(hMargin).marginTop(1).sizeToFit()
93+
posterTimeLabel.pin.below(of: posterHeadlineLabel, aligned: .left).right(hMargin).marginTop(1).sizeToFit()
94+
95+
posterCommentLabel.pin.below(of: posterTimeLabel).left(hMargin).right().right(hMargin)
96+
.marginTop(vMargin).sizeToFit()
97+
98+
contentImageView.pin.below(of: posterCommentLabel).hCenter().width(100%).sizeToFit()
99+
contentTitleLabel.pin.below(of: contentImageView).left().right().marginHorizontal(hMargin).sizeToFit()
100+
contentDomainLabel.pin.below(of: contentTitleLabel, aligned: .left).right().marginRight(hMargin)
101+
.sizeToFit()
102+
103+
likeLabel.pin.below(of: contentDomainLabel, aligned: .left).marginTop(vMargin)
104+
commentLabel.pin.top(to: likeLabel.edge.top).hCenter(50%)
105+
shareLabel.pin.top(to: likeLabel.edge.top).right().marginRight(hMargin)
106+
107+
actorImageView.pin.below(of: likeLabel, aligned: .left).marginTop(vMargin)
108+
actorCommentLabel.pin.right(of: actorImageView, aligned: .center).marginLeft(4)
109+
}
110+
```

Docs/Benchmark.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<p align="center">
2+
<img src="pinlayout-logo-small.png" alt="PinLayout Performance" width=100/>
3+
</p>
4+
5+
<h1 align="center" style="color: #376C9D; font-family: Arial Black, Gadget, sans-serif; font-size: 1.5em">PinLayout Benchmark</h1>
6+
7+
## Methodology
8+
9+
##### LayoutKit Benchmark
10+
PinLayout's performance has been tested using a fork of [LayoutKit](https://github.com/mirego/LayoutKit). LayoutKit include an example app with a really nice and simple benchmark. It is used to compare LayoutKit with Auto layout, UIStackViews and manual layouting.
11+
12+
The benchmark has been modified to also include [PinLayout](https://github.com/mirego/LayoutKit/blob/master/LayoutKitSampleApp/Benchmarks/FeedItemPinLayoutView.swift). Remark in the implemantation that PinLayout's layout code is concise, clean and doesn't contain any computation [compared to Manual Layouting source code](Benchmark-PinLayout-SourceCode.md).
13+
14+
The benchmark include tests for the following layout systems:
15+
16+
* Auto layout
17+
* Auto layout using UIStackViews
18+
* LayoutKit
19+
* Manual layout (i.e. set UIView's frame directly)
20+
* PinLayout
21+
22+
Anyone who would like to integrate any other layout frameworks to this GitHub repository is welcome.
23+
24+
##### Benchmark details
25+
The LayoutKit benchmark layout UICollectionView and UITableView cells in multiple pass, each pass contains more cells than the previous one. The **X axis** in following charts indicates the number of cell contained for each pass. The **Y axis** indicates the number of miliseconds to render all cells from one pass.
26+
27+
Here are the rendering results to compare visual results:
28+
29+
* [Auto layout rendering result](Benchmark/Benchmark-Autolayout.png)
30+
* [PinLayout rendering result](Benchmark/Benchmark-PinLayout.png)
31+
* [LayoutKit rendering result](Benchmark/Benchmark-LayoutKit.png)
32+
33+
## Results
34+
35+
As you can see in the following chart, PinLayout's performance is as fast as manual layouting, and up to **12x faster than auto layout**, and **16x faster than UIStackViews**. [LayoutKit](https://github.com/linkedin/LayoutKit) is also really fast, slightly slower than PinLayout and manual layouting.
36+
37+
These results also means that PinLayout is by far faster than any layout frameworks that is built over auto layout ([SnapKit](https://github.com/SnapKit/SnapKit), [Stevia](https://github.com/freshOS/Stevia), [PureLayout](https://github.com/PureLayout/PureLayout), ...).
38+
39+
It takes almost half a second (0.468 ms) to render 100 UICollectionView's cells using UIStackViews, and 1/3 of second (0.344) using auto layout on a iPhone 6S device.
40+
41+
<br>
42+
43+
<p align="center">iPhone 6S - iOS 10.3.2</p>
44+
<p align="center">
45+
<a href=""><img src="Benchmark/Chart-iPhone6S.png" alt="PinLayout Performance"/></a>
46+
<p align="center" style="font-size:10px;">X axis in the number cells in a UICollectionView, and Y axis is the time in miliseconds to layout all cells.</p>
47+
</p>
48+
49+
You can have a look at the [spreadsheet containing all the data](Benchmark/Benchmark-iPhone6S.xlsx)
50+
51+
### Other device's chart will be coming soon...
106 KB
Loading
124 KB
Loading
94.7 KB
Loading
16.4 KB
Binary file not shown.

Docs/Benchmark/Chart-iPhone6S.png

145 KB
Loading
31.2 KB
Binary file not shown.

README.md

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616

1717
<br>
1818

19-
Swift manual views layouting without auto layout, no magic, pure code, full control. Concise syntax, readable & chainable.
19+
Swift manual views layouting without auto layout, no magic, pure code, full control and FAST! Concise syntax, readable & chainable.
2020

2121
> "No auto-layout constraints attached"
2222
2323
<br>
2424

2525
* [PinLayout principles and philosophy](#introduction)
26-
* [Installation](#installation)
26+
* [Performance](#performance)
2727
* [Documentation](#documentation)
2828
* [Layout using distances from superview’s edges](#distance_from_superview_edge)
2929
* [Anchors](#anchors)
@@ -34,6 +34,7 @@ Swift manual views layouting without auto layout, no magic, pure code, full cont
3434
* [Warnings](#warnings)
3535
* [More examples](#more_examples)
3636

37+
* [Installation](#installation)
3738
* [FAQ](#faq)
3839
* [Comments, ideas, suggestions, issues, ....](#comments)
3940

@@ -42,6 +43,7 @@ Swift manual views layouting without auto layout, no magic, pure code, full cont
4243
## PinLayout principles and philosophy <a name="introduction"></a>
4344

4445
* Manual layouting. No magic, pure code, full control.
46+
* Fast, PinLayout exist to be simple and fast as possible! In fact it is fast as manual layouting. See [performance results below.](#performance)
4547
* Layout one view at a time.
4648
* Concise syntax. Layout most views using a single line.
4749

@@ -71,39 +73,23 @@ A view can be layouted using PinLayout and later with another method/framework.
7173

7274
<br>
7375

74-
## Installation <a name="installation"></a>
75-
76-
### CocoaPods
77-
78-
To integrate PinLayout into your Xcode project using CocoaPods, specify it in your `Podfile`:
79-
80-
```ruby
81-
pod 'PinLayout'
82-
```
76+
# PinLayout's Performance <a name="performance"></a>
8377

84-
Then, run `pod install`.
78+
PinLayout's performance has been measured using the excellent LayoutKit benchmark. PinLayout has been added to this benchmark to compare its performance.
8579

86-
### Carthage
80+
As you can see in the following chart, PinLayout's performance is as fast as manual layouting, and up to **12x faster than auto layout**, and **16x faster than UIStackViews**.
8781

88-
To integrate PinLayout into your Xcode project using Carthage, specify it in your `Cartfile`:
82+
These results also means that **PinLayout is by far faster than any layout frameworks that is built over auto layout**.
8983

90-
```ogdl
91-
github "mirego/PinLayout"
92-
```
84+
[More details and explanation of the benchmark](Docs/Benchmark.md)
9385

94-
Then, run `carthage update` to build the framework and drag the built `PinLayout.framework` into your Xcode project.
95-
96-
### Swift Package Manager
97-
98-
Once you have your Swift package set up, you only need to add PinLayout as a dependency of your `Package.swift`.
99-
100-
```ogdl
101-
dependencies: [
102-
.Package(url: "https://github.com/mirego/PinLayout.git", majorVersion: 1)
103-
]
104-
```
86+
<p align="center"> Tested on a iPhone 6S iOS 10.3.2</p>
87+
<p align="center">
88+
<img src="Docs/Benchmark/Chart-iPhone6S.png" alt="PinLayout Performance" width=650/>
89+
<p align="center" style="font-size:9px;">X axis in the number cells in a UICollectionView, and Y axis is the time in miliseconds to layout all cells.</p>
90+
</p>
10591

106-
<br>
92+
<br/>
10793

10894
# Usage sample
10995
###### Example:
@@ -133,13 +119,12 @@ override func layoutSubviews() {
133119
}
134120
```
135121

136-
:pushpin: This example and some other examples are available in the **PinLayoutSample** project. Please note that you must do a `pod install` before running the sample project.
122+
:pushpin: This example and some other examples are available in the **Example** project. Please note that you must do a `pod install` before running the example project.
137123

138124
:pushpin: PinLayout doesn't use auto layout constraints, it is a framework that manually layout views. For that reason you need to update the layout inside either `UIView.layoutSubviews()` or `UIViewController.viewDidLayoutSubviews()` to handle container size's changes, including device rotation. You'll also need to handle UITraitCollection changes for app's that support multitask. In the example above PinLayout's commands are inside UIView's `layoutSubviews()` method.
139125

140126
<br/>
141127

142-
143128
# Documentation <a name="documentation"></a>
144129

145130
## Layout using distances from superview’s edges <a name="distance_from_superview_edge"></a>
@@ -788,6 +773,40 @@ Cell D:
788773

789774
<br>
790775

776+
## Installation <a name="installation"></a>
777+
778+
### CocoaPods
779+
780+
To integrate PinLayout into your Xcode project using CocoaPods, specify it in your `Podfile`:
781+
782+
```ruby
783+
pod 'PinLayout'
784+
```
785+
786+
Then, run `pod install`.
787+
788+
### Carthage
789+
790+
To integrate PinLayout into your Xcode project using Carthage, specify it in your `Cartfile`:
791+
792+
```ogdl
793+
github "mirego/PinLayout"
794+
```
795+
796+
Then, run `carthage update` to build the framework and drag the built `PinLayout.framework` into your Xcode project.
797+
798+
### Swift Package Manager
799+
800+
Once you have your Swift package set up, you only need to add PinLayout as a dependency of your `Package.swift`.
801+
802+
```ogdl
803+
dependencies: [
804+
.Package(url: "https://github.com/mirego/PinLayout.git", majorVersion: 1)
805+
]
806+
```
807+
808+
<br>
809+
791810
## Coming soon <a name="coming_soon"></a>
792811
* minWidth/maxWidth, minHeight/maxHeight
793812
* CALayer support
@@ -808,17 +827,19 @@ Cell D:
808827
```
809828
<br>
810829

811-
## Comments, ideas, suggestions, issues, .... <a name="comments"></a>
830+
### Comments, ideas, suggestions, issues, .... <a name="comments"></a>
812831
For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues).
813832

814-
<br>
815833

816-
## Contributing
817-
1. Fork it!
818-
2. Create your feature branch: `git checkout -b my-new-feature`
819-
3. Commit your changes: `git commit -am 'Add some feature'`
820-
4. Push to the branch: `git push origin my-new-feature`
821-
5. Submit a pull request :D
834+
### Thanks
835+
PinLayout was inspired by other great layout frameworks, including:
836+
837+
* [MCUIViewLayout](https://github.com/mirego/MCUIViewLayout)
838+
* HTML's CSS
839+
* [SnapKit](https://github.com/SnapKit/SnapKit)
840+
* [Stevia](https://github.com/freshOS/Stevia)
841+
* ... and even Auto layout :-)
842+
822843

823844
## License
824845
BSD 3-Clause License

0 commit comments

Comments
 (0)