mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-09-04 20:00:53 +00:00
Revert "Remove old website folder from master branch" (#1818)
This commit is contained in:
parent
f71e207e0a
commit
ae87717263
2
docs/CNAME
Normal file
2
docs/CNAME
Normal file
@ -0,0 +1,2 @@
|
||||
asyncdisplaykit.org
|
||||
|
420
docs/LICENSE.md
Normal file
420
docs/LICENSE.md
Normal 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
9
docs/README.md
Normal 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
13
docs/_config.yml
Normal 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
|
18
docs/_includes/footer.html
Normal file
18
docs/_includes/footer.html
Normal 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 & Instagram collaboration ♥</p>
|
||||
</div>
|
||||
|
||||
<div class="footer-col footer-col-right">
|
||||
<p class="text">
|
||||
© 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
24
docs/_includes/head.html
Normal 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 }} — {% 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 }} — {% 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>
|
25
docs/_includes/header.html
Normal file
25
docs/_includes/header.html
Normal 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>
|
20
docs/_layouts/default.html
Normal file
20
docs/_layouts/default.html
Normal 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
27
docs/_layouts/docs.html
Normal 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 }}">← prev</a>
|
||||
{% endif %}
|
||||
{% if page.next %}
|
||||
<a class="docs-next" href="{{ site.baseurl }}/{{ page.next }}">next →</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
16
docs/_layouts/page.html
Normal file
16
docs/_layouts/page.html
Normal 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
15
docs/_layouts/post.html
Normal 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
205
docs/_sass/_base.scss
Normal 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
265
docs/_sass/_layout.scss
Normal 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;
|
||||
}
|
76
docs/_sass/_syntax-highlighting.scss
Normal file
76
docs/_sass/_syntax-highlighting.scss
Normal 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 }
|
||||
}
|
BIN
docs/assets/guide/1-shuffle-crop.png
Normal file
BIN
docs/assets/guide/1-shuffle-crop.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1016 B |
BIN
docs/assets/guide/1-shuffle.png
Normal file
BIN
docs/assets/guide/1-shuffle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
docs/assets/logo-square.png
Executable file
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
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
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
33
docs/build.sh
Executable 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
49
docs/css/main.scss
Executable 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"
|
||||
;
|
150
docs/guide/1-introduction.md
Normal file
150
docs/guide/1-introduction.md
Normal 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 — 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 — 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 — subclass to make custom nodes.
|
||||
* *ASControlNode*. Analogous to UIControl — subclass to make buttons.
|
||||
* *ASImageNode*. Like UIImageView — decodes images asynchronously.
|
||||
* *ASTextNode*. Like UITextView — 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 — 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 — 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:
|
||||
|
||||
[]({{ 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½
|
||||
points tall — nowhere near the standard 44×44 minimum tap target
|
||||
size — 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!
|
211
docs/guide/2-custom-nodes.md
Normal file
211
docs/guide/2-custom-nodes.md
Normal 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 — once to figure out
|
||||
how big our view needs to be and once when laying it out — 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 application’s 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 — 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
|
||||
— 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 — 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!
|
102
docs/guide/3-asynchronous-display.md
Normal file
102
docs/guide/3-asynchronous-display.md
Normal 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 — 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 — 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 —
|
||||
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 — 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 — 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 — 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.
|
139
docs/guide/4-making-the-most-of-asdk.md
Normal file
139
docs/guide/4-making-the-most-of-asdk.md
Normal 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 — 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 — 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
|
||||
— 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 — 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 — 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 — even when rendered asynchronously — 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.
|
89
docs/guide/5-under-the-hood.md
Normal file
89
docs/guide/5-under-the-hood.md
Normal 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 — 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 — 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 — 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
86
docs/index.md
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
layout: page
|
||||
title: Smooth asynchronous user interfaces for iOS apps
|
||||
---
|
||||
|
||||

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

|
||||
|
||||
You can construct entire node hierarchies in parallel, or instantiate and size
|
||||
a single node on a background thread — 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
|
||||
— from loading network data to rendering — 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)
|
Loading…
x
Reference in New Issue
Block a user