mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
Update docs
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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<Model>) {
|
||||
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>
|
||||
|
||||
@@ -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).**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user