Update docs

This commit is contained in:
Garrett Moon
2017-04-07 11:40:20 -07:00
parent fe66bc1a8c
commit fa18ad226b
14 changed files with 315 additions and 76 deletions

View File

@@ -14,14 +14,16 @@ When enabled, ASM means that your nodes no longer require `addSubnode:` or `remo
<br>
Consider the following intialization method from the PhotoCellNode class in <a href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples/ASDKgram">ASDKgram sample app</a>. This <code>ASCellNode</code> subclass produces a simple social media photo feed cell.
In the "Original Code" we see the familiar `addSubnode:` calls in bold. In the "Code with ASM" (switch at top right of code block) these have been removed and replaced with a single line that enables ASM.
In the "Original Code" we see the familiar `addSubnode:` calls in bold. In the "Code with ASM" these have been removed and replaced with a single line that enables ASM.
By setting `.automaticallyManagesSubnodes` to `YES` on the `ASCellNode`, we _no longer_ need to call `addSubnode:` for each of the `ASCellNode`'s subnodes. These `subNodes` will be present in the node hierarchy as long as this class' `layoutSpecThatFits:` method includes them.
<div class = "highlight-group">
<i>Original code</i>
<div class="highlight-group">
<span class="language-toggle">
<a data-lang="swift" class="swiftButton">Code with ASM</a>
<a data-lang="objective-c" class = "active objcButton">Original Code</a>
<a data-lang="objective-c" class="active objcButton">Objective-C</a>
<a data-lang="swift" class="swiftButton">Swift</a>
</span>
<div class = "code">
<pre lang="objc" class="objcCode">
@@ -57,8 +59,50 @@ By setting `.automaticallyManagesSubnodes` to `YES` on the `ASCellNode`, we _no
return self;
}
</pre>
<pre lang="swift" class="swiftCode hidden">
class PhotoCellNode {
private let photoModel: PhotoModel
private let userAvatarImageNode = ASNetworkImageNode()
private let photoImageNode = ASNetworkImageNode()
private let userNameTextNode = ASTextNode()
private let photoLocationTextNode = ASTextNode()
<pre lang="swift" class = "swiftCode hidden">
init(photo: PhotoModel) {
photoModel = photo
super.init()
userAvatarImageNode.URL = photo.ownerUserProfile.userPicURL
<b>addSubnode(userAvatarImageNode)</b>
photoImageNode.URL = photo.URL
<b>addSubnode(photoImageNode)</b>
userNameTextNode.attributedText = poto.ownerUserProfile.usernameAttributedString(fontSize: fontSize)
<b>addSubnode(userNameTextNode)</b>
photo.location.reverseGeocodeLocation { [weak self] location in
if locationModel == self?.photoModel.location {
self?.photoLocationTextNode.attributedText = photo.locationAttributedString(fontSize: fontSize)
self?.setNeedsLayout()
}
}
<b>addSubnode(photoLocationTextNode)</b>
}
}
</pre>
</div>
</div>
<i>Code with ASM</i>
<div class="highlight-group">
<span class="language-toggle">
<a data-lang="objective-c" class="active objcButton">Objective-C</a>
<a data-lang="swift" class="swiftButton">Swift</a>
</span>
<div class = "code">
<pre lang="objc" class="objcCode">
- (instancetype)initWithPhotoObject:(PhotoModel *)photo;
{
self = [super init];
@@ -89,6 +133,37 @@ By setting `.automaticallyManagesSubnodes` to `YES` on the `ASCellNode`, we _no
return self;
}
</pre>
<pre lang="swift" class="swiftCode hidden">
class PhotoCellNode {
private let photoModel: PhotoModel
private let userAvatarImageNode = ASNetworkImageNode()
private let photoImageNode = ASNetworkImageNode()
private let userNameTextNode = ASTextNode()
private let photoLocationTextNode = ASTextNode()
init(photo: PhotoModel) {
photoModel = photo
super.init()
<b>automaticallyManagesSubnodes = true</b>
userAvatarImageNode.URL = photo.ownerUserProfile.userPicURL
photoImageNode.URL = photo.URL
userNameTextNode.attributedText = poto.ownerUserProfile.usernameAttributedString(fontSize: fontSize)
photo.location.reverseGeocodeLocation { [weak self] location in
if locationModel == self?.photoModel.location {
self?.photoLocationTextNode.attributedText = photo.locationAttributedString(fontSize: fontSize)
self?.setNeedsLayout()
}
}
}
}
</pre>
</div>
</div>
@@ -105,7 +180,10 @@ An <code>ASLayoutSpec</code> completely describes the UI of a view in your app b
Consider the abreviated `layoutSpecThatFits:` method for the `ASCellNode` subclass above.
<div class = "highlight-group">
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
<span class="language-toggle">
<a data-lang="objective-c" class="active objcButton">Objective-C</a>
<a data-lang="swift" class="swiftButton">Swift</a>
</span>
<div class = "code">
<pre lang="objc" class="objcCode">
@@ -153,7 +231,47 @@ Consider the abreviated `layoutSpecThatFits:` method for the `ASCellNode` subcla
</pre>
<pre lang="swift" class = "swiftCode hidden">
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let headerSubStack: ASStackLayoutSpec = .vertical()
headerSubStack.style.flexShrink = 1
<b>if photoLocationLabel.attributedText != nil {</b>
headerSubStack.children = [userNameLabel, photoLocationLabel]
<b>} else {</b>
headerSubStack.children = [userNameLabel]
<b>}</b>
userAvatarImageNode.style.preferredSize = CGSize(width: userImageHeight, height: userImageHeight) // constrain avatar image frame size
let spacer = ASLayoutSpec()
spacer.style.flexGrow = 1
let avatarInsets = UIEdgeInsets(top: horizontalBuffer, left: 0, bottom: horizontalBuffer, right: horizontalBuffer)
let avatarInset = ASInsetLayoutSpec(insets: avatarInsets, child: <b>userAvatarImageNode</b>)
let headerStack: ASStackLayoutSpec = .horizontal()
headerStack.alignItems = .center // center items vertically in horizontal stack
headerStack.justifyContent = .start // justify content to the left side of the header stack
headerStack.children = [avatarInset, headerSubStack, spacer]
// header inset stack
let insets = UIEdgeInsets(top: 0, left: horizontalBuffer, bottom: 0, right: horizontalBuffer)
let headerWithInset = ASInsetLayoutSpec(insets: insets, child: headerStack)
// footer inset stack
let footerInsets = UIEdgeInsets(top: verticalBuffer, left: horizontalBuffer, bottom: verticalBuffer, right: horizontalBuffer)
let footerWithInset = ASInsetLayoutSpec(insets: footerInsets, child: <b>photoCommentsNode</b>)
// vertical stack
let cellWidth = constrainedSize.max.width
photoImageNode.style.preferredSize = CGSize(width: cellWidth, height: cellWidth) // constrain photo frame size
let verticalStack: ASStackLayoutSpec = .vertical()
verticalStack.alignItems = .stretch // stretch headerStack to fill horizontal space
verticalStack.children = [headerWithInset, <b>photoImageNode</b>, footerWithInset]
return verticalStack
}
</pre>
</div>
</div>

View File

@@ -22,7 +22,7 @@ If you've used `-setTitle:forControlState:` then you already know how to set up
[buttonNode setTitle:@"Button Title Normal" withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal];
</pre>
<pre lang="swift" class = "swiftCode hidden">
button.setTitle("Button Title Normal", withFont: nil, withColor: UIColor.blueColor(), forState: .Normal)
buttonNode.setTitle("Button Title Normal", with: nil, with: .blue, for: .normal)
</pre>
</div>
</div>
@@ -37,7 +37,7 @@ If you need even more control, you can also opt to use the attributed string ver
[self.buttonNode setAttributedTitle:attributedTitle forState:ASControlStateNormal];
</pre>
<pre lang="swift" class = "swiftCode hidden">
buttonNode.setAttributedTitle(attributedTitle, for: [])
buttonNode.setAttributedTitle(attributedTitle, for: .normal)
</pre>
</div>
</div>
@@ -54,7 +54,7 @@ Again, analagous to UIKit, you can add sets of target-action pairs to respond to
[buttonNode addTarget:self action:@selector(buttonPressed:) forControlEvents:ASControlNodeEventTouchUpInside];
</pre>
<pre lang="swift" class = "swiftCode hidden">
button.addTarget(self, action: #selector(buttonPressed(_:)), forControlEvents: .TouchUpInside)
buttonNode.addTarget(self, action: #selector(buttonPressed), forControlEvents: .touchUpInside)
</pre>
</div>
</div>
@@ -72,8 +72,8 @@ self.buttonNode.contentVerticalAlignment = ASVerticalAlignmentTop;
self.buttonNode.contentHorizontalAlignment = ASHorizontalAlignmentMiddle;
</pre>
<pre lang="swift" class = "swiftCode hidden">
buttonNode.contentVerticalAlignment = .Top
buttonNode.contentHorizontalAlignment = .Middle
buttonNode.contentVerticalAlignment = .top
buttonNode.contentHorizontalAlignment = .middle
</pre>
</div>
</div>

View File

@@ -42,20 +42,20 @@ For example, say you already have a view controller written that manages an `AST
return [[AnimalTableNodeController alloc] initWithAnimals:animals];
} didLoadBlock:nil];
node.preferredFrameSize = pagerNode.bounds.size;
node.style.preferredSize = pagerNode.bounds.size;
return node;
}
</pre>
<pre lang="swift" class = "swiftCode hidden">
func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
func pagerNode(_ pagerNode: ASPagerNode, nodeAt index: Int) -> ASCellNode {
let animals = allAnimals[index]
let node = ASCellNode(viewControllerBlock: { () -> UIViewController in
return AnimalTableNodeController(animals: animals)
}, didLoadBlock: nil)
}, didLoad: nil)
node.preferredFrameSize = pagerNode.bounds.size
node.style.preferredSize = pagerNode.bounds.size
return node
}
@@ -86,20 +86,20 @@ Alternatively, if you already have a `UIView` or `CALayer` subclass that you'd l
return [[SomeAnimalView alloc] initWithAnimal:animal];
}];
node.preferredFrameSize = pagerNode.bounds.size;
node.style.preferredSize = pagerNode.bounds.size;
return node;
}
</pre>
<pre lang="swift" class = "swiftCode hidden">
func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
func pagerNode(_ pagerNode: ASPagerNode, nodeAt index: Int) -> ASCellNode {
let animal = animals[index]
let node = ASCellNode { () -> UIView in
return SomeAnimalView(animal: animal)
}
node.preferredFrameSize = pagerNode.bounds.size
node.style.preferredSize = pagerNode.bounds.size
return node
}

View File

@@ -64,12 +64,58 @@ It is recommended that you use the node block version of the method so that your
As noted in the previous section:
<ul>
<li>ASCollectionNodes do not utilize cell resuse.</li>
<li>ASCollectionNodes do not utilize cell reuse.</li>
<li>Using the "nodeBlock" method is preferred.</li>
<li>It is very important that the returned node blocks are thread-safe.</li>
<li>ASCellNodes can be used by ASTableNode, ASCollectionNode and ASPagerNode.</li>
</ul>
### Node Block Thread Safety Warning
It is very important that node blocks be thread-safe. One aspect of that is ensuring that the data model is accessed _outside_ of the node block. Therefore, it is unlikely that you should need to use the index inside of the block.
Consider the following `-collectionNode:nodeBlockForItemAtIndexPath:` method.
<div class = "highlight-group">
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
<div class = "code">
<pre lang="objc" class="objcCode">
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
{
PhotoModel *photoModel = [_photoFeed objectAtIndex:indexPath.row];
// this may be executed on a background thread - it is important to make sure it is thread safe
ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhoto:photoModel];
cellNode.delegate = self;
return cellNode;
};
return cellNodeBlock;
}
</pre>
<pre lang="swift" class = "swiftCode hidden">
func tableNode(_ collectionNode: ASCollectionNode, nodeBlockForItemAt indexPath: IndexPath) -> ASCellNodeBlock {
guard photoFeed.count > indexPath.row else { return { ASCellNode() } }
let photoModel = photoFeed[indexPath.row]
// this may be executed on a background thread - it is important to make sure it is thread safe
let cellNodeBlock = { () -> ASCellNode in
let cellNode = PhotoCellNode(photo: photoModel)
cellNode.delegate = self
return cellNode
}
return cellNodeBlock
}
</pre>
</div>
</div>
In the example above, you can see how the index is used to access the photo model before creating the node block.
### Replacing a UICollectionViewController with an ASViewController
AsyncDisplayKit does not offer an equivalent to UICollectionViewController. Instead, you can use the flexibility of ASViewController to recreate any type of UI<em>...</em>ViewController.

View File

@@ -60,7 +60,25 @@ All of this logic can be removed from where it previously existed in the "view"
</pre>
<pre lang="swift" class = "swiftCode hidden">
// Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
final class PhotoCellNodeController: ASNodeController<PhotoCellNode> {
override func loadNode() {
self.node = PhotoCellNode(photoObject: photoModel)
}
override func didEnterPreloadState() {
super.didEnterPreloadState()
let commentFeedModel = photoModel.commentFeed
commentFeedModel.refreshFeedWithCompletionBlock { [weak self] newComments in
// load comments for photo
if commentFeedModel.numberOfItemsInFeed > 0 {
self?.node.photoCommentsNode.updateWithCommentFeedModel(commentFeedModel)
self?.node.setNeedsLayout()
}
}
}
}
</pre>
</div>
</div>
@@ -102,7 +120,22 @@ Next, we add a mutable array to the `PhotoFeedNodeController` to store our node
</pre>
<pre lang="swift" class = "swiftCode hidden">
// Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
final class PhotoFeedNodeController: PhotoFeedBaseController {
let photoFeed: PhotoFeedModel
let tableNode: ASTableNode = ASTableNode()
<b>var photoCellNodeControllers: [PhotoCellNodeController] = []</b>
init() {
super.init(node: tableNode)
navigationItem.title = "ASDK"
navigationController.isNavigationBarHidden = true
tableNode.dataSource = self
tableNode.delegate = self
}
}
</pre>
</div>
</div>
@@ -140,7 +173,25 @@ To use this node controller, we modify our table row insertion logic to create a
</pre>
<pre lang="swift" class = "swiftCode hidden">
// Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
func insertNewRowsInTableNode(newPhotos: [PhotoFeedModel]) {
let section = 0
var indexPaths: [NSIndexPath] = []
let newTotalNumberOfPhotos = photoFeed.numberOfItemsInFeed
let firstRow = newTotalNumberOfPhotos - newPhotos.count
(firstRow..<newTotalNumberOfPhotos).forEach { row in
<b>// create photoCellNodeControllers for the new photos
let cellController = PhotoCellNodeController()
cellController.photoModel = photoFeed[row]
photoCellNodeControllers.append(cellController)</b>
// include this index path in the insert rows call for the table
let path = IndexPath(row: row, section: section)
indexPaths.append(path)
}
tableNode.insertRows(at: indexPaths, with: .none)
}
</pre>
</div>
</div>
@@ -169,7 +220,16 @@ Don't forget to modify the table data source method to return the node controlle
</pre>
<pre lang="swift" class = "swiftCode hidden">
// Click the "Edit on GitHub" button at the bottom of this page to contribute the swift code for this section. Thanks!
func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
<b>let cellController = photoCellNodeControllers[indexPath.row]</b>
// this will be executed on a background thread - important to make sure it's thread safe
let cellNodeBlock = { () -> ASCellNode in
let cellNode = cellController.node
return cellNode
}
return cellNodeBlock
}
</pre>
</div>
</div>

View File

@@ -22,7 +22,7 @@ nextPage: containers-ascollectionnode.html
</pre>
<pre lang="swift" class = "swiftCode hidden">
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
</pre>
</div>
</div>
@@ -41,7 +41,7 @@ with your choice of **_one_** of the following methods
</pre>
<pre lang="swift" class = "swiftCode hidden">
override func tableNode(tableNode: ASTableNode, nodeForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNode
func tableNode(_ tableNode: ASTableNode, nodeForRowAt indexPath: IndexPath) -> ASCellNode
</pre>
</div>
</div>
@@ -60,14 +60,14 @@ or
</pre>
<pre lang="swift" class = "swiftCode hidden">
override func tableNode(tableNode: ASTableNode, nodeBlockForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock
func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock
</pre>
</div>
</div>
<br>
<div class = "note">
It is recommended that you use the node block version of these methods so that your collection node will be able to prepare and display all of its cells concurrently. This means that all subnode initialization methods can be run in the background. Make sure to keep 'em thread safe.
It is recommended that you use the node block version of these methods so that your table node will be able to prepare and display all of its cells concurrently. This means that all subnode initialization methods can be run in the background. Make sure to keep 'em thread safe.
</div>
These two methods, need to return either an <a href = "cell-node.html">`ASCellNode`</a> or an `ASCellNodeBlock`. An `ASCellNodeBlock` is a block that creates a `ASCellNode` which can be run on a background thread. Note that `ASCellNodes` are used by `ASTableNode`, `ASCollectionNode` and `ASPagerNode`.
@@ -101,10 +101,10 @@ An `ASTableNode` is assigned to be managed by an `ASViewController` in its `-ini
</pre>
<pre lang="swift" class = "swiftCode hidden">
func initWithModel(models: Array&lt;Model&gt;) {
let tableNode = ASTableNode(style:.Plain)
init(models: [Model]) {
let tableNode = ASTableNode(style: .plain)
super.initWithNode(tableNode)
super.init(node: tableNode)
self.models = models
self.tableNode = tableNode
@@ -122,8 +122,6 @@ It is very important that node blocks be thread-safe. One aspect of that is ensu
Consider the following `-tableNode:nodeBlockForRowAtIndexPath:` method from the `PhotoFeedNodeController.m` file in the <a href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples/ASDKgram">ASDKgram sample app</a>.
In the example below, you can see how the index is used to access the photo model before creating the node block.
<div class = "highlight-group">
<span class="language-toggle"><a data-lang="swift" class="swiftButton">Swift</a><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
<div class = "code">
@@ -144,24 +142,25 @@ In the example below, you can see how the index is used to access the photo mode
</pre>
<pre lang="swift" class = "swiftCode hidden">
func tableNode(tableNode: UITableNode!, nodeBlockForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock! {
guard photoFeed.count > indexPath.row else { return nil }
let photoModel = photoFeed[indexPath.row]
// this may be executed on a background thread - it is important to make sure it is thread safe
let cellNodeBlock = { () -> ASCellNode in
let cellNode = PhotoCellNode(photo: photoModel)
cellNode.delegate = self;
return ASCellNode()
}
return cellNodeBlock
func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
guard photoFeed.count > indexPath.row else { return { ASCellNode() } }
let photoModel = photoFeed[indexPath.row]
// this may be executed on a background thread - it is important to make sure it is thread safe
let cellNodeBlock = { () -> ASCellNode in
let cellNode = PhotoCellNode(photo: photoModel)
cellNode.delegate = self
return cellNode
}
return cellNodeBlock
}
</pre>
</div>
</div>
In the example above, you can see how the index is used to access the photo model before creating the node block.
### Accessing the ASTableView
@@ -194,7 +193,7 @@ override func viewDidLoad() {
super.viewDidLoad()
tableNode.view.allowsSelection = false
tableNode.view.separatorStyle = .None
tableNode.view.separatorStyle = .none
tableNode.view.leadingScreensForBatching = 3.0 // default is 2.0
}
</pre>

View File

@@ -36,7 +36,7 @@ The <a href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples/Ve
In your `AppDelegate.m` file,
<ul>
<li>import <code>AsyncDisplayKit+Debug.h</code></li>
<li>add <code>[ASRangeController setShouldShowRangeDebugOverlay:YES]</code> at the top of your AppDelegate's <code>didFinishLaunchingWithOptions:</code> method</li>
<li>add <code>[ASDisplayNode setShouldShowRangeDebugOverlay:YES]</code> at the top of your AppDelegate's <code>didFinishLaunchingWithOptions:</code> method</li>
</ul>
**Make sure to call this method before initializing any component that uses an ASRangeControllers (ASTableView, ASCollectionView).**

View File

@@ -24,7 +24,7 @@ NSLog(@"Underlying view: %@", node.view);
<pre lang="swift" class = "swiftCode hidden">
let node = ASDisplayNode()
node.backgroundColor = UIColor.orangeColor()
node.backgroundColor = .orange
node.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
print("Underlying view: \(node.view)")
@@ -51,7 +51,7 @@ NSLog(@"Backing layer: %@", node.layer);
<pre lang="swift" class = "swiftCode hidden">
let node = ASDisplayNode()
node.clipsToBounds = true // not .masksToBounds
node.borderColor = UIColor.blueColor() //layer name when there is no UIView equivalent
node.borderColor = UIColor.blue.cgColor // layer name when there is no UIView equivalent
print("Backing layer: \(node.layer)")
</pre>
@@ -78,10 +78,10 @@ ASDisplayNode *node = [ASDisplayNode alloc] initWithViewBlock:^{
</pre>
<pre lang="swift" class = "swiftCode hidden">
let node = ASDisplayNode(viewBlock: { () -> UIView! in
let view = SomeView();
return view
})
let node = ASDisplayNode { () -> UIView in
let view = SomeView()
return view
}
</pre>
</div>
</div>

View File

@@ -190,7 +190,7 @@ It is imperative to call `completeTransition:` on the context object once your a
Note that there hasn't been a use of `addSubnode:` or `removeFromSupernode` during the transition. AsyncDisplayKit's layout transition API analyzes the differences in the node hierarchy between the old and new layout, implicitly performing node insertions and removals via <a href="automatic-subnode-mgmt.html">Automatic Subnode Management</a>.
Nodes are inserted before your implementation of `animateLayoutTransition:` is called and this is a good place to manually manage the hierarchy before you begin the animation. Removals are preformed in `didCompleteLayoutTransition:` after you call `completeTransition:` on the context object. If you need to manually perform deletions, override `didCompleteLayoutTransition:` and perform your custom operations. Note that this will override the default behavior and it is recommended to either call `super` or walk through the `removedSubnodes` getter in the context object to perform the cleanup.
Nodes are inserted before your implementation of `animateLayoutTransition:` is called and this is a good place to manually manage the hierarchy before you begin the animation. Removals are performed in `didCompleteLayoutTransition:` after you call `completeTransition:` on the context object. If you need to manually perform deletions, override `didCompleteLayoutTransition:` and perform your custom operations. Note that this will override the default behavior and it is recommended to either call `super` or walk through the `removedSubnodes` getter in the context object to perform the cleanup.
Passing `NO` to `transitionLayoutWithAnimation:` will still run through your `animateLayoutTransition:` and `didCompleteLayoutTransition:` implementations with the `[context isAnimated]` property set to `NO`. It is your choice on how to handle this case — if at all. An easy way to provide a default implementation this is to call super:

View File

@@ -405,10 +405,22 @@ Use `-[ASDisplayNode layoutThatFits:]` instead to get an `ASLayout` and call `si
CGSize size = [displayNode measure:CGSizeMake(100, 100)];
// 2.0:
ASLayout *layout = [displayNode layoutThatFits:ASSizeMake(CGSizeZero, CGSizeMake(100, 100))];
// Creates an ASSizeRange with min and max sizes.
ASLayout *layout = [displayNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
// Or an exact size
// ASLayout *layout = [displayNode layoutThatFits:ASSizeRangeMake(CGSizeMake(100, 100))];
CGSize size = layout.size;
</pre>
<pre lang="swift" class = "swiftCode hidden">
// 1.x
let size = displayNode.measure(CGSize(width: 100, height: 100))
// 2.0:
// Creates an ASSizeRange with min and max sizes.
let layout = displayNode.layoutThatFits(ASSizeRange(min: CGSizeZero, max: CGSize(width: 100, height: 100)))
// Or an exact size
// let layout = displayNode.layoutThatFits(ASSizeRangeMake(CGSize(width: 100, height: 100)))
let size = layout.size
</pre>
</div>
</div>

View File

@@ -85,7 +85,8 @@ they will be resolved again, causing justifyContent and alignItems to be updated
Thus, it is preferred to those properties.
- `justifyContent`. The amount of space between each child.
- `alignItems`. Orientation of children along cross axis.
- `baselineRelativeArrangement`. If `YES` the vertical spacing between two views is measured from the last baseline of the top view to the top of the bottom view.
- `flexWrap`. Whether children are stacked into a single or multiple lines. Defaults to single line.
- `alignContent`. Orientation of lines along cross axis if there are multiple lines.
<div class = "highlight-group">
<span class="language-toggle">
@@ -130,7 +131,7 @@ override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
</div>
</div>
Flexbox works the same way in AsyncDisplayKit as it does in CSS on the web, with a few exceptions. The defaults are different, there is no `flex` parameter and `flexGrow` and `flexShrink` only supports a boolean value.
Flexbox works the same way in AsyncDisplayKit as it does in CSS on the web, with a few exceptions. For example, the defaults are different and there is no `flex` parameter. See <a href = "layout2-web-flexbox-differences.html">Web Flexbox Differences</a> for more information.
<br>
@@ -242,7 +243,7 @@ override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
return ASBackgroundLayoutSpec(child: backgroundNode, background: backgroundNode)
return ASBackgroundLayoutSpec(child: foregroundNode, background: backgroundNode)
}
</pre>
</div>

View File

@@ -30,7 +30,7 @@ AsyncDisplayKit's layout system is centered around two basic concepts:
### Layout Specs
A layout spec, short for "layout specification", has no physical presence. Instead, layout specs act as containers for other layout elements by understanding how these children layout elments relate to each other.
A layout spec, short for "layout specification", has no physical presence. Instead, layout specs act as containers for other layout elements by understanding how these children layout elements relate to each other.
AsyncDisplayKit provides several <a hfref = "layout2-layoutspec-types.html">subclasses</a> of `ASLayoutSpec`, from a simple layout specification that insets a single child, to a more complex layout specification that arranges multiple children in varying stack configurations.
@@ -62,7 +62,7 @@ Some elements have an "intrinsic size" based on their immediately available cont
- `ASTextNode`
- `ASButtonNode`
All other nodes either do not have an intrinsic size or lack an intrinsic size until their external resource is loaded. For example, an `ASNetworkImageNode` does not know its size until the image has been downloaded from the URL. These sorts of elments inlcude
All other nodes either do not have an intrinsic size or lack an intrinsic size until their external resource is loaded. For example, an `ASNetworkImageNode` does not know its size until the image has been downloaded from the URL. These sorts of elements include
- `ASVideoNode`
- `ASVideoPlayerNode`
@@ -73,7 +73,7 @@ These nodes that lack an initial intrinsic size must have an initial size set fo
### Layout Debugging
Calling `-asciiArtString` on any `ASDisplayNode` or `ASLayoutSpec` returns an ascii-art representation of the object and its children. Optionally, if you set the `.debugName` on any node or layout spec, that will also included in the ascii art. An example is seen below.
Calling `-asciiArtString` on any `ASDisplayNode` or `ASLayoutSpec` returns an ascii-art representation of the object and its children. Optionally, if you set the `.debugName` on any node or layout spec, that will also be included in the ascii art. An example is seen below.
<div class = "highlight-group">
<div class = "code">

View File

@@ -18,4 +18,4 @@ Layoutables don't have a padding or margin property. Instead wrapping a layoutab
### Missing features
Certain features like `flexWrap` on a `ASStackLayoutSpec` are not supported currently. See <a href = "layout2-properties.html">Layout Properties</a> for the full list of properties that are supported.
Certain features are not supported currently. See <a href = "layout2-layout-element-properties.html">Layout Properties</a> for the full list of properties that are supported.

View File

@@ -20,7 +20,7 @@ NSDictionary *attrs = @{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNe
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Hey, here's some text." attributes:attrs];
_node = [[ASTextNode alloc] init];
_node.attributedString = string;
_node.attributedText = string;
</pre>
<pre lang="swift" class = "swiftCode hidden">
@@ -28,7 +28,7 @@ let attrs = [NSFontAttributeName: UIFont(name: "HelveticaNeue", size: 12.0)]
let string = NSAttributedString(string: "Hey, here's some text.", attributes: attrs)
node = ASTextNode()
node.attributedString = string
node.attributedText = string
</pre>
</div>
</div>
@@ -46,15 +46,15 @@ In any case where you need your text node to fit into a space that is smaller th
<div class = "code">
<pre lang="objc" class="objcCode">
_textNode = [[ASTextNode alloc] init];
_textNode.attributedString = string;
_textNode.truncationAttributedString = [[NSAttributedString alloc]
_textNode.attributedText = string;
_textNode.truncationAttributedText = [[NSAttributedString alloc]
initWithString:@"¶¶¶"];
</pre>
<pre lang="swift" class = "swiftCode hidden">
textNode = ASTextNode()
textNode.attributedString = string
textNode.truncationAttributedString = NSAttributedString(string: "¶¶¶")
textNode.attributedText = string
textNode.truncationAttributedText = NSAttributedString(string: "¶¶¶")
</pre>
</div>
</div>
@@ -86,20 +86,22 @@ NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithS
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
}
range:[blurb rangeOfString:@"placekitten.com"]];
_textNode.attributedString = string;
_textNode.attributedText = string;
</pre>
<pre lang="swift" class = "swiftCode hidden">
textNode.linkAttributeNames = [kLinkAttributeName]
let blurb: NSString = "kittens courtesy placekitten.com 😸"
let attributedString = NSMutableAttributedString(string: blurb as String)
attributedString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue-Light", size: 16.0)!, range: NSRange(location: 0, length: blurb.length))
attributedString.addAttributes([kLinkAttributeName: NSURL(string: "http://placekitten.com/")!,
NSForegroundColorAttributeName: UIColor.grayColor(),
NSUnderlineStyleAttributeName: (NSUnderlineStyle.StyleSingle.rawValue | NSUnderlineStyle.PatternDashDot.rawValue)],
range: blurb.rangeOfString("placekitten.com"))
textNode.attributedString = attributedString
NSForegroundColorAttributeName: UIColor.gray,
NSUnderlineStyleAttributeName: (NSUnderlineStyle.styleSingle.rawValue | NSUnderlineStyle.patternDashDot.rawValue)],
range: blurb.range(of: "placekitten.com"))
textNode.attributedText = attributedString
</pre>
</div>
</div>
@@ -127,10 +129,11 @@ Conforming to `ASTextNodeDelegate` allows your class to react to various events
</pre>
<pre lang="swift" class = "swiftCode hidden">
func textNode(textNode: ASTextNode, tappedLinkAttribute attribute: String, value: AnyObject, atPoint point: CGPoint, textRange: NSRange) {
guard let url = value as? NSURL else { return }
func textNode(_ textNode: ASTextNode, tappedLinkAttribute attribute: String, value: Any, at point: CGPoint, textRange: NSRange) {
guard let url = value as? URL else { return }
UIApplication.sharedApplication().openURL(url)
// the link was tapped, open it
UIApplication.shared.openURL(url)
}
</pre>
</div>