Initial exploratory stab at the main challenge of the app - visualizing ASLayoutSpecs

This commit is contained in:
Hannah Troisi
2016-03-19 17:05:15 -07:00
parent afdb3f9a8a
commit 8d7871fe9a
12 changed files with 228 additions and 25 deletions

View File

@@ -31,6 +31,8 @@
#import "ASLayoutSpec.h"
#import "ASCellNode.h"
#import "ASStaticLayoutSpec.h" // FIXME: remove later
NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp";
@@ -1789,8 +1791,23 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
{
ASDN::MutexLocker l(_propertyLock);
if (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) {
ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize];
ASStaticLayoutSpec *staticSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[[self layoutSpecThatFits:constrainedSize]]];
ASLayoutSpec *layoutSpec = staticSpec;
// for (id <ASLayoutable> sublayoutable in layoutSpec.children) {
// BOOL isNode = [sublayoutable isKindOfClass:[ASDisplayNode class]];
//
// if (!isNode) {
// // create a magical type of node that can wrap the layoutSpec (for visualization debug)
//
// }
// }
//
layoutSpec.isMutable = NO;
ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize];
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.
if (layout.layoutableObject != self) {
@@ -1798,11 +1815,15 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
layout = [ASLayout layoutWithLayoutableObject:self size:layout.size sublayouts:@[layout]];
}
return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) {
// NSLog(@"%@, %@", evaluatedLayout, evaluatedLayout.layoutableObject);
NSLog(@"\n%@", [evaluatedLayout.layoutableObject asciiArtString]);
if (self.usesImplicitHierarchyManagement) {
return ASObjectIsEqual(layout, evaluatedLayout) == NO && [evaluatedLayout.layoutableObject isKindOfClass:[ASDisplayNode class]];
} else {
return [_subnodes containsObject:evaluatedLayout.layoutableObject];
}
}];
} else {
// If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used,
@@ -2622,6 +2643,18 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
return self;
}
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)asciiArtString
{
return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]];
}
- (NSString *)asciiArtName
{
return NSStringFromClass([self class]);
}
#if TARGET_OS_TV
#pragma mark - UIFocusEnvironment Protocol (tvOS)
@@ -2714,18 +2747,6 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
return subtree;
}
#pragma mark - ASLayoutableAsciiArtProtocol
- (NSString *)asciiArtString
{
return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]];
}
- (NSString *)asciiArtName
{
return NSStringFromClass([self class]);
}
@end
// We use associated objects as a last resort if our view is not a _ASDisplayView ie it doesn't have the _node ivar to write to

View File

@@ -110,7 +110,7 @@ extern BOOL CGPointIsNull(CGPoint point)
for (ASLayout *sublayout in context.layout.sublayouts) {
// Mark layout trees that have already been flattened for future identification of immediate sublayouts
BOOL flattened = context.flattened ? : context.layout.flattened;
BOOL flattened = context.flattened ? YES : context.layout.flattened;
queue.push({sublayout, context.absolutePosition + sublayout.position, NO, flattened});
}
}

View File

@@ -17,6 +17,10 @@
#import "ASLayout.h"
#import "ASLayoutOptions.h"
#import "ASThread.h"
#import "ASDisplayNode+Subclasses.h" // FIXME: remove this later
#import "ASDisplayNode+Beta.h" // FIXME: remove this later
#import "ASInsetLayoutSpec.h" // FIXME: remove this later
#import "ASControlNode.h" // FIXME: remove this later
#import <objc/runtime.h>
@@ -25,6 +29,43 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
@interface ASLayoutSpec()
@property (nonatomic, strong) NSMutableDictionary *layoutChildren;
@property (nonatomic, assign) BOOL neverMagicNode;
@end
@interface ASLayoutSpecMagicNode : ASControlNode
@property (nonatomic, strong) ASLayoutSpec *layoutSpec;
- (instancetype)initWithLayoutSpec:(ASLayoutSpec *)layoutSpec;
@end
@implementation ASLayoutSpecMagicNode
- (instancetype)initWithLayoutSpec:(ASLayoutSpec *)layoutSpec
{
self = [super init];
if (self) {
self.layoutSpec = layoutSpec;
self.usesImplicitHierarchyManagement = YES;
self.layer.borderColor = [[UIColor redColor] CGColor];
self.layer.borderWidth = 2;
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
insetSpec.neverMagicNode = YES;
self.layoutSpec.neverMagicNode = YES;
UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10);
insetSpec.insets = insets;
insetSpec.child = self.layoutSpec;
return insetSpec;
}
@end
@implementation ASLayoutSpec
@@ -52,7 +93,8 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
- (id<ASLayoutable>)finalLayoutable
{
return self;
return (self.neverMagicNode) ? self : [[ASLayoutSpecMagicNode alloc] initWithLayoutSpec:self];
}
- (id<ASLayoutable>)layoutableToAddFromLayoutable:(id<ASLayoutable>)child

View File

@@ -13,7 +13,7 @@
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/ASStackLayoutable.h>
#import <AsyncDisplayKit/ASStaticLayoutable.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
#import <AsyncDisplayKit/ASLayoutablePrivate.h>
@class ASLayout;
@@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
* access to the options via convenience properties. If you are creating custom layout spec, then you can
* extend the backing layout options class to accommodate any new layout options.
*/
@protocol ASLayoutable <ASStackLayoutable, ASStaticLayoutable, ASLayoutablePrivate>
@protocol ASLayoutable <ASStackLayoutable, ASStaticLayoutable, ASLayoutablePrivate, ASLayoutableAsciiArtProtocol>
/**
* @abstract Calculate a layout based on given size range.

View File

@@ -16,6 +16,8 @@
76466F331C9DFFC4006C4D2D /* ColorNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 76466F2D1C9DFFC4006C4D2D /* ColorNode.m */; };
76466F341C9DFFC4006C4D2D /* PlaygroundNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 76466F2F1C9DFFC4006C4D2D /* PlaygroundNode.m */; };
76466F351C9DFFC4006C4D2D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 76466F311C9DFFC4006C4D2D /* ViewController.m */; };
76F58D591C9E114B004512CC /* EditorNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 76F58D581C9E114B004512CC /* EditorNode.m */; };
76F58D5C1C9E15C1004512CC /* PlaygroundContainerNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 76F58D5B1C9E15C1004512CC /* PlaygroundContainerNode.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -35,6 +37,10 @@
76466F2F1C9DFFC4006C4D2D /* PlaygroundNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaygroundNode.m; sourceTree = "<group>"; };
76466F301C9DFFC4006C4D2D /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
76466F311C9DFFC4006C4D2D /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
76F58D571C9E114B004512CC /* EditorNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditorNode.h; sourceTree = "<group>"; };
76F58D581C9E114B004512CC /* EditorNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EditorNode.m; sourceTree = "<group>"; };
76F58D5A1C9E15C1004512CC /* PlaygroundContainerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaygroundContainerNode.h; sourceTree = "<group>"; };
76F58D5B1C9E15C1004512CC /* PlaygroundContainerNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaygroundContainerNode.m; sourceTree = "<group>"; };
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -78,10 +84,14 @@
76466F2B1C9DFFC4006C4D2D /* AppDelegate.m */,
76466F301C9DFFC4006C4D2D /* ViewController.h */,
76466F311C9DFFC4006C4D2D /* ViewController.m */,
76F58D5A1C9E15C1004512CC /* PlaygroundContainerNode.h */,
76F58D5B1C9E15C1004512CC /* PlaygroundContainerNode.m */,
76466F2E1C9DFFC4006C4D2D /* PlaygroundNode.h */,
76466F2F1C9DFFC4006C4D2D /* PlaygroundNode.m */,
76466F2C1C9DFFC4006C4D2D /* ColorNode.h */,
76466F2D1C9DFFC4006C4D2D /* ColorNode.m */,
76F58D571C9E114B004512CC /* EditorNode.h */,
76F58D581C9E114B004512CC /* EditorNode.m */,
05E2128419D4DB510098F589 /* Supporting Files */,
);
path = Sample;
@@ -237,10 +247,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
76F58D591C9E114B004512CC /* EditorNode.m in Sources */,
76466F321C9DFFC4006C4D2D /* AppDelegate.m in Sources */,
05E2128719D4DB510098F589 /* main.m in Sources */,
76466F331C9DFFC4006C4D2D /* ColorNode.m in Sources */,
76466F341C9DFFC4006C4D2D /* PlaygroundNode.m in Sources */,
76F58D5C1C9E15C1004512CC /* PlaygroundContainerNode.m in Sources */,
76466F351C9DFFC4006C4D2D /* ViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -25,7 +25,7 @@
self.layer.borderWidth = 2;
self.layer.borderColor = [[UIColor blackColor] CGColor];
self.backgroundColor = [UIColor purpleColor];
self.alignSelf = ASStackLayoutAlignSelfEnd;
}
return self;

View File

@@ -0,0 +1,15 @@
//
// EditorNode.h
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface EditorNode : ASDisplayNode
@property (nonatomic, strong) id<ASLayoutable> layoutableToEdit;
@end

View File

@@ -0,0 +1,48 @@
//
// EditorNode.m
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "EditorNode.h"
@implementation EditorNode
{
ASTextNode *_textNode;
}
- (void)setLayoutableToEdit:(id<ASLayoutable>)layoutableToEdit
{
if (_layoutableToEdit != layoutableToEdit) {
_layoutableToEdit = layoutableToEdit;
_textNode.attributedString = [self attributedStringFromLayout:_layoutableToEdit];
self.backgroundColor = [UIColor colorWithRed:40/255.0 green:43/255.0 blue:53/255.0 alpha:1.0];
}
}
- (instancetype)init
{
self = [super init];
if (self) {
self.usesImplicitHierarchyManagement = YES;
_textNode = [[ASTextNode alloc] init];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) child:_textNode];
return insetSpec;
}
- (NSAttributedString *)attributedStringFromLayout:(id<ASLayoutable>)layoutable
{
NSDictionary *attributes = @{NSForegroundColorAttributeName : [UIColor whiteColor],
NSFontAttributeName : [UIFont fontWithName:@"Menlo-Regular" size:12]};
return [[NSAttributedString alloc] initWithString:[layoutable asciiArtString] attributes:attributes];
}
@end

View File

@@ -0,0 +1,13 @@
//
// PlaygroundContainerNode.h
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface PlaygroundContainerNode : ASDisplayNode
@end

View File

@@ -0,0 +1,44 @@
//
// PlaygroundContainerNode.m
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "PlaygroundContainerNode.h"
#import "PlaygroundNode.h"
#import "EditorNode.h"
@implementation PlaygroundContainerNode
{
EditorNode *_editorNode;
PlaygroundNode *_playgroundNode;
}
- (instancetype)init
{
self = [super init];
if (self) {
self.usesImplicitHierarchyManagement = YES;
_editorNode = [[EditorNode alloc] init];
_editorNode.flexBasis = ASRelativeDimensionMakeWithPercent(1.0);
_playgroundNode = [[PlaygroundNode alloc] init];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.children = @[_playgroundNode, _editorNode];
verticalStack.horizontalAlignment = ASAlignmentMiddle;
_editorNode.layoutableToEdit = verticalStack;
return verticalStack;
}
@end

View File

@@ -40,8 +40,14 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
NSMutableArray *children = [[NSMutableArray alloc] init];
for (ASDisplayNode *node in _colorNodes) {
UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10);
ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:node];
[children addObject:insetSpec];
}
ASStackLayoutSpec *innerStack = [ASStackLayoutSpec verticalStackLayoutSpec];
innerStack.children = _colorNodes;
innerStack.children = children;
ASStackLayoutSpec *outerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
outerStack.children = @[innerStack, _individualColorNode];

View File

@@ -7,7 +7,8 @@
//
#import "ViewController.h"
#import "PlaygroundNode.h"
#import "PlaygroundContainerNode.h"
#import "ASDisplayNode+Beta.h" // FIXME?
@interface ViewController ()
@@ -21,14 +22,15 @@
- (instancetype)init
{
self = [super initWithNode:[[PlaygroundNode alloc] init]];
self = [super initWithNode:[[PlaygroundContainerNode alloc] init]];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
@end