Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions Docs/Benchmark-PinLayout-SourceCode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<h1 align="center" style="color: #376C9D; font-family: Arial Black, Gadget, sans-serif; font-size: 1.5em">PinLayout Benchmark Source code</h1>

PinLayout's layout code is concise, clean and doesn't contain any computation compared to Manual Layouting source code.


#### Manual layouting benchmark source code


```
override func layoutSubviews() {
super.layoutSubviews()

optionsLabel.frame = CGRect(x: bounds.width-optionsLabel.frame.width, y: 0,
width: optionsLabel.frame.width, height: optionsLabel.frame.height)
actionLabel.frame = CGRect(x: 0, y: 0, width: bounds.width-optionsLabel.frame.width, height: 0)
actionLabel.sizeToFit()

posterImageView.frame = CGRect(x: 0, y: actionLabel.frame.bottom,
width: posterImageView.frame.width, height: 0)
posterImageView.sizeToFit()

let contentInsets = UIEdgeInsets(top: 0, left: 1, bottom: 2, right: 3)
let posterLabelWidth = bounds.width-posterImageView.frame.width - contentInsets.left -
contentInsets.right
posterNameLabel.frame = CGRect(x: posterImageView.frame.right + contentInsets.left,
y: posterImageView.frame.origin.y + contentInsets.top,
width: posterLabelWidth, height: 0)
posterNameLabel.sizeToFit()

let spacing: CGFloat = 1
posterHeadlineLabel.frame = CGRect(x: posterImageView.frame.right + contentInsets.left,
y: posterNameLabel.frame.bottom + spacing,
width: posterLabelWidth, height: 0)
posterHeadlineLabel.sizeToFit()

posterTimeLabel.frame = CGRect(x: posterImageView.frame.right + contentInsets.left,
y: posterHeadlineLabel.frame.bottom + spacing, width: posterLabelWidth,
height: 0)
posterTimeLabel.sizeToFit()

posterCommentLabel.frame = CGRect(x: 0, y: max(posterImageView.frame.bottom,
posterTimeLabel.frame.bottom +
contentInsets.bottom),
width: frame.width, height: 0)
posterCommentLabel.sizeToFit()

contentImageView.frame = CGRect(x: frame.width/2 - contentImageView.frame.width/2,
y: posterCommentLabel.frame.bottom, width: frame.width, height: 0)
contentImageView.sizeToFit()

contentTitleLabel.frame = CGRect(x: 0, y: contentImageView.frame.bottom, width: frame.width, height: 0)
contentTitleLabel.sizeToFit()

contentDomainLabel.frame = CGRect(x: 0, y: contentTitleLabel.frame.bottom, width: frame.width, height: 0)
contentDomainLabel.sizeToFit()

likeLabel.frame = CGRect(x: 0, y: contentDomainLabel.frame.bottom, width: 0, height: 0)
likeLabel.sizeToFit()

commentLabel.sizeToFit()
commentLabel.frame = CGRect(x: frame.width/2-commentLabel.frame.width/2,
y: contentDomainLabel.frame.bottom,
width: commentLabel.frame.width, height: commentLabel.frame.height)

shareLabel.sizeToFit()
shareLabel.frame = CGRect(x: frame.width-shareLabel.frame.width, y: contentDomainLabel.frame.bottom,
width: shareLabel.frame.width, height: shareLabel.frame.height)

actorImageView.frame = CGRect(x: 0, y: likeLabel.frame.bottom, width: 0, height: 0)
actorImageView.sizeToFit()

actorCommentLabel.frame = CGRect(x: actorImageView.frame.right, y: likeLabel.frame.bottom,
width: frame.width-actorImageView.frame.width, height: 0)
actorCommentLabel.sizeToFit()
}
```

### PinLayout benchmark source code

```
override func layoutSubviews() {
super.layoutSubviews()

let hMargin: CGFloat = 8
let vMargin: CGFloat = 2

optionsLabel.pin.topRight().margin(hMargin)
actionLabel.pin.topLeft().margin(hMargin)

posterImageView.pin.below(of: actionLabel, aligned: .left).marginTop(10)
posterNameLabel.pin.right(of: posterImageView, aligned: .top).margin(-6, 6).right(hMargin).sizeToFit()
posterHeadlineLabel.pin.below(of: posterNameLabel, aligned: .left).right(hMargin).marginTop(1).sizeToFit()
posterTimeLabel.pin.below(of: posterHeadlineLabel, aligned: .left).right(hMargin).marginTop(1).sizeToFit()

posterCommentLabel.pin.below(of: posterTimeLabel).left(hMargin).right().right(hMargin)
.marginTop(vMargin).sizeToFit()

contentImageView.pin.below(of: posterCommentLabel).hCenter().width(100%).sizeToFit()
contentTitleLabel.pin.below(of: contentImageView).left().right().marginHorizontal(hMargin).sizeToFit()
contentDomainLabel.pin.below(of: contentTitleLabel, aligned: .left).right().marginRight(hMargin)
.sizeToFit()

likeLabel.pin.below(of: contentDomainLabel, aligned: .left).marginTop(vMargin)
commentLabel.pin.top(to: likeLabel.edge.top).hCenter(50%)
shareLabel.pin.top(to: likeLabel.edge.top).right().marginRight(hMargin)

actorImageView.pin.below(of: likeLabel, aligned: .left).marginTop(vMargin)
actorCommentLabel.pin.right(of: actorImageView, aligned: .center).marginLeft(4)
}
```
51 changes: 51 additions & 0 deletions Docs/Benchmark.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<p align="center">
<img src="pinlayout-logo-small.png" alt="PinLayout Performance" width=100/>
</p>

<h1 align="center" style="color: #376C9D; font-family: Arial Black, Gadget, sans-serif; font-size: 1.5em">PinLayout Benchmark</h1>

## Methodology

##### LayoutKit Benchmark
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.

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

The benchmark include tests for the following layout systems:

* Auto layout
* Auto layout using UIStackViews
* LayoutKit
* Manual layout (i.e. set UIView's frame directly)
* PinLayout

Anyone who would like to integrate any other layout frameworks to this GitHub repository is welcome.

##### Benchmark details
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.

Here are the rendering results to compare visual results:

* [Auto layout rendering result](Benchmark/Benchmark-Autolayout.png)
* [PinLayout rendering result](Benchmark/Benchmark-PinLayout.png)
* [LayoutKit rendering result](Benchmark/Benchmark-LayoutKit.png)

## Results

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.

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), ...).

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.

<br>

<p align="center">iPhone 6S - iOS 10.3.2</p>
<p align="center">
<a href=""><img src="Benchmark/Chart-iPhone6S.png" alt="PinLayout Performance"/></a>
<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>
</p>

You can have a look at the [spreadsheet containing all the data](Benchmark/Benchmark-iPhone6S.xlsx)

### Other device's chart will be coming soon...
Binary file added Docs/Benchmark/Benchmark-Autolayout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Benchmark/Benchmark-LayoutKit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Benchmark/Benchmark-PinLayout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/Benchmark/Benchmark-iPhone6S.xlsx
Binary file not shown.
Binary file added Docs/Benchmark/Chart-iPhone6S.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
99 changes: 60 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

<br>

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

> "No auto-layout constraints attached"

<br>

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

* [Installation](#installation)
* [FAQ](#faq)
* [Comments, ideas, suggestions, issues, ....](#comments)

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

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

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

<br>

## Installation <a name="installation"></a>

### CocoaPods

To integrate PinLayout into your Xcode project using CocoaPods, specify it in your `Podfile`:

```ruby
pod 'PinLayout'
```
# PinLayout's Performance <a name="performance"></a>

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

### Carthage
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**.

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

```ogdl
github "mirego/PinLayout"
```
[More details and explanation of the benchmark](Docs/Benchmark.md)

Then, run `carthage update` to build the framework and drag the built `PinLayout.framework` into your Xcode project.

### Swift Package Manager

Once you have your Swift package set up, you only need to add PinLayout as a dependency of your `Package.swift`.

```ogdl
dependencies: [
.Package(url: "https://github.com/mirego/PinLayout.git", majorVersion: 1)
]
```
<p align="center"> Tested on a iPhone 6S iOS 10.3.2</p>
<p align="center">
<img src="Docs/Benchmark/Chart-iPhone6S.png" alt="PinLayout Performance" width=650/>
<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>
</p>

<br>
<br/>

# Usage sample
###### Example:
Expand Down Expand Up @@ -133,13 +119,12 @@ override func layoutSubviews() {
}
```

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

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

<br/>


# Documentation <a name="documentation"></a>

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

<br>

## Installation <a name="installation"></a>

### CocoaPods

To integrate PinLayout into your Xcode project using CocoaPods, specify it in your `Podfile`:

```ruby
pod 'PinLayout'
```

Then, run `pod install`.

### Carthage

To integrate PinLayout into your Xcode project using Carthage, specify it in your `Cartfile`:

```ogdl
github "mirego/PinLayout"
```

Then, run `carthage update` to build the framework and drag the built `PinLayout.framework` into your Xcode project.

### Swift Package Manager

Once you have your Swift package set up, you only need to add PinLayout as a dependency of your `Package.swift`.

```ogdl
dependencies: [
.Package(url: "https://github.com/mirego/PinLayout.git", majorVersion: 1)
]
```

<br>

## Coming soon <a name="coming_soon"></a>
* minWidth/maxWidth, minHeight/maxHeight
* CALayer support
Expand All @@ -808,17 +827,19 @@ Cell D:
```
<br>

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

<br>

## Contributing
1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D
### Thanks
PinLayout was inspired by other great layout frameworks, including:

* [MCUIViewLayout](https://github.com/mirego/MCUIViewLayout)
* HTML's CSS
* [SnapKit](https://github.com/SnapKit/SnapKit)
* [Stevia](https://github.com/freshOS/Stevia)
* ... and even Auto layout :-)


## License
BSD 3-Clause License