mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-05-18 09:59:46 +08:00
Initial exploratory stab at the main challenge of the app - visualizing ASLayoutSpecs
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
self.layer.borderWidth = 2;
|
||||
self.layer.borderColor = [[UIColor blackColor] CGColor];
|
||||
self.backgroundColor = [UIColor purpleColor];
|
||||
|
||||
self.alignSelf = ASStackLayoutAlignSelfEnd;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
15
examples/ASLayoutSpecPlayground/Sample/EditorNode.h
Normal file
15
examples/ASLayoutSpecPlayground/Sample/EditorNode.h
Normal 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
|
||||
48
examples/ASLayoutSpecPlayground/Sample/EditorNode.m
Normal file
48
examples/ASLayoutSpecPlayground/Sample/EditorNode.m
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user