Revert "Remove old website folder from master branch" (#1818)

This commit is contained in:
appleguy 2016-06-26 17:12:59 -07:00 committed by GitHub
parent f71e207e0a
commit ae87717263
27 changed files with 1994 additions and 0 deletions

2
docs/CNAME Normal file
View File

@ -0,0 +1,2 @@
asyncdisplaykit.org

420
docs/LICENSE.md Normal file
View File

@ -0,0 +1,420 @@
---
layout: page
title: License
permalink: /license/
---
AsyncDisplayKit is free software under the <a
href="https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE">BSD
license</a>.
Code examples and <a
href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples">sample
projects</a> are licensed as follows:
This file provided by Facebook is for non-commercial testing and evaluation
purposes only. Facebook reserves all rights not expressly granted.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
All other AsyncDisplayKit documentation is licensed <a
href="https://creativecommons.org/licenses/by/4.0/">CC-BY-4.0</a>.
Attribution 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution 4.0 International Public License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution 4.0 International Public License ("Public License"). To the
extent this Public License may be interpreted as a contract, You are
granted the Licensed Rights in consideration of Your acceptance of
these terms and conditions, and the Licensor grants You such rights in
consideration of benefits the Licensor receives from making the
Licensed Material available under these terms and conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
d. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
e. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
f. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
g. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
h. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
i. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
j. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
k. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
4. If You Share Adapted Material You produce, the Adapter's
License You apply must not prevent recipients of the Adapted
Material from complying with this Public License.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material; and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public licenses.
Notwithstanding, Creative Commons may elect to apply one of its public
licenses to material it publishes and in those instances will be
considered the "Licensor." Except for the limited purpose of indicating
that material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the public
licenses.
Creative Commons may be contacted at creativecommons.org.

9
docs/README.md Normal file
View File

@ -0,0 +1,9 @@
# Documentation
## Building
You need Jekyll and appledoc. See `build.sh`.
## License
See LICENSE.md.

13
docs/_config.yml Normal file
View File

@ -0,0 +1,13 @@
# Site settings
title: AsyncDisplayKit
description: Smooth asynchronous user interfaces for iOS apps.
baseurl: ""
url: "http://asyncdisplaykit.org"
# Build settings
highlighter: pygments
markdown: redcarpet
exclude:
- README.md
- build.sh

View File

@ -0,0 +1,18 @@
<footer class="site-footer">
<div class="wrapper">
<div class="footer-col-wrapper">
<div class="footer-col footer-col-left">
<p class="text">a Facebook &amp; Instagram collaboration &#x2665;</p>
</div>
<div class="footer-col footer-col-right">
<p class="text">
&copy; 2014 Facebook Inc (<a href="{{ site.baseurl }}/license/">CC-BY-4.0</a>)
</p>
</div>
</div>
</div>
</footer>

24
docs/_includes/head.html Normal file
View File

@ -0,0 +1,24 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta property="og:title" content="{% if page.title %}{{ page.title }} &mdash; {% endif %}AsyncDisplayKit">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
<meta property="og:image" content="{{ site.url }}/assets/logo-square.png">
<meta property="og:description" content="Smooth asynchronous user interfaces for iOS apps">
<title>{% if page.title %}{{ page.title }} &mdash; {% endif %}AsyncDisplayKit</title>
<meta name="description" content="{{ site.description }}">
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
<script>
if (location.host == "facebook.github.io") {
// get outta here
location = 'http://asyncdisplaykit.org';
}
</script>
</head>

View File

@ -0,0 +1,25 @@
<header class="site-header">
<div class="wrapper">
<a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
<nav class="site-nav">
<a href="#" class="menu-icon">
<svg viewBox="0 0 18 15">
<path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
<path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
<path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
</svg>
</a>
<div class="trigger">
<a class="page-link{% if page.sectionid == 'docs' %} page-link-active{% endif %}" href="{{ site.baseurl }}/guide">guide</a>
<a class="page-link" href="{{ site.baseurl }}/appledoc">api</a>
<a class="page-link" href="https://github.com/facebook/AsyncDisplayKit">github</a>
</div>
</nav>
</div>
</header>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
{% include head.html %}
<body>
{% include header.html %}
<div class="page-content">
<div class="wrapper">
{{ content }}
</div>
</div>
{% include footer.html %}
</body>
</html>

27
docs/_layouts/docs.html Normal file
View File

@ -0,0 +1,27 @@
---
layout: default
sectionid: docs
---
<div class="post">
<header class="post-header">
<h1 class="post-title">
{{ page.title }}
<a class="edit-page-link" href="https://github.com/facebook/AsyncDisplayKit/tree/master/docs/{{ page.path }}" target="_blank">[edit]</a>
</h1>
</header>
<article class="post-content">
{{ content }}
</article>
<div class="docs-prevnext">
{% if page.prev %}
<a class="docs-prev" href="{{ site.baseurl }}/{{ page.prev }}">&larr; prev</a>
{% endif %}
{% if page.next %}
<a class="docs-next" href="{{ site.baseurl }}/{{ page.next }}">next &rarr;</a>
{% endif %}
</div>
</div>

16
docs/_layouts/page.html Normal file
View File

@ -0,0 +1,16 @@
---
layout: default
---
<div class="post">
{% if page.shouldDisplayTitle %}
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
</header>
{% endif %}
<article class="post-content">
{{ content }}
</article>
</div>

15
docs/_layouts/post.html Normal file
View File

@ -0,0 +1,15 @@
---
layout: default
---
<div class="post">
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
<p class="post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>
</header>
<article class="post-content">
{{ content }}
</article>
</div>

205
docs/_sass/_base.scss Normal file
View File

@ -0,0 +1,205 @@
/**
* Reset some basic elements
*/
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre, hr,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
/**
* Basic styling
*/
body {
font-family: $base-font-family;
font-size: $base-font-size;
line-height: $base-line-height;
font-weight: 300;
color: $text-color;
background-color: $background-color;
-webkit-text-size-adjust: 100%;
}
/**
* Set `margin-bottom` to maintain vertical rhythm
*/
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
%vertical-rhythm {
margin-bottom: $spacing-unit / 2;
}
/**
* Images
*/
img {
max-width: 100%;
vertical-align: middle;
}
/**
* Figures
*/
figure > img {
display: block;
}
figcaption {
font-size: $small-font-size;
}
/**
* Lists
*/
ul, ol {
margin-left: $spacing-unit;
}
li {
> ul,
> ol {
margin-bottom: 0;
}
}
/**
* Headings
*/
h1, h2, h3, h4, h5, h6 {
font-weight: 300;
}
/**
* Links
*/
a {
color: $brand-color;
text-decoration: none;
&:visited {
color: darken($brand-color, 15%);
}
&:hover {
color: $text-color;
text-decoration: underline;
}
}
/**
* Blockquotes
*/
blockquote {
color: $grey-color;
border-left: 4px solid $grey-color-light;
padding-left: $spacing-unit / 2;
font-size: 18px;
letter-spacing: -1px;
font-style: italic;
> :last-child {
margin-bottom: 0;
}
}
/**
* Code formatting
*/
pre,
code {
font-family: Monaco, monospace;
font-size: 14px;
border: 1px solid #afe4ff;
border-radius: 3px;
background-color: #fafdff;
}
code {
padding: 1px 5px;
}
pre {
padding: 8px 12px;
overflow-x: scroll;
> code {
border: 0;
padding-right: 0;
padding-left: 0;
}
}
/**
* Wrapper
*/
.wrapper {
max-width: -webkit-calc(800px - (#{$spacing-unit} * 2));
max-width: calc(800px - (#{$spacing-unit} * 2));
margin-right: auto;
margin-left: auto;
padding-right: $spacing-unit;
padding-left: $spacing-unit;
@extend %clearfix;
@include media-query($on-laptop) {
max-width: -webkit-calc(800px - (#{$spacing-unit}));
max-width: calc(800px - (#{$spacing-unit}));
padding-right: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
}
/**
* Clearfix
*/
%clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
/**
* Icons
*/
.icon {
> svg {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
path {
fill: $grey-color;
}
}
}

265
docs/_sass/_layout.scss Normal file
View File

@ -0,0 +1,265 @@
/**
* Site header
*/
.site-header {
border-top: 5px solid $grey-color-dark;
border-bottom: 1px solid $grey-color-light;
min-height: 56px;
background-color: #f8f8f8;
// Positioning context for the mobile navigation icon
position: relative;
}
.site-title {
font-size: 26px;
line-height: 56px;
letter-spacing: -1px;
margin-bottom: 0;
float: left;
&,
&:visited {
color: $grey-color-dark;
}
&:hover {
text-decoration: none;
}
}
.site-nav {
float: right;
line-height: 56px;
.menu-icon {
display: none;
}
.page-link {
color: $grey-color;
line-height: $base-line-height;
// Gaps between nav items, but not on the first one
&:not(:first-child) {
margin-left: 20px;
}
&:hover {
color: $text-color;
}
}
.page-link-active {
color: $text-color;
}
@include media-query($on-palm) {
position: absolute;
top: 9px;
right: 30px;
background-color: $background-color;
border: 1px solid $grey-color-light;
border-radius: 5px;
text-align: right;
.menu-icon {
display: block;
float: right;
width: 36px;
height: 26px;
line-height: 0;
padding-top: 10px;
text-align: center;
> svg {
width: 18px;
height: 15px;
path {
fill: $grey-color-dark;
}
}
}
.trigger {
clear: both;
display: none;
}
&:hover .trigger {
display: block;
padding-bottom: 5px;
}
.page-link {
display: block;
padding: 5px 10px;
}
}
}
/**
* Site footer
*/
.site-footer {
border-top: 1px solid $grey-color-light;
padding: $spacing-unit 0;
}
.footer-heading {
font-size: 18px;
margin-bottom: $spacing-unit / 2;
}
.contact-list,
.social-media-list {
list-style: none;
margin-left: 0;
}
.footer-col-wrapper {
font-size: 11px;
color: $grey-color;
margin-left: -$spacing-unit / 2;
@extend %clearfix;
}
.footer-col {
float: left;
margin-bottom: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
.footer-col-left {
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
width: calc(50% - (#{$spacing-unit} / 2));
}
.footer-col-right {
text-align: right;
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
width: calc(50% - (#{$spacing-unit} / 2));
}
@include media-query($on-laptop) {
.footer-col-left {
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
width: calc(50% - (#{$spacing-unit} / 2));
}
.footer-col-right {
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
@include media-query($on-palm) {
.footer-col {
float: none;
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
/**
* Page content
*/
.page-content {
padding: $spacing-unit 0;
background-color: white;
}
.page-heading {
font-size: 20px;
}
.post-list {
margin-left: 0;
list-style: none;
> li {
margin-bottom: $spacing-unit;
}
}
.post-meta {
font-size: $small-font-size;
color: $grey-color;
}
.post-link {
display: block;
font-size: 24px;
}
/**
* Posts
*/
.post-header {
margin-bottom: $spacing-unit;
}
.post-title {
font-size: 42px;
letter-spacing: -1px;
line-height: 1;
@include media-query($on-laptop) {
font-size: 36px;
}
.edit-page-link {
font-size: 18px;
}
}
.post-content {
margin-bottom: $spacing-unit;
h2 {
font-size: 32px;
@include media-query($on-laptop) {
font-size: 28px;
}
}
h3 {
font-size: 26px;
@include media-query($on-laptop) {
font-size: 22px;
}
}
h4 {
font-size: 20px;
@include media-query($on-laptop) {
font-size: 18px;
}
}
}
/**
* Docs
*/
.docs-prevnext {
@extend %clearfix;
}
.docs-prev {
float: left;
}
.docs-next {
float: right;
}

View File

@ -0,0 +1,76 @@
/**
* Syntax highlighting styles
*/
/* not official Xcode colors, but looks better on the web */
$xc-black: black;
$xc-green: #008d14;
$xc-red: #b72748;
$xc-blue: #103ffb;
$xc-turquoise: #3a95ba;
.highlight {
background: #fff;
@extend %vertical-rhythm;
.c { color: $xc-green; font-style: italic } // Comment
.err { color: #a61717; background-color: #e3d2d2 } // Error
.k { color: $xc-blue} // Keyword
.o { } // Operator
.cm { color: $xc-green; font-style: italic } // Comment.Multiline
.cp { color: $xc-red} // Comment.Preproc
.c1 { color: $xc-green; font-style: italic } // Comment.Single
.cs { color: $xc-green; font-weight: bold; font-style: italic } // Comment.Special
.gd { color: #000; background-color: #fdd } // Generic.Deleted
.gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
.ge { font-style: italic } // Generic.Emph
.gr { color: #a00 } // Generic.Error
.gh { color: #999 } // Generic.Heading
.gi { color: #000; background-color: #dfd } // Generic.Inserted
.gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
.go { color: #888 } // Generic.Output
.gp { color: #555 } // Generic.Prompt
.gs { font-weight: bold } // Generic.Strong
.gu { color: #aaa } // Generic.Subheading
.gt { color: #a00 } // Generic.Traceback
.kc { color: orange} // Keyword.Constant
.kd { color: orange} // Keyword.Declaration
.kp { color: $xc-green} // Keyword.Pseudo
.kr { color: $xc-green} // Keyword.Reserved
.kt { color: $xc-blue} // Keyword.Type
.m { color: orange } // Literal.Number
.s { color: $xc-red } // Literal.String
.na { color: orange } // Name.Attribute
.nb { color: $xc-blue } // Name.Builtin
.nc { color: $xc-turquoise } // Name.Class
.no { color: orange } // Name.Constant
.ni { color: orange } // Name.Entity
.ne { color: orange } // Name.Exception
.nf { } // Name.Function
.nn { color: orange } // Name.Namespace
.nt { color: orange } // Name.Tag
.nv { } // Name.Variable
.ow { } // Operator.Word
.w { color: #bbb } // Text.Whitespace
.mf {} // Literal.Number.Float
.mh { color: $xc-black } // Literal.Number.Hex
.mi { color: $xc-black } // Literal.Number.Integer
.mo { color: $xc-black } // Literal.Number.Oct
.il { color: $xc-black } // Literal.Number.Integer.Long
.sb { color: #d14 } // Literal.String.Backtick
.sc { color: #d14 } // Literal.String.Char
.sd { color: #d14 } // Literal.String.Doc
.s2 { color: #d14 } // Literal.String.Double
.se { color: #d14 } // Literal.String.Escape
.sh { color: #d14 } // Literal.String.Heredoc
.si { color: #d14 } // Literal.String.Interpol
.sx { color: #d14 } // Literal.String.Other
.sr { color: orange } // Literal.String.Regex
.s1 { color: $xc-red } // Literal.String.Single
.ss { color: $xc-red } // Literal.String.Symbol
.bp { color: $xc-turquoise } // Name.Builtin.Pseudo
.vc { color: $xc-turquoise } // Name.Variable.Class
.vg { color: $xc-black } // Name.Variable.Global
.vi { color: orange } // Name.Variable.Instance
.nl { color: $xc-turquoise }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/assets/logo-square.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
docs/assets/logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
docs/assets/node-view-layer.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

33
docs/build.sh Executable file
View File

@ -0,0 +1,33 @@
#!/bin/sh
set -e
HEADERS=`ls ../AsyncDisplayKit/*.h ../AsyncDisplayKit/Details/ASRangeController.h ../AsyncDisplayKit/Layout/*.h`
rm -rf htdocs appledoc
jekyll build --destination htdocs
appledoc \
--no-create-docset \
--create-html \
--exit-threshold 2 \
--no-repeat-first-par \
--no-merge-categories \
--explicit-crossref \
--warn-missing-output-path \
--warn-missing-company-id \
--warn-undocumented-object \
--warn-undocumented-member \
--warn-empty-description \
--warn-unknown-directive \
--warn-invalid-crossref \
--warn-missing-arg \
--project-name AsyncDisplayKit \
--project-company Facebook \
--company-id "com.facebook" \
--output appledoc \
$HEADERS
mv appledoc/html htdocs/appledoc
rmdir appledoc

49
docs/css/main.scss Executable file
View File

@ -0,0 +1,49 @@
---
# Only the main Sass file needs front matter (the dashes are enough)
---
@charset "utf-8";
// Our variables
$base-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
$base-font-size: 16px;
$small-font-size: $base-font-size * 0.875;
$base-line-height: 1.5;
$spacing-unit: 30px;
$text-color: #111;
$background-color: #f8f8f8;
$brand-color: #21b6ff;
$grey-color: #828282;
$grey-color-light: lighten($grey-color, 40%);
$grey-color-dark: darken($grey-color, 25%);
$on-palm: 600px;
$on-laptop: 800px;
// Using media queries with like this:
// @include media-query($palm) {
// .wrapper {
// padding-right: $spacing-unit / 2;
// padding-left: $spacing-unit / 2;
// }
// }
@mixin media-query($device) {
@media screen and (max-width: $device) {
@content;
}
}
// Import partials from `sass_dir` (defaults to `_sass`)
@import
"base",
"layout",
"syntax-highlighting"
;

View File

@ -0,0 +1,150 @@
---
layout: docs
title: Getting started
permalink: /guide/
next: guide/2/
---
## Concepts
AsyncDisplayKit's basic unit is the *node*. ASDisplayNode is an abstraction
over UIView, which in turn is an abstraction over CALayer. Unlike views, which
can only be used on the main thread, nodes are thread-safe: you can
instantiate and configure entire hierarchies of them in parallel on background
threads.
To keep its user interface smooth and responsive, your app should render at 60
frames per second &mdash; the gold standard on iOS. This means the main thread
has one-sixtieth of a second to push each frame. That's 16 milliseconds to
execute all layout and drawing code! And because of system overhead, your code
usually has less than ten milliseconds to run before it causes a frame drop.
AsyncDisplayKit lets you move image decoding, text sizing and rendering, and
other expensive UI operations off the main thread. It has other tricks up its
sleeve too... but we'll get to that later. :]
## Nodes as drop-in view replacements
If you're used to working with views, you already know how to use nodes. The
node API is similar to UIView's, with some additional conveniences &mdash; for
example, you can access common CALayer properties directly. To add a node to
an existing view or layer hierarchy, use its `node.view` or `node.layer`.
AsyncDisplayKit's core components include:
* *ASDisplayNode*. Counterpart to UIView &mdash; subclass to make custom nodes.
* *ASControlNode*. Analogous to UIControl &mdash; subclass to make buttons.
* *ASImageNode*. Like UIImageView &mdash; decodes images asynchronously.
* *ASTextNode*. Like UITextView &mdash; built on TextKit with full-featured
rich text support.
* *ASTableView* and *ASCollectionView*. UITableView and UICollectionView
subclasses that support nodes.
You can use these as drop-in replacements for their UIKit counterparts. While
ASDK works most effectively with fully node-based hierarchies, even replacing
individual views with nodes can improve performance.
Let's look at an example.
We'll start out by using nodes synchronously on the main thread &mdash; the
same way you already use views. This code is a familiar sight in custom view
controller `-loadView` implementations:
```objective-c
_imageView = [[UIImageView alloc] init];
_imageView.image = [UIImage imageNamed:@"hello"];
_imageView.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f);
[self.view addSubview:_imageView];
```
We can replace it with the following node-based code:
```objective-c
_imageNode = [[ASImageNode alloc] init];
_imageNode.backgroundColor = [UIColor lightGrayColor];
_imageNode.image = [UIImage imageNamed:@"hello"];
_imageNode.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f);
[self.view addSubview:_imageNode.view];
```
This doesn't take advantage of ASDK's asynchronous sizing and layout
functionality, but it's already an improvement. The first block of code
synchronously decodes `hello.png` on the main thread; the second starts
decoding the image on a background thread, possibly on a different CPU core.
(Note that we're setting a placeholder background colour on the node, "holding
its place" onscreen until the real content appears. This works well with
images but less so with text &mdash; people expect text to appear instantly,
with images loading in after a slight delay. We'll discuss techniques to
improve this later on.)
## Button nodes
ASImageNode and ASTextNode both inherit from ASControlNode, so you can use them
as buttons. Let's say we're making a music player and we want to add a
(non-skeuomorphic, iOS 7-style) shuffle button:
[![shuffle]({{ site.baseurl }}/assets/guide/1-shuffle-crop.png)]({{ site.baseurl }}/assets/guide/1-shuffle.png)
Our view controller will look something like this:
```objective-c
- (void)viewDidLoad
{
[super viewDidLoad];
// attribute a string
NSDictionary *attrs = @{
NSFontAttributeName: [UIFont systemFontOfSize:12.0f],
NSForegroundColorAttributeName: [UIColor redColor],
};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"shuffle"
attributes:attrs];
// create the node
_shuffleNode = [[ASTextNode alloc] init];
_shuffleNode.attributedString = string;
// configure the button
_shuffleNode.userInteractionEnabled = YES; // opt into touch handling
[_shuffleNode addTarget:self
action:@selector(buttonTapped:)
forControlEvents:ASControlNodeEventTouchUpInside];
// size all the things
CGRect b = self.view.bounds; // convenience
CGSize size = [_shuffleNode measure:CGSizeMake(b.size.width, FLT_MAX)];
CGPoint origin = CGPointMake(roundf( (b.size.width - size.width) / 2.0f ),
roundf( (b.size.height - size.height) / 2.0f ));
_shuffleNode.frame = (CGRect){ origin, size };
// add to our view
[self.view addSubview:_shuffleNode.view];
}
- (void)buttonTapped:(id)sender
{
NSLog(@"tapped!");
}
```
This works as you would expect. Unfortunately, this button is only 14&frac12;
points tall &mdash; nowhere near the standard 44&times;44 minimum tap target
size &mdash; and it's very difficult to tap. We could solve this by
subclassing the text node and overriding `-hitTest:withEvent:`. We could even
force the text view to have a minimum height during layout. But wouldn't it be
nice if there were a more elegant way?
```objective-c
// size all the things
/* ... */
// make the tap target taller
CGFloat extendY = roundf( (44.0f - size.height) / 2.0f );
_shuffleNode.hitTestSlop = UIEdgeInsetsMake(-extendY, 0.0f, -extendY, 0.0f);
```
Et voilà! *Hit-test slops* work on all nodes, and are a nice example of what
this new abstraction enables.
Next up, making your own nodes!

View File

@ -0,0 +1,211 @@
---
layout: docs
title: Custom nodes
permalink: /guide/2/
prev: guide/
next: guide/3/
---
## View hierarchies
Sizing and layout of custom view hierarchies are typically done all at once on
the main thread. For example, a custom UIView that minimally encloses a text
view and an image view might look like this:
```objective-c
- (CGSize)sizeThatFits:(CGSize)size
{
// size the image
CGSize imageSize = [_imageView sizeThatFits:size];
// size the text view
CGSize maxTextSize = CGSizeMake(size.width - imageSize.width, size.height);
CGSize textSize = [_textView sizeThatFits:maxTextSize];
// make sure everything fits
CGFloat minHeight = MAX(imageSize.height, textSize.height);
return CGSizeMake(size.width, minHeight);
}
- (void)layoutSubviews
{
CGSize size = self.bounds.size; // convenience
// size and layout the image
CGSize imageSize = [_imageView sizeThatFits:size];
_imageView.frame = CGRectMake(size.width - imageSize.width, 0.0f,
imageSize.width, imageSize.height);
// size and layout the text view
CGSize maxTextSize = CGSizeMake(size.width - imageSize.width, size.height);
CGSize textSize = [_textView sizeThatFits:maxTextSize];
_textView.frame = (CGRect){ CGPointZero, textSize };
}
```
This isn't ideal. We're sizing our subviews twice &mdash; once to figure out
how big our view needs to be and once when laying it out &mdash; and while our
layout arithmetic is cheap and quick, we're also blocking the main thread on
expensive text sizing.
We could improve the situation by manually cacheing our subviews' sizes, but
that solution comes with its own set of problems. Just adding `_imageSize` and
`_textSize` ivars wouldn't be enough: for example, if the text were to change,
we'd need to recompute its size. The boilerplate would quickly become
untenable.
Further, even with a cache, we'll still be blocking the main thread on sizing
*sometimes*. We could try to shift sizing to a background thread with
`dispatch_async()`, but even if our own code is thread-safe, UIView methods are
documented to [only work on the main
thread](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/index.html):
> Manipulations to your applications user interface must occur on the main
> thread. Thus, you should always call the methods of the UIView class from
> code running in the main thread of your application. The only time this may
> not be strictly necessary is when creating the view object itself but all
> other manipulations should occur on the main thread.
This is a pretty deep rabbit hole. We could attempt to work around the fact
that UILabels and UITextViews cannot safely be sized on background threads by
manually creating a TextKit stack and sizing the text ourselves... but that's a
laborious duplication of work. Further, if UITextView's layout behaviour
changes in an iOS update, our sizing code will break. (And did we mention that
TextKit isn't thread-safe either?)
## Node hierarchies
Enter AsyncDisplayKit. Our custom node looks like this:
```objective-c
#import <AsyncDisplayKit/AsyncDisplayKit+Subclasses.h>
...
// perform expensive sizing operations on a background thread
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
// size the image
CGSize imageSize = [_imageNode measure:constrainedSize];
// size the text node
CGSize maxTextSize = CGSizeMake(constrainedSize.width - imageSize.width,
constrainedSize.height);
CGSize textSize = [_textNode measure:maxTextSize];
// make sure everything fits
CGFloat minHeight = MAX(imageSize.height, textSize.height);
return CGSizeMake(constrainedSize.width, minHeight);
}
// do as little work as possible in main-thread layout
- (void)layout
{
// layout the image using its cached size
CGSize imageSize = _imageNode.calculatedSize;
_imageNode.frame = CGRectMake(self.bounds.size.width - imageSize.width, 0.0f,
imageSize.width, imageSize.height);
// layout the text view using its cached size
CGSize textSize = _textNode.calculatedSize;
_textNode.frame = (CGRect){ CGPointZero, textSize };
}
```
ASImageNode and ASTextNode, like the rest of AsyncDisplayKit, are thread-safe,
so we can size them on background threads. The `-measure:` method is like
`-sizeThatFits:`, but with side effects: it caches both the argument
(`constrainedSizeForCalculatedSize`) and the result (`calculatedSize`) for
quick access later on &mdash; like in our now-snappy `-layout` implementation.
As you can see, node hierarchies are sized and laid out in much the same way as
their view counterparts. Custom nodes do need to be written with a few things
in mind:
* Nodes must recursively measure all of their subnodes in their
`-calculateSizeThatFits:` implementations. Note that the `-measure:`
machinery will only call `-calculateSizeThatFits:` if a new measurement pass
is needed (e.g., if the constrained size has changed).
* Nodes should perform any other expensive pre-layout calculations in
`-calculateSizeThatFits:`, cacheing useful intermediate results in ivars as
appropriate.
* Nodes should call `[self invalidateCalculatedSize]` when necessary. For
example, ASTextNode invalidates its calculated size when its
`attributedString` property is changed.
For more examples of custom sizing and layout, along with a demo of
ASTextNode's features, check out `BlurbNode` and `KittenNode` in the
[Kittens](https://github.com/facebook/AsyncDisplayKit/tree/master/examples/Kittens)
sample project.
## Custom drawing
To guarantee thread safety in its highly-concurrent drawing system, the node
drawing API diverges substantially from UIView's. Instead of implementing
`-drawRect:`, you must:
1. Define an internal "draw parameters" class for your custom node. This
class should be able to store any state your node needs to draw itself
&mdash; it can be a plain old NSObject or even a dictionary.
2. Return a configured instance of your draw parameters class in
`-drawParametersForAsyncLayer:`. This method will always be called on the
main thread.
3. Implement either `+drawRect:withParameters:isCancelled:isRasterizing:` or
`+displayWithParameters:isCancelled:`. Note that these are *class* methods
that will not have access to your node's state &mdash; only the draw
parameters object. They can be called on any thread and must be
thread-safe.
For example, this node will draw a rainbow:
```objective-c
@interface RainbowNode : ASDisplayNode
@end
@implementation RainbowNode
+ (void)drawRect:(CGRect)bounds
withParameters:(id<NSObject>)parameters
isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock
isRasterizing:(BOOL)isRasterizing
{
// clear the backing store, but only if we're not rasterising into another layer
if (!isRasterizing) {
[[UIColor whiteColor] set];
UIRectFill(bounds);
}
// UIColor sadly lacks +indigoColor and +violetColor methods
NSArray *colors = @[ [UIColor redColor],
[UIColor orangeColor],
[UIColor yellowColor],
[UIColor greenColor],
[UIColor blueColor],
[UIColor purpleColor] ];
CGFloat stripeHeight = roundf(bounds.size.height / (float)colors.count);
// draw the stripes
for (UIColor *color in colors) {
CGRect stripe = CGRectZero;
CGRectDivide(bounds, &stripe, &bounds, stripeHeight, CGRectMinYEdge);
[color set];
UIRectFill(stripe);
}
}
@end
```
This could easily be extended to support vertical rainbows too, by adding a
`vertical` property to the node, exporting it in
`-drawParametersForAsyncLayer:`, and referencing it in
`+drawRect:withParameters:isCancelled:isRasterizing:`. More-complex nodes can
be supported in much the same way.
For more on custom nodes, check out the [subclassing
header](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/ASDisplayNode%2BSubclasses.h)
or read on!

View File

@ -0,0 +1,102 @@
---
layout: docs
title: Asynchronous display
permalink: /guide/3/
prev: guide/2/
next: guide/4/
---
## Realistic placeholders
Nodes need to complete both a *measurement pass* and a *display pass* before
they're fully rendered. It's possible to force either step to happen
synchronously: call `-measure:` in `-layoutSubviews` to perform sizing on the
main thread, or set a node's `displaysAsynchronously` flag to NO to disable
ASDK's async display machinery. (AsyncDisplayKit can still improve your app's
performance even when rendering fully synchronously &mdash; more on that
later!)
The recommended way to use ASDK is to only add nodes to your view hierarchy
once they've been sized. This avoids unsightly layout changes as the
measurement pass completes, but if you enable asynchronous display, it will
always be possible for a node to appear onscreen before its content has fully
rendered. We'll discuss techniques to minimise this shortly, but you should
take it into account and include *realistic placeholders* in your app designs.
Once its measurement pass has completed, a node can accurately place all of its
subnodes onscreen &mdash; they'll just be blank. The easiest way to make a
realistic placeholder is to set static background colours on your subnodes.
This effect looks better than generic placeholder images because it varies
based on the content being loaded, and it works particularly well for opaque
images. You can also create visually-appealing placeholder nodes, like the
shimmery lines representing text in Paper as its stories are loaded, and swap
them out with your content nodes once they've finished displaying.
## Working range
So far, we've only discussed asynchronous sizing: toss a "create a node
hierarchy and measure it" block onto a background thread, then trampoline to
the main thread to add it to the view hierarchy when that's done. Ideally, as
much content as possible should be fully-rendered and ready to go as soon as
the user scrolls to it. This requires triggering display passes in advance.
If your app's content is in a scroll view or can be paged through, like
Instagram's main feed or Paper's story strip, the solution is a *working
range*. A working range controller tracks the *visible range*, the subset of
content that's currently visible onscreen, and enqueues asynchronous rendering
for the next few screenfuls of content. As the user scrolls, a screenful or
two of previous content is preserved; the rest is cleared to conserve memory.
If she starts scrolling in the other direction, the working range trashes its
render queue and starts pre-rendering in the new direction of scroll &mdash;
and because of the buffer of previous content, this entire process will
typically be invisible.
AsyncDisplayKit includes a generic working range controller,
`ASRangeController`. Its working range size can be tuned depending on your
app: if your nodes are simple, even an iPhone 4 can maintain a substantial
working range, but heavyweight nodes like Facebook stories are expensive and
need to be pruned quickly.
```objective-c
ASRangeController *rangeController = [[ASRangeController alloc] init];
rangeController.tuningParameters = (ASRangeTuningParameters){
.leadingBufferScreenfuls = 2.0f; // two screenfuls in the direction of scroll
.trailingBufferScreenfuls = 0.5f; // one-half screenful in the other direction
};
```
If you use a working range, you should profile your app and consider tuning it
differently on a per-device basis. iPhone 4 has 512MB of RAM and a single-core
A4 chipset, while iPhone 6 has 1GB of RAM and the orders-of-magnitude-faster
multicore A8 &mdash; and if your app supports iOS 7, it will be used on both.
## ASTableView
ASRangeController manages working ranges, but doesn't actually display content.
If your content is currently rendered in a UITableView, you can convert it to
use `ASTableView` and custom nodes &mdash; just subclass `ASCellNode` instead
of ASDisplayNode. ASTableView is a UITableView subclass that integrates
node-based cells and a working range.
ASTableView doesn't let cells onscreen until their underlying nodes have been
sized, and as such can fully benefit from realistic placeholders. Its API is
very similar to UITableView (see the
[Kittens](https://github.com/facebook/AsyncDisplayKit/tree/master/examples/Kittens)
sample project for an example), with some key changes:
* Rather than setting the table view's `.delegate` and `.dataSource`, you set
its `.asyncDelegate` and `.asyncDataSource`. See
[ASTableView.h](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/ASTableView.h)
for how its delegate and data source protocols differ from UITableView's.
* Instead of implementing `-tableView:cellForRowAtIndexPath:`, your data
source must implement `-tableView:nodeForRowAtIndexPath:`. This method must
be thread-safe and should not implement reuse. Unlike the UITableView
version, it won't be called when the row is about to display.
* `-tableView:heightForRowAtIndexPath:` has been removed &mdash; ASTableView
lets your cell nodes size themselves. This means you no longer have to
manually duplicate or factor out layout and sizing logic for
dynamically-sized UITableViewCells!
Next up, how to get the most out of ASDK in your app.

View File

@ -0,0 +1,139 @@
---
layout: docs
title: Making the most of AsyncDisplayKit
permalink: /guide/4/
prev: guide/3/
next: guide/5/
---
## A note on optimisation
AsyncDisplayKit is powerful and flexible, but it is not a panacea. If your app
has a complex image- or text-heavy user interface, ASDK can definitely help
improve its performance &mdash; but if you're blocking the main thread on
network requests, you should consider rearchitecting a few things first. :]
So why is it worthwhile to change the way we do view layout and rendering,
given that UIKit has always been locked to the main thread and performant iOS
apps have been shipping since iPhone's launch?
### Modern animations
Until iOS 7, static animations (à la `+[UIView
animateWithDuration:animations:]`) were the standard. The post-skeuomorphism
redesign brought with it highly-interactive, physics-based animations, with
springs joining the ranks of constant animation functions like
`UIViewAnimationOptionCurveEaseInOut`.
Classic animations aren't actually executed in your app. They're executed
out-of-process, in the high-priority Core Animation render server. Thanks to
pre-emptive multitasking, an app can block its main thread continuously without
causing the animation to drop a single frame.
Critically, dynamic animations can't be offloaded the same way, and both
[pop](https://github.com/facebook/pop) and UIKit Dynamics execute physics
simulations on your app's main thread. This is because executing arbitrary
code in the render server would introduce unacceptable latency, even if it
could be done securely.
Physics-based animations are often interactive, letting you start an animation
and interrupt it before it completes. Paper lets you fling objects across the
screen and catch them before they land, or grab a view that's being pulled by a
spring and tear it off. This requires processing touch events and updating
animation targets in realtime &mdash; even short delays for inter-process
communication would shatter the illusion.
(Fun fact: Inertial scrolling is also a physics animation! UIScrollView has
always implemented its animations on the main thread, which is why stuttery
scrolling is the hallmark of a slow app.)
### The main-thread bottleneck
Physics animations aren't the only thing that need to happen on the main
thread. The main thread's [run
loop](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/multithreading/runloopmanagement/runloopmanagement.html)
is responsible for handling touch events and initiating drawing operations
&mdash; and with UIKit in the mix, it also has to render text, decode images,
and do any other custom drawing (e.g., using Core Graphics).
If an iteration of the main thread's run loop takes too long, it will drop an
animation frame and may fail to handle touch events in time. Each iteration of
the run loop must complete within 16ms in order to drive 60fps animations, and
your own code typically has less than 10ms to execute. This means that the
best way to keep your app smooth and responsive is to do as little work on the
main thread as possible.
What's more, the main thread only executes on one core! Single-threaded view
hierarchies can't take advantage of the multicore CPUs in all modern iOS
devices. This is important for more than just performance reasons &mdash; it's
also critical for battery life. Running the CPU on all cores for a short time
is preferable to running one core for an extended amount of time: if the
processor can *race to sleep* by finishing its work and idling faster, it can
spend more time in a low-power mode, improving battery life.
## When to go asynchronous
AsyncDisplayKit really shines when used fully asynchronously, shifting both
measurement and rendering passes off the main thread and onto multiple cores.
This requires a completely node-based hierarchy. Just as degrading from
UIViews to CALayers disables view-specific functionality like touch handling
from that point on, degrading from nodes to views disables async behaviour.
You don't, however, need to convert your app's entire view hierarchy to nodes.
In fact, you shouldn't! Asynchronously bringing up your app's core UI, like
navigation elements or tab bars, would be a very confusing experience. Those
elements of your apps can still be nodes, but should be fully synchronous to
guarantee a fully-usable interface as quickly as possible. (This is why
UIWindow has no node equivalent.)
There are two key situations where asynchronous hierarchies excel:
1. *Parallelisation*. Measuring and rendering UITableViewCells (or your app's
equivalent, e.g., story cards in Paper) are embarrassingly parallel
problems. Table cells typically have a fixed width and variable height
determined by their contents &mdash; the argument to `-measure:` for one
cell doesn't depend on any other cells' calculations, so we can enqueue an
arbitrary number of cell measurement passes at once.
2. *Preloading*. An app with five tabs should synchronously load the first
one so content is ready to go as quickly as possible. Once this is
complete and the CPU is idle, why not asynchronously prepare the other tabs
so the user doesn't have to wait? This is inconvenient with views, but
very easy with nodes.
Paper's asynchronous rendering starts at the story strip. You should profile
your app and watch how people use it in the wild to decide what combination of
synchrony and asynchrony yields the best user experience.
## Additional optimisations
Complex hierarchies &mdash; even when rendered asynchronously &mdash; can
impose a cost because of the sheer number of views involved. Working around
this can be painful, but AsyncDisplayKit makes it easy!
* *Layer-backing*. In some cases, you can substantially improve your app's
performance by using layers instead of views. Manually converting
view-based code to layers is laborious due to the difference in APIs.
Worse, if at some point you need to enable touch handling or other
view-specific functionality, you have to manually convert everything back
(and risk regressions!).
With nodes, converting an entire subtree from views to layers is as simple
as...
rootNode.layerBacked = YES;
...and if you need to go back, it's as simple as deleting one line. We
recommend enabling layer-backing as a matter of course in any custom node
that doesn't need touch handling.
* *Precompositing*. Flattening an entire view hierarchy into a single layer
also improves performance, but comes with a hit to maintainability and
hierarchy-based reasoning. Nodes can do this for you too!
rootNode.shouldRasterizeDescendants = YES;
...will cause the entire node hierarchy from that point on to be rendered
into one layer.
Next up: AsyncDisplayKit, under the hood.

View File

@ -0,0 +1,89 @@
---
layout: docs
title: Under the hood
permalink: /guide/5/
prev: guide/4/
---
## Node architecture
*(Skip to the next section if you're not interested in AsyncDisplayKit implementation details.)*
We've described nodes as an abstraction over views and layers, and shown how to
interact with the underlying UIViews and CALayers when necessary. Nodes don't
wrap or vend their UIKit counterparts, though &mdash; an ASImageNode's `.view`
is not a UIImageView! So how do nodes work?
**NOTE:** Classes whose names begin with `_` are private. Don't use them
directly!
Creating a node doesn't create its underlying view-layer pair. This is why you
can create nodes cheaply and on background threads. When you use a UIView or
CALayer property on a node, you're actually interacting with a proxy object,
[`_ASPendingState`](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/Private/_ASPendingState.h),
that's preconfigured to match UIView and CALayer defaults.
The first access to a node's `.view` or `.layer` property causes both to be
initialised and configured with the node's current state. If it has subnodes,
they are recursively loaded as well. Once a node has been loaded, the proxy
object is destroyed and the node becomes main-thread-affined &mdash; its
properties will update the underlying view directly. (Layer-backed nodes do
the same, not loading views.)
Nodes are powered by
[`_ASDisplayLayer`](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/Details/_ASDisplayLayer.h)
and
[`_ASDisplayView`](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/Details/_ASDisplayView.h).
These are lightweight to create and add to their respective hierarchies, and
provide integration points that allow nodes to act as full-fledged views or
layers. It's possible to create nodes that are backed by custom view or layer
classes, but doing so is strongly discouraged as it disables the majority of
ASDK's functionality.
When Core Animation asks an `_ASDisplayLayer` to draw itself, the request is
forwarded to its node. Unless asynchronous display has been disabled, the
actual draw call won't happen immediately or on the main thread. Instead, a
display block will be added to a background queue. These blocks are executed
in parallel, but you can enable `ASDISPLAYNODE_DELAY_DISPLAY` in
[`ASDisplayNode(AsyncDisplay)`](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/Private/ASDisplayNode%2BAsyncDisplay.mm)
to serialise the render system for debugging.
Common UIView subclass hooks are forwarded from `_ASDisplayView` to its
underlying node, including touch handling, hit-testing, and gesture recogniser
delegate calls. Because an `_ASDisplayView`'s layer is an `_ASDisplayLayer`,
view-backed nodes also participate in asynchronous display.
## In practice
What does this mean for your custom nodes?
You can implement methods like `-touchesBegan:withEvent:` /
`touchesMoved:withEvent:` / `touchesEnded:withEvent:` /
`touchesCancelled:withEvent:` in your nodes exactly as you would in a UIView
subclass. If you find you need a subclass hook that hasn't already been
provided, please file an issue on GitHub &mdash; or add it yourself and submit a
pull request!
If you need to interact or configure your node's underlying view or layer,
don't do so in `-init`. Instead, override `-didLoad` and check if you're
layer-backed:
```objective-c
- (void)didLoad
{
[super didLoad];
// add a gesture recogniser, if we have a view to add it to
if (!self.layerBacked) {
_gestureRecogniser = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(_tap:)];
[self.view addGestureRecognizer:_gestureRecogniser];
}
}
```
## *fin.*
Thanks for reading! If you have any questions, please file a GitHub issue or
post in the [Facebook group](https://www.facebook.com/groups/551597518288687).
We'd love to hear from you.

86
docs/index.md Normal file
View File

@ -0,0 +1,86 @@
---
layout: page
title: Smooth asynchronous user interfaces for iOS apps
---
![logo]({{ site.baseurl }}/assets/logo.png)
AsyncDisplayKit is an iOS framework that keeps even the most complex user
interfaces smooth and responsive. It was originally built to make Facebook's
[Paper](https://facebook.com/paper) possible, and goes hand-in-hand with
[pop](https://github.com/facebook/pop)'s physics-based animations &mdash; but
it's just as powerful with UIKit Dynamics and conventional app designs.
<br />
### Quick start
ASDK is available on [CocoaPods](http://cocoapods.org). Add the following to your Podfile:
```ruby
pod 'AsyncDisplayKit'
```
(ASDK can also be used as a regular static library: Copy the project to your
codebase manually, adding `AsyncDisplayKit.xcodeproj` to your workspace. Add
`libAsyncDisplayKit.a`, AssetsLibrary, and Photos to the "Link Binary With
Libraries" build phase. Include `-lc++ -ObjC` in your project linker flags.)
Import the framework header, or create an [Objective-C bridging
header](https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html)
if you're using Swift:
```objective-c
#import <AsyncDisplayKit/AsyncDisplayKit.h>
```
AsyncDisplayKit Nodes are a thread-safe abstraction layer over UIViews and
CALayers:
![logo]({{ site.baseurl }}/assets/node-view-layer.png)
You can construct entire node hierarchies in parallel, or instantiate and size
a single node on a background thread &mdash; for example, you could do
something like this in a UIViewController:
```objective-c
dispatch_async(_backgroundQueue, ^{
ASTextNode *node = [[ASTextNode alloc] init];
node.attributedString = [[NSAttributedString alloc] initWithString:@"hello!"
attributes:nil];
[node measure:CGSizeMake(screenWidth, FLT_MAX)];
node.frame = (CGRect){ CGPointZero, node.calculatedSize };
// self.view isn't a node, so we can only use it on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self.view addSubview:node.view];
});
});
```
AsyncDisplayKit at a glance:
* `ASImageNode` and `ASTextNode` are drop-in replacements for UIImageView and
UITextView.
* `ASMultiplexImageNode` can load and display progressively higher-quality
variants of an image over a slow cell network, letting you quickly show a
low-resolution photo while the full size downloads.
* `ASNetworkImageNode` is a simpler, single-image counterpart to the Multiplex
node.
* `ASTableView` and `ASCollectionView` are a node-aware UITableView and
UICollectionView, respectively, that can asynchronously preload cell nodes
&mdash; from loading network data to rendering &mdash; all without blocking
the main thread.
You can also easily [create your own
nodes](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/ASDisplayNode%2BSubclasses.h)
to implement node hierarchies or custom drawing.
<br />
### Learn more
* Read the [Getting Started guide]({{ site.baseurl }}/guide)
* Get the [sample projects](https://github.com/facebook/AsyncDisplayKit/tree/master/examples)
* Browse the [API reference]({{ site.baseurl }}/appledoc)
* Watch the [NSLondon talk](http://vimeo.com/103589245) or the [NSSpain talk](https://www.youtube.com/watch?v=RY_X7l1g79Q)