mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-11 00:40:25 +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