Beef up the properties, beef up the demo app

This commit is contained in:
Adlai Holler 2016-02-17 18:30:42 -08:00
parent 7a6006e627
commit 563d0893a3
8 changed files with 197 additions and 50 deletions

View File

@ -94,13 +94,12 @@
- (void)setNeedsLayout
{
ASDisplayNodeAssertThreadAffinity(self);
CGSize oldSize = self.calculatedSize;
[super setNeedsLayout];
if (_layoutDelegate != nil && self.isNodeLoaded) {
BOOL sizeChanged = !CGSizeEqualToSize(oldSize, self.calculatedSize);
ASPerformBlockOnMainThread(^{
BOOL sizeChanged = !CGSizeEqualToSize(oldSize, self.calculatedSize);
[_layoutDelegate nodeDidRelayout:self sizeChanged:sizeChanged];
});
}

View File

@ -628,7 +628,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize completion:(void(^)())completion
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
if (![self __shouldSize])
return nil;
@ -1868,7 +1867,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
if (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) {
ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize];
layoutSpec.isMutable = NO;
@ -1895,25 +1894,25 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
return _preferredFrameSize;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
return nil;
}
- (ASLayout *)calculatedLayout
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
return _layout;
}
- (CGSize)calculatedSize
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
return _layout.size;
}
@ -1944,7 +1943,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
- (void)invalidateCalculatedLayout
{
ASDisplayNodeAssertThreadAffinity(self);
ASDN::MutexLocker l(_propertyLock);
// This will cause -measureWithSizeRange: to actually compute the size instead of returning the previously cached size
_flags.isMeasured = NO;
}

View File

@ -335,9 +335,9 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingVie
{
_bridge_prologue_write;
_setToLayer(opaque, newOpaque);
// FIXME: Would like to setNeedsDisplay if opaqueness changed, but
// not safe to read old value in background.
// NOTE: If we're in the background, then when the pending state
// is applied to the view on main, we will call `setNeedsDisplay` if
// the new opaque value doesn't match the one on the layer.
}
- (BOOL)isUserInteractionEnabled
@ -539,8 +539,9 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { _pendingVie
{
_bridge_prologue_write;
_setToLayer(backgroundColor, newBackgroundColor.CGColor);
// FIXME: Would like to setNeedsDisplay if background color changed, but
// not safe to read old color in background.
// NOTE: If we're in the background, then when the pending state
// is applied to the view on main, we will call `setNeedsDisplay` if
// the new background color doesn't match the one on the layer.
}
- (UIColor *)tintColor

View File

@ -557,6 +557,13 @@ static UIColor *defaultTintColor = nil;
- (void)applyToLayer:(CALayer *)layer
{
ASPendingStateFlags flags = _flags;
if (flags.needsDisplay
|| (flags.setOpaque && opaque != layer.opaque)
|| (flags.setBackgroundColor && !CGColorEqualToColor(backgroundColor, layer.backgroundColor))) {
[layer setNeedsDisplay];
}
if (flags.setAnchorPoint)
layer.anchorPoint = anchorPoint;
@ -629,9 +636,6 @@ static UIColor *defaultTintColor = nil;
if (flags.setEdgeAntialiasingMask)
layer.edgeAntialiasingMask = edgeAntialiasingMask;
if (flags.needsDisplay)
[layer setNeedsDisplay];
if (flags.needsLayout)
[layer setNeedsLayout];
@ -658,6 +662,12 @@ static UIColor *defaultTintColor = nil;
CALayer *layer = view.layer;
ASPendingStateFlags flags = _flags;
if (flags.needsDisplay
|| (flags.setOpaque && opaque != view.opaque)
|| (flags.setBackgroundColor && !CGColorEqualToColor(backgroundColor, layer.backgroundColor))) {
[view setNeedsDisplay];
}
if (flags.setAnchorPoint)
layer.anchorPoint = anchorPoint;
@ -752,9 +762,6 @@ static UIColor *defaultTintColor = nil;
if (flags.setEdgeAntialiasingMask)
layer.edgeAntialiasingMask = edgeAntialiasingMask;
if (flags.needsDisplay)
[view setNeedsDisplay];
if (flags.needsLayout)
[view setNeedsLayout];

View File

@ -11,6 +11,8 @@
CCD3736F1C751C8A00AB7199 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD3736E1C751C8A00AB7199 /* ViewController.swift */; };
CCD373741C751C8A00AB7199 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CCD373731C751C8A00AB7199 /* Assets.xcassets */; };
CCD373771C751C8A00AB7199 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CCD373751C751C8A00AB7199 /* LaunchScreen.storyboard */; };
CCD3737F1C7520AB00AB7199 /* DemoCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD3737E1C7520AB00AB7199 /* DemoCellNode.swift */; };
CCD373811C75228900AB7199 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCD373801C75228900AB7199 /* Utilities.swift */; };
FE56E788869496B3522E8AE2 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D966CA4D089E4178A58E447C /* Pods.framework */; };
/* End PBXBuildFile section */
@ -23,6 +25,8 @@
CCD373731C751C8A00AB7199 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
CCD373761C751C8A00AB7199 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
CCD373781C751C8A00AB7199 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CCD3737E1C7520AB00AB7199 /* DemoCellNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoCellNode.swift; sourceTree = "<group>"; };
CCD373801C75228900AB7199 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
D966CA4D089E4178A58E447C /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@ -78,6 +82,8 @@
children = (
CCD3736C1C751C8A00AB7199 /* AppDelegate.swift */,
CCD3736E1C751C8A00AB7199 /* ViewController.swift */,
CCD373801C75228900AB7199 /* Utilities.swift */,
CCD3737E1C7520AB00AB7199 /* DemoCellNode.swift */,
CCD373731C751C8A00AB7199 /* Assets.xcassets */,
CCD373751C751C8A00AB7199 /* LaunchScreen.storyboard */,
CCD373781C751C8A00AB7199 /* Info.plist */,
@ -206,7 +212,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CCD3737F1C7520AB00AB7199 /* DemoCellNode.swift in Sources */,
CCD3736F1C751C8A00AB7199 /* ViewController.swift in Sources */,
CCD373811C75228900AB7199 /* Utilities.swift in Sources */,
CCD3736D1C751C8A00AB7199 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -0,0 +1,87 @@
//
// DemoCellNode.swift
// BackgroundPropertySetting
//
// Created by Adlai Holler on 2/17/16.
// Copyright © 2016 Adlai Holler. All rights reserved.
//
import UIKit
import AsyncDisplayKit
final class DemoCellNode: ASCellNode {
let childA = ASDisplayNode()
let childB = ASDisplayNode()
var state = State.Right
override init() {
super.init()
usesImplicitHierarchyManagement = true
}
override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
let specA = ASRatioLayoutSpec(ratio: 1, child: childA)
specA.flexBasis = ASRelativeDimensionMakeWithPoints(1)
specA.flexGrow = true
let specB = ASRatioLayoutSpec(ratio: 1, child: childB)
specB.flexBasis = ASRelativeDimensionMakeWithPoints(1)
specB.flexGrow = true
let children = state.isReverse ? [ specB, specA ] : [ specA, specB ]
let direction: ASStackLayoutDirection = state.isVertical ? .Vertical : .Horizontal
return ASStackLayoutSpec(direction: direction,
spacing: 20,
justifyContent: .SpaceAround,
alignItems: .Center,
children: children)
}
override func animateLayoutTransition(context: ASContextTransitioning!) {
childA.frame = context.initialFrameForNode(childA)
childB.frame = context.initialFrameForNode(childB)
let tinyDelay = drand48() / 10
UIView.animateWithDuration(0.5, delay: tinyDelay, usingSpringWithDamping: 0.9, initialSpringVelocity: 1.5, options: .BeginFromCurrentState, animations: { () -> Void in
self.childA.frame = context.finalFrameForNode(self.childA)
self.childB.frame = context.finalFrameForNode(self.childB)
}, completion: {
context.completeTransition($0)
})
}
enum State {
case Right
case Up
case Left
case Down
var isVertical: Bool {
switch self {
case .Up, .Down:
return true
default:
return false
}
}
var isReverse: Bool {
switch self {
case .Left, .Up:
return true
default:
return false
}
}
mutating func advance() {
switch self {
case .Right:
self = .Up
case .Up:
self = .Left
case .Left:
self = .Down
case .Down:
self = .Right
}
}
}
}

View File

@ -0,0 +1,15 @@
//
// Utilities.swift
// BackgroundPropertySetting
//
// Created by Adlai Holler on 2/17/16.
// Copyright © 2016 Adlai Holler. All rights reserved.
//
import UIKit
extension UIColor {
static func random() -> UIColor {
return UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0)
}
}

View File

@ -9,56 +9,87 @@
import UIKit
import AsyncDisplayKit
final class ViewController: ASViewController, ASTableDelegate, ASTableDataSource {
final class ViewController: ASViewController, ASCollectionDelegate, ASCollectionDataSource {
let itemCount = 1000
var tableNode: ASTableNode {
return node as! ASTableNode
let itemSize: CGSize
let padding: CGFloat
var collectionNode: ASCollectionNode {
return node as! ASCollectionNode
}
init() {
super.init(node: ASTableNode(style: .Plain))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Update", style: .Plain, target: self, action: "didTapUpdateButton")
tableNode.delegate = self
tableNode.dataSource = self
title = "Background Node Updating Demo"
let layout = UICollectionViewFlowLayout()
(padding, itemSize) = ViewController.computeLayoutSizesForMainScreen()
layout.minimumInteritemSpacing = padding
layout.minimumLineSpacing = padding
super.init(node: ASCollectionNode(collectionViewLayout: layout))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Color", style: .Plain, target: self, action: "didTapColorsButton")
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Layout", style: .Plain, target: self, action: "didTapLayoutButton")
collectionNode.delegate = self
collectionNode.dataSource = self
title = "Background Updating"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let rowCount = 20
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowCount
// MARK: ASCollectionDataSource
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount
}
func tableView(tableView: ASTableView, nodeBlockForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock {
func collectionView(collectionView: ASCollectionView, nodeBlockForItemAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock {
return {
let node = ASCellNode()
node.backgroundColor = getRandomColor()
let node = DemoCellNode()
node.backgroundColor = UIColor.random()
node.childA.backgroundColor = UIColor.random()
node.childB.backgroundColor = UIColor.random()
return node
}
}
@objc private func didTapUpdateButton() {
let currentlyVisibleNodes = tableNode.view.visibleNodes()
func collectionView(collectionView: ASCollectionView, constrainedSizeForNodeAtIndexPath indexPath: NSIndexPath) -> ASSizeRange {
return ASSizeRangeMake(itemSize, itemSize)
}
// MARK: Action Handling
@objc private func didTapColorsButton() {
let currentlyVisibleNodes = collectionNode.view.visibleNodes()
let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)
dispatch_async(queue) {
for case let node as ASCellNode in currentlyVisibleNodes {
node.backgroundColor = getRandomColor()
for case let node as DemoCellNode in currentlyVisibleNodes {
node.backgroundColor = UIColor.random()
}
}
}
}
func getRandomColor() -> UIColor{
let randomRed:CGFloat = CGFloat(drand48())
let randomGreen:CGFloat = CGFloat(drand48())
let randomBlue:CGFloat = CGFloat(drand48())
return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
@objc private func didTapLayoutButton() {
let currentlyVisibleNodes = collectionNode.view.visibleNodes()
let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)
dispatch_async(queue) {
for case let node as DemoCellNode in currentlyVisibleNodes {
node.state.advance()
node.setNeedsLayout()
}
}
}
// MARK: Static
static func computeLayoutSizesForMainScreen() -> (padding: CGFloat, itemSize: CGSize) {
let numberOfColumns = 4
let screen = UIScreen.mainScreen()
let scale = screen.scale
let screenWidth = Int(screen.bounds.width * screen.scale)
let itemWidthPx = (screenWidth - (numberOfColumns - 1)) / numberOfColumns
let leftover = screenWidth - itemWidthPx * numberOfColumns
let paddingPx = leftover / (numberOfColumns - 1)
let itemDimension = CGFloat(itemWidthPx) / scale
let padding = CGFloat(paddingPx) / scale
return (padding: padding, itemSize: CGSize(width: itemDimension, height: itemDimension))
}
}