diff --git a/Docs/Benchmark-PinLayout-SourceCode.md b/Docs/Benchmark-PinLayout-SourceCode.md new file mode 100644 index 00000000..fa0545a7 --- /dev/null +++ b/Docs/Benchmark-PinLayout-SourceCode.md @@ -0,0 +1,110 @@ +

PinLayout Benchmark Source code

+ +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) +} +``` diff --git a/Docs/Benchmark.md b/Docs/Benchmark.md new file mode 100644 index 00000000..46a76ded --- /dev/null +++ b/Docs/Benchmark.md @@ -0,0 +1,51 @@ +

+ PinLayout Performance +

+ +

PinLayout Benchmark

+ +## 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. + +
+ +

iPhone 6S - iOS 10.3.2

+

+ PinLayout Performance +

X axis in the number cells in a UICollectionView, and Y axis is the time in miliseconds to layout all cells.

+

+ +You can have a look at the [spreadsheet containing all the data](Benchmark/Benchmark-iPhone6S.xlsx) + +### Other device's chart will be coming soon... diff --git a/Docs/Benchmark/Benchmark-Autolayout.png b/Docs/Benchmark/Benchmark-Autolayout.png new file mode 100644 index 00000000..016b9ce1 Binary files /dev/null and b/Docs/Benchmark/Benchmark-Autolayout.png differ diff --git a/Docs/Benchmark/Benchmark-LayoutKit.png b/Docs/Benchmark/Benchmark-LayoutKit.png new file mode 100644 index 00000000..8ed6bac5 Binary files /dev/null and b/Docs/Benchmark/Benchmark-LayoutKit.png differ diff --git a/Docs/Benchmark/Benchmark-PinLayout.png b/Docs/Benchmark/Benchmark-PinLayout.png new file mode 100644 index 00000000..179ea540 Binary files /dev/null and b/Docs/Benchmark/Benchmark-PinLayout.png differ diff --git a/Docs/Benchmark/Benchmark-iPhone6S.xlsx b/Docs/Benchmark/Benchmark-iPhone6S.xlsx new file mode 100644 index 00000000..be936055 Binary files /dev/null and b/Docs/Benchmark/Benchmark-iPhone6S.xlsx differ diff --git a/Docs/Benchmark/Chart-iPhone6S.png b/Docs/Benchmark/Chart-iPhone6S.png new file mode 100644 index 00000000..d9e49cae Binary files /dev/null and b/Docs/Benchmark/Chart-iPhone6S.png differ diff --git a/Docs/Benchmark/LayoutKit-Benchmark-with-PinLayout-iPhone6S.xlsx b/Docs/Benchmark/LayoutKit-Benchmark-with-PinLayout-iPhone6S.xlsx new file mode 100644 index 00000000..af562bd4 Binary files /dev/null and b/Docs/Benchmark/LayoutKit-Benchmark-with-PinLayout-iPhone6S.xlsx differ diff --git a/README.md b/README.md index 0f0aea1b..3ac4a235 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@
-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"
* [PinLayout principles and philosophy](#introduction) -* [Installation](#installation) +* [Performance](#performance) * [Documentation](#documentation) * [Layout using distances from superview’s edges](#distance_from_superview_edge) * [Anchors](#anchors) @@ -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) @@ -42,6 +43,7 @@ Swift manual views layouting without auto layout, no magic, pure code, full cont ## PinLayout principles and philosophy * 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. @@ -71,39 +73,23 @@ A view can be layouted using PinLayout and later with another method/framework.
-## Installation - -### CocoaPods - -To integrate PinLayout into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```ruby - pod 'PinLayout' -``` +# PinLayout's Performance -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) -] -``` +

Tested on a iPhone 6S iOS 10.3.2

+

+ PinLayout Performance +

X axis in the number cells in a UICollectionView, and Y axis is the time in miliseconds to layout all cells.

+

-
+
# Usage sample ###### Example: @@ -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.
- # Documentation ## Layout using distances from superview’s edges @@ -788,6 +773,40 @@ Cell D:
+## Installation + +### 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) +] +``` + +
+ ## Coming soon * minWidth/maxWidth, minHeight/maxHeight * CALayer support @@ -808,17 +827,19 @@ Cell D: ```
-## Comments, ideas, suggestions, issues, .... +### Comments, ideas, suggestions, issues, .... For any **comments**, **ideas**, **suggestions**, **issues**, simply open an [issue](https://github.com/mirego/PinLayout/issues). -
-## 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