added docs for control node, cell node, text cell node, and tweaked styling of code blocks

This commit is contained in:
Luke Parham
2016-05-16 01:05:34 -05:00
parent ebf23b2ab3
commit d552103375
9 changed files with 282 additions and 15 deletions

View File

@@ -5,9 +5,138 @@ permalink: /docs/cell-node.html
next: text-cell-node.html
---
ASCellNode is maybe the most commonly <a href = "subclassing.html">subclassed node</a>. It can be used as the cell for both ASTableNodes and ASCollectionNodes.
ASCellNode, as you may have guessed, is the cell class of ASDK. Unlike the various cells in UIKit, ASCellNode can be used with ASTableNodes, ASCollectionNodes and ASPagerNodes, making it incredibly flexible.
That being said, subclassing it is largely the same as subclassing a regular ASDisplayNode. Most importantly, you'll write an -init method, a -layoutSpecThatFits: method for measurement and layout, and, if necessary, a -didLoad method for adding extra gesture recognizers and a -layout method for any extra layout needs.
### 3 Ways to Party
If you don't feel like subclassing you're also free to use the `-initWithView:` or `-initWithViewController:` methods to return nodes with backing views created from an existing view or view controller you already have.
There are three ways in which you can implement the cells you'll use in your ASDK app: subclassing ASCellNode, initializing with an existing ASViewController or using an existing UIView or CALayer.
#### Subclassing
Subclassing an ASCellNode is pretty much the same as <a href = "/docs/subclassing.html">subclassing</a> a regular ASDisplayNode.
Most likely, you'll write a few of the following:
<ul>
<li>
<code>-init</code> -- Thread safe initialization
</li>
<li>
<code>-layoutSpecThatFits:</code> -- Return a layout spec that defines the layout of your cell.
</li>
<li>
<code>-didLoad</code> -- Called on the main thread. Good place to add gesture recognizers, etc.
</li>
<li>
<code>-layout</code> -- Also called on the main thread. Layout is complete after the call to super which means you can do any extra tweaking you need to do.
</li>
</ul>
#### Initializing with an ASViewController
Say you already have some type of view controller written to display a view in your app. If you want to take that view controller and drop its view in as a cell in one of the scrolling nodes or a pager node its no problem.
For example, say you already have a view controller written that manages an ASTableNode. To use that table as a page in an ASPagerNode you can use `-initWithViewControllerBlock`.
<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">
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
{
NSArray *animals = self.allAnimals[index];
ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^UIViewController * _Nonnull{
return [[AnimalTableNodeController alloc] initWithAnimals:animals];
} didLoadBlock:nil];
node.preferredFrameSize = pagerNode.bounds.size;
return node;
}
</pre>
<pre lang="swift" class = "swiftCode hidden">
func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
let animals = allAnimals[index]
let node = ASCellNode(viewControllerBlock: { () -> UIViewController in
return AnimalTableNodeController(animals: animals)
}, didLoadBlock: nil)
node.preferredFrameSize = pagerNode.bounds.size
return node
}
</pre>
</div>
</div>
And this works for any combo of scrolling container node and UIViewController subclass. You want to embed random view controllers in your collection node? Go for it.
<div class = "note">
Notice that you need to set the preferredFrameSize of a node created this way. Normally your nodes will implement -layoutSpecThatFits: but since these don't you'll need give the cell a size.
</div>
#### Initializing with a UIView or CALayer
Alternatively, if you already have a UIView or CALayer subclass that you'd like to drop in as cell you can do that instead.
<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">
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
{
NSArray *animal = self.animals[index];
ASCellNode *node = [[ASCellNode alloc] initWithViewBlock:^UIView * _Nonnull{
return [[SomeAnimalView alloc] initWithAnimal:animal];
}];
node.preferredFrameSize = pagerNode.bounds.size;
return node;
}
</pre>
<pre lang="swift" class = "swiftCode hidden">
func pagerNode(pagerNode: ASPagerNode!, nodeAtIndex index: Int) -> ASCellNode! {
let animal = animals[index]
let node = ASCellNode { () -> UIView in
return SomeAnimalView(animal: animal)
}
node.preferredFrameSize = pagerNode.bounds.size
return node
}
</pre>
</div>
</div>
As you can see, its roughly the same idea. That being said, if you're doing this, you may consider converting the existing UIView subclass to be an ASCellNode subclass in order to gain the advantage of asynchronous display.
### Never Show Placeholders
Usually, if a cell hasn't finished its display pass before it has reached the screen it will show placeholders until it has completed drawing its content.
If placeholders are unacceptable, you can set an ASCellNode's `neverShowPlaceholders` property to `YES`.
<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">
node.neverShowPlaceholders = YES;
</pre>
<pre lang="swift" class = "swiftCode hidden">
node.neverShowPlaceholders = true
</pre>
</div>
</div>
With this property set to `YES`, the main thread will be blocked until display has completed for the cell. This is more similar to UIKit, and in fact makes AsyncDisplayKit scrolling visually indistinguishable from UIKit's, except being faster.
<div class = "note">
Using this option does not eliminate all of the performance advantages of AsyncDisplayKit. Normally, a cell has been preloading and is almost done when it reaches the screen, so the blocking time is very short. Even if the rangeTuningParameters are set to 0 this option outperforms UIKit. While the main thread is waiting, subnode display executes concurrently.
</div>

View File

@@ -183,7 +183,7 @@ For example, you may want to set a table's separator style property. This can be
_tableNode.view.allowsSelection = NO;
_tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableNode.view.leadingScreensForBatching = 3.0; // overriding default of 2.0
_tableNode.view.leadingScreensForBatching = 3.0; // default is 2.0
}
</pre>
@@ -193,7 +193,7 @@ override func viewDidLoad() {
tableNode.view.allowsSelection = false
tableNode.view.separatorStyle = .None
tableNode.view.leadingScreensForBatching = 3.0 // overriding default of 2.0
tableNode.view.leadingScreensForBatching = 3.0 // default is 2.0
}
</pre>
</div>
@@ -219,7 +219,6 @@ This is different from UIKit where normally you would have to call reload row /
### Sample Apps with ASTableNodes
<ul>
<li><a href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples/ASDKgram">ASDKgram</a></li>
<li><a href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples/ASDKgram">ASDKgram</a></li>
<li><a href="https://github.com/facebook/AsyncDisplayKit/tree/master/examples/Kittens">Kittens</a></li>
</ul>

View File

@@ -5,7 +5,86 @@ permalink: /docs/control-node.html
next: button-node.html
---
<div>😑 This page is coming soon...</div>
ASControlNode is the ASDK equivalent to UIControl. You don't create instantce of ASControlNode directly. Instead, you can use it as a subclassing point when creating controls of your own. In fact, <a href = "#">ASTextNode</a> and <a href = "#">ASImageNode</a> are subclasses of ASControlNode which means they can easily be used in the same way you would normally use a UIButton.
####Tappable Area Visualization####
With just one line of code, you can easily visualize the tappable areas of all ASControlNodes in your app using the <a href = "debug-tool-hit-test-slop.html">hit test slop debug tool</a>.
### Control State
Like UIControl, ASControlNode has a state which defines its appearance and ability to support user interactions. Its state can be one of any state defined by `ASControlState`.
<div class = "highlight-group">
<span class="language-toggle"><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
<div class = "code">
<pre lang="objc" class="objcCode">
typedef NS_OPTIONS(NSUInteger, ASControlState) {
ASControlStateNormal = 0,
ASControlStateHighlighted = 1 << 0, // used when isHighlighted is set
ASControlStateDisabled = 1 << 1,
ASControlStateSelected = 1 << 2, // used when isSelected is set
...
};
</pre>
</div>
</div>
### Target-Action Mechanism
Also similarly to UIControl, ASControlNode's have a set of events defined which you can react to by assigning a target-action pair.
The available actions are:
<div class = "highlight-group">
<span class="language-toggle"><a data-lang="objective-c" class = "active objcButton">Objective-C</a></span>
<div class = "code">
<pre lang="objc" class="objcCode">
typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
{
/** A touch-down event in the control node. */
ASControlNodeEventTouchDown = 1 << 0,
/** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */
ASControlNodeEventTouchDownRepeat = 1 << 1,
/** An event where a finger is dragged inside the bounds of the control node. */
ASControlNodeEventTouchDragInside = 1 << 2,
/** An event where a finger is dragged just outside the bounds of the control. */
ASControlNodeEventTouchDragOutside = 1 << 3,
/** A touch-up event in the control node where the finger is inside the bounds of the node. */
ASControlNodeEventTouchUpInside = 1 << 4,
/** A touch-up event in the control node where the finger is outside the bounds of the node. */
ASControlNodeEventTouchUpOutside = 1 << 5,
/** A system event canceling the current touches for the control node. */
ASControlNodeEventTouchCancel = 1 << 6,
/** All events, including system events. */
ASControlNodeEventAllEvents = 0xFFFFFFFF
};
</pre>
</div>
</div>
Assigning a target and action for these events is done with the same methods as a UIControl, namely using `addTarget:action:forControlEvents:` or `actionsForTarget:forControlEvent:`.
### Hit Test Slop
While all node's have a `hitTestSlop` property, this is usually most useful when dealing with controls. Instead of needing to make your control bigger, or needing to override -hitTest:withEvent: you can just assign a UIEdgeInsets to your control and its boundaries will be expanded accordingly.
<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">
CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2;
CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2;
_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
</pre>
<!-- <pre lang="swift" class = "swiftCode hidden">
let horizontalDiff: CGFloat = (bounds.size.width - playButton.bounds.size.width)/2.0
let verticalDiff: CGfloat = (bounds.size.height - playButton.bounds.size.height)/2.0
playButton.hitTestSlop = UIEdgeInsets(top: -verticalDiff, left: -horizontalDiff, bottom: -verticalDiff, right: -horizontalDiff)
</pre> -->
</div>
</div>
Remember that, since the property is an inset, you'll need to use negative values in order to expand the size of your tappable region.
### Hit Test Visualization
The <a href = "/docs/debug-tool-hit-test-visualization.html">hit test visualization tool</a> is an option to enable highlighting of the tappable areas of your nodes. To enable it, include `[ASControlNode setEnableHitTestDebug:YES]` in your app delegate in `-application:didFinishLaunchingWithOptions:`.

View File

@@ -41,7 +41,7 @@ Properties of both views and layers are forwarded to nodes and can be easily acc
<div class = "code">
<pre lang="objc" class="objcCode">
ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.clipsToBounds = YES; // not .masksToBounds
node.clipsToBounds = YES; // not .masksToBounds
node.borderColor = [UIColor blueColor]; //layer name when there is no UIView equivalent
NSLog(@"Backing layer: %@", node.layer);
@@ -49,7 +49,7 @@ NSLog(@"Backing layer: %@", node.layer);
<pre lang="swift" class = "swiftCode hidden">
let node = ASDisplayNode()
node.clipsToBounds = true // not .masksToBounds
node.clipsToBounds = true // not .masksToBounds
node.borderColor = UIColor.blueColor() //layer name when there is no UIView equivalent
print("Backing layer: \(node.layer)")
@@ -71,7 +71,7 @@ In some cases, it is desirable to initialize a node and provide a view to be use
<div class = "code">
<pre lang="objc" class="objcCode">
ASDisplayNode *node = [ASDisplayNode alloc] initWithViewBlock:^{
SomeView *view = [[SomeView alloc] init];
SomeView *view = [[SomeView alloc] init];
return view;
}];
</pre>

View File

@@ -53,7 +53,7 @@ Say you wanted to add 8 pts of padding to the stack you've already set up:
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.direction = ASStackLayoutDirectionVertical;
verticalStack.direction = ASStackLayoutDirectionVertical;
verticalStack.spacing = 4.0;
[verticalStack setChildren:_commentNodes];

View File

@@ -5,4 +5,22 @@ permalink: /docs/synchronous-concurrency.html
next: subtree-rasterization.html
---
<div class = "warning">😑 This page is coming soon...</div>
Both `ASViewController` and `ASCellNode` have a property called `neverShowPlaceholders`.
By setting this property to YES, the main thread will be blocked until display has completed for the cell or view controller's view.
Using this option does not eliminate all of the performance advantages of AsyncDisplayKit. Normally, a given node has been preloading and is almost done when it reaches the screen, so the blocking time is very short. Even if the rangeTuningParameters are set to 0 this option outperforms UIKit. While the main thread is waiting, all subnode display executes concurrently, thus synchronous concurrency.
<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">
node.neverShowPlaceholders = YES;
</pre>
<pre lang="swift" class = "swiftCode hidden">
node.neverShowPlaceholders = true
</pre>
</div>
</div>
Usually, if a cell hasn't finished its display pass before it has reached the screen it will show placeholders until it has drawing its content. Setting this option to YES makes your scrolling node or ASViewController act more like UIKit, and in fact makes AsyncDisplayKit scrolling visually indistinguishable from UIKit's, except that it's faster.

View File

@@ -5,4 +5,40 @@ permalink: /docs/text-cell-node.html
next: control-node.html
---
<div class = "warning">😑 This page is coming soon...</div>
ASTextCellNode is a simple ASCellNode subclass you can use when all you need is a cell with styled text.
<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">
ASTextCellNode *textCell = [[ASTextCellNode alloc]
initWithAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"SomeFont" size:16.0]} insets:UIEdgeInsetsMake(8, 16, 8, 16)];
</pre>
<pre lang="swift" class = "swiftCode hidden">
let textCellNode = ASTextCellNode(attributes: [NSFontAttributeName: UIFont(name: "SomeFont", size: 16.0)],
insets: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16))
</pre>
</div>
</div>
The text can be configured on initialization or after the fact.
<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">
ASTextCellNode *textCell = [[ASTextCellNode alloc] init];
textCellNode.text = @"Some dang ol' text";
textCellNode.attributes = @{NSFontAttributeName: [UIFont fontWithName:@"SomeFont" size:16.0]};
textCellNode.insets = UIEdgeInsetsMake(8, 16, 8, 16);
</pre>
<pre lang="swift" class = "swiftCode hidden">
let textCellNode = ASTextCellNode()
textCellNode.text = "Some dang ol' text"
textCellNode.attributes = [NSFontAttributeName: UIFont(name: "SomeFont", size: 16.0)]
textCellNode.insets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
</pre>
</div>
</div>

View File

@@ -16,6 +16,7 @@
<script type="text/javascript" src="//use.typekit.net/vqa1hcx.js"></script>
<script type="text/javascript">{'try{Typekit.load();}catch(e){}'}</script>
<script src="/static/toggle.js"></script>
<link href='https://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
</head>
<body>

View File

@@ -774,3 +774,8 @@ body {
padding-bottom: 10px;
overflow: auto;
}
pre {
font-family: 'Inconsolata' !important;
font-weight: 500;
color: #333333;
}