mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-05-17 16:53:33 +08:00
added new ASLayoutableInspectorNode features
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
#import "ASControlNode.h"
|
||||
#import "ASControlNode+Subclasses.h"
|
||||
#import "ASThread.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASLayoutSpec+Debug.h"
|
||||
#import "ASLayoutableInspectorNode.h"
|
||||
|
||||
// UIControl allows dragging some distance outside of the control itself during
|
||||
// tracking. This value depends on the device idiom (25 or 70 points), so
|
||||
@@ -87,9 +90,26 @@ static BOOL _enableHitTestDebug = NO;
|
||||
|
||||
// As we have no targets yet, we start off with user interaction off. When a target is added, it'll get turned back on.
|
||||
self.userInteractionEnabled = NO;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)inspectElement
|
||||
{
|
||||
[ASLayoutableInspectorNode sharedInstance].layoutableToEdit = self;
|
||||
}
|
||||
|
||||
- (void)setHierarchyState:(ASHierarchyState)hierarchyState
|
||||
{
|
||||
[super setHierarchyState:hierarchyState];
|
||||
|
||||
// FIXME: handle disabling hierarchy state on nodes that had previously enabled it (remove target)
|
||||
if (ASHierarchyStateIncludesVisualizeLayoutSpecs(hierarchyState)) {
|
||||
[self addTarget:self action:@selector(inspectElement) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
// NSLog(@"%@", self);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUserInteractionEnabled:(BOOL)userInteractionEnabled
|
||||
{
|
||||
[super setUserInteractionEnabled:userInteractionEnabled];
|
||||
@@ -236,6 +256,7 @@ static BOOL _enableHitTestDebug = NO;
|
||||
{
|
||||
NSParameterAssert(action);
|
||||
NSParameterAssert(controlEventMask != 0);
|
||||
ASDisplayNodeAssert(!self.isLayerBacked, @"ASControlNode is layer backed, will never be able to call target in target:action: pair.");
|
||||
|
||||
ASDN::MutexLocker l(_controlLock);
|
||||
|
||||
|
||||
@@ -1838,6 +1838,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
[ASLayoutSpec setShouldVisualizeLayoutSpecs2:YES];
|
||||
}
|
||||
|
||||
|
||||
ASStaticLayoutSpec *staticSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[[self layoutSpecThatFits:constrainedSize]]];
|
||||
|
||||
ASLayoutSpec *layoutSpec = staticSpec;
|
||||
|
||||
@@ -33,10 +33,11 @@ static BOOL __shouldVisualizeLayoutSpecs = NO;
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.layer.borderWidth = 2;
|
||||
self.layoutSpec = layoutSpec;
|
||||
self.usesImplicitHierarchyManagement = YES;
|
||||
self.layer.borderColor = [[UIColor redColor] CGColor];
|
||||
self.layer.borderWidth = 2;
|
||||
|
||||
[self addTarget:self action:@selector(layoutMagicNodeTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
}
|
||||
return self;
|
||||
@@ -47,16 +48,35 @@ static BOOL __shouldVisualizeLayoutSpecs = NO;
|
||||
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; // FIXME: need to auto pass properties to children
|
||||
insetSpec.neverShouldVisualize = YES;
|
||||
self.layoutSpec.neverShouldVisualize = YES;
|
||||
UIEdgeInsets insets = UIEdgeInsetsZero; //UIEdgeInsetsMake(10, 10, 10, 10);
|
||||
CGFloat insetFloat = [ASLayoutableInspectorNode sharedInstance].vizNodeInsetSize;
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(insetFloat, insetFloat, insetFloat, insetFloat);
|
||||
// UIEdgeInsets insets = UIEdgeInsetsZero;
|
||||
|
||||
// propogate child's layoutSpec properties to the inset that we are adding
|
||||
insetSpec.flexGrow = _layoutSpec.flexGrow;
|
||||
insetSpec.flexGrow = _layoutSpec.flexGrow; // FIXME:
|
||||
insetSpec.flexShrink = _layoutSpec.flexShrink;
|
||||
insetSpec.alignSelf = _layoutSpec.alignSelf;
|
||||
insetSpec.alignSelf = _layoutSpec.alignSelf;
|
||||
insetSpec.insets = insets;
|
||||
insetSpec.child = self.layoutSpec;
|
||||
|
||||
insetSpec.insets = insets;
|
||||
insetSpec.child = self.layoutSpec;
|
||||
return self.layoutSpec;
|
||||
return insetSpec; //self.layoutSpec;
|
||||
}
|
||||
|
||||
- (void)setLayoutSpec:(ASLayoutSpec *)layoutSpec
|
||||
{
|
||||
_layoutSpec = layoutSpec;
|
||||
|
||||
// self.flexGrow = _layoutSpec.flexGrow;
|
||||
// self.flexShrink = _layoutSpec.flexShrink;
|
||||
// self.alignSelf = _layoutSpec.alignSelf;
|
||||
|
||||
if ([layoutSpec isKindOfClass:[ASInsetLayoutSpec class]]) {
|
||||
self.layer.borderColor = [[UIColor redColor] CGColor];
|
||||
|
||||
} else if ([layoutSpec isKindOfClass:[ASStackLayoutSpec class]]) {
|
||||
self.layer.borderColor = [[UIColor greenColor] CGColor];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutMagicNodeTapped:(UIGestureRecognizer *)sender
|
||||
@@ -64,5 +84,10 @@ static BOOL __shouldVisualizeLayoutSpecs = NO;
|
||||
[[ASLayoutableInspectorNode sharedInstance] setLayoutableToEdit:self.layoutSpec];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [self.layoutSpec description]; // FIXME: expand on layoutSpec description (e.g. have StackLayoutSpec return horz/vert)
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@protocol ASLayoutableInspectorNodeDelegate <NSObject>
|
||||
|
||||
- (void)shouldShowMasterSplitViewController;
|
||||
|
||||
- (void)toggleVizualization:(BOOL)toggle;
|
||||
@end
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
@property (nonatomic, strong) id<ASLayoutable> layoutableToEdit;
|
||||
@property (nonatomic, strong) id<ASLayoutableInspectorNodeDelegate> delegate;
|
||||
@property (nonatomic, assign) CGFloat vizNodeInsetSize;
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// Navigate layout hierarchy
|
||||
ASButtonNode *_parentNodeNavBtn;
|
||||
ASButtonNode *_siblingNodeRightNavBtn;
|
||||
ASButtonNode *_addNodeNavBtn;
|
||||
ASButtonNode *_addLayoutSpecNavBtn;
|
||||
ASButtonNode *_siblingNodeLefttNavBtn;
|
||||
ASButtonNode *_childNodeNavBtn;
|
||||
|
||||
@@ -48,7 +50,12 @@
|
||||
|
||||
// LayoutSpec properties
|
||||
ASTextNode *_layoutSpecPropertiesSectionTitle;
|
||||
|
||||
|
||||
// debug help
|
||||
ASTextNode *_debugSectionTitle;
|
||||
ASTextNode *_vizNodeInsetSizeTitle;
|
||||
ASButtonNode *_vizNodeInsetSizeBtn;
|
||||
ASButtonNode *_vizNodeBordersBtn;
|
||||
}
|
||||
|
||||
#pragma mark - class methods
|
||||
@@ -72,6 +79,8 @@
|
||||
|
||||
self.usesImplicitHierarchyManagement = YES;
|
||||
|
||||
_vizNodeInsetSize = 0;
|
||||
|
||||
_itemDescription = [[ASTextNode alloc] init];
|
||||
|
||||
_itemPropertiesSectionTitle = [[ASTextNode alloc] init];
|
||||
@@ -80,6 +89,10 @@
|
||||
_layoutablePropertiesSectionTitle.attributedString = [self attributedStringFromString:@"<Layoutable> Properties"];
|
||||
_layoutSpecPropertiesSectionTitle = [[ASTextNode alloc] init];
|
||||
_layoutSpecPropertiesSectionTitle.attributedString = [self attributedStringFromString:@"<LayoutSpec> Properties"];
|
||||
_debugSectionTitle = [[ASTextNode alloc] init];
|
||||
_debugSectionTitle.attributedString = [self attributedStringFromString:@"debugging help"];
|
||||
_vizNodeInsetSizeTitle = [[ASTextNode alloc] init];
|
||||
_vizNodeInsetSizeTitle.attributedString = [self attributedStringFromString:@"inset ASLayoutSpecs"];
|
||||
|
||||
_flexGrowBtn = [self makeBtnNodeWithTitle:@"flexGrow"];
|
||||
[_flexGrowBtn addTarget:self action:@selector(setFlexGrowValue:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
@@ -101,11 +114,28 @@
|
||||
_itemBackgroundColorBtn = [self makeBtnNodeWithTitle:@"node color"];
|
||||
[_itemBackgroundColorBtn addTarget:self action:@selector(changeColor:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
|
||||
_vizNodeBordersBtn = [self makeBtnNodeWithTitle:@"visualize ASLayoutSpecs"];
|
||||
[_vizNodeBordersBtn addTarget:self action:@selector(setVizNodeBorders:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
_vizNodeBordersBtn.selected = YES;
|
||||
|
||||
_vizNodeInsetSizeBtn = [self makeBtnNodeWithTitle:@"overlap ASLayoutSpecs"];
|
||||
[_vizNodeInsetSizeBtn addTarget:self action:@selector(setVizNodeInsets:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
_vizNodeInsetSizeBtn.selected = YES;
|
||||
|
||||
_parentNodeNavBtn = [self makeBtnNodeWithTitle:@"parent\nnode"];
|
||||
_siblingNodeRightNavBtn = [self makeBtnNodeWithTitle:@"sibling\nnode"];
|
||||
_addNodeNavBtn = [self makeBtnNodeWithTitle:@"add\nnode"];
|
||||
_addLayoutSpecNavBtn = [self makeBtnNodeWithTitle:@"add\nlayoutSpec"];
|
||||
_siblingNodeLefttNavBtn = [self makeBtnNodeWithTitle:@"sibling\nnode"];
|
||||
_childNodeNavBtn = [self makeBtnNodeWithTitle:@"child\nnode"];
|
||||
|
||||
// _parentNodeNavBtn.selected = YES;
|
||||
// _siblingNodeRightNavBtn.selected = YES;
|
||||
// _addNodeNavBtn.selected = YES;
|
||||
// _addLayoutSpecNavBtn.selected = YES;
|
||||
// _siblingNodeLefttNavBtn.selected = YES;
|
||||
// _childNodeNavBtn.selected = YES;
|
||||
|
||||
_slider = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
|
||||
UISlider *slider = [[UISlider alloc] init];
|
||||
return slider;
|
||||
@@ -131,16 +161,21 @@
|
||||
{
|
||||
// navigate layout hierarchy
|
||||
|
||||
_parentNodeNavBtn.alignSelf = ASStackLayoutAlignSelfCenter;
|
||||
_childNodeNavBtn.alignSelf = ASStackLayoutAlignSelfCenter;
|
||||
|
||||
ASStackLayoutSpec *horizontalStackNav = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
horizontalStackNav.flexGrow = YES;
|
||||
horizontalStackNav.alignSelf = ASStackLayoutAlignSelfCenter;
|
||||
horizontalStackNav.children = @[_siblingNodeLefttNavBtn, _siblingNodeRightNavBtn];
|
||||
|
||||
ASStackLayoutSpec *horizontalStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
horizontalStack.flexGrow = YES;
|
||||
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||
|
||||
spacer.flexGrow = YES;
|
||||
horizontalStack.children = @[_flexGrowBtn, spacer];
|
||||
_flexGrowValue.alignSelf = ASStackLayoutAlignSelfEnd; // FIXME: framework give a warning if you use ASAlignmentBottom!!!!!
|
||||
_flexGrowValue.alignSelf = ASStackLayoutAlignSelfEnd; // FIXME: make framework give a warning if you use ASAlignmentBottom!!!!!
|
||||
|
||||
ASStackLayoutSpec *horizontalStack2 = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
horizontalStack2.flexGrow = YES;
|
||||
@@ -153,7 +188,7 @@
|
||||
_flexBasisValue.alignSelf = ASStackLayoutAlignSelfEnd;
|
||||
|
||||
ASStackLayoutSpec *itemDescriptionStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
itemDescriptionStack.children = @[_itemDescription, _itemBackgroundColorBtn,];
|
||||
itemDescriptionStack.children = @[_itemDescription];
|
||||
itemDescriptionStack.spacing = 5;
|
||||
itemDescriptionStack.flexGrow = YES;
|
||||
|
||||
@@ -166,11 +201,16 @@
|
||||
layoutSpecStack.children = @[_layoutSpecPropertiesSectionTitle, _alignItemsBtn];
|
||||
layoutSpecStack.spacing = 5;
|
||||
layoutSpecStack.flexGrow = YES;
|
||||
|
||||
ASStackLayoutSpec *debugHelpStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
debugHelpStack.children = @[_debugSectionTitle, _vizNodeInsetSizeBtn, _vizNodeBordersBtn];
|
||||
debugHelpStack.spacing = 5;
|
||||
debugHelpStack.flexGrow = YES;
|
||||
|
||||
ASStackLayoutSpec *verticalLayoutableStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalLayoutableStack.flexGrow = YES;
|
||||
verticalLayoutableStack.spacing = 20;
|
||||
verticalLayoutableStack.children = @[_parentNodeNavBtn, horizontalStackNav, _childNodeNavBtn, itemDescriptionStack, layoutableStack, layoutSpecStack];
|
||||
verticalLayoutableStack.children = @[_parentNodeNavBtn, horizontalStackNav, _childNodeNavBtn, itemDescriptionStack, layoutableStack, layoutSpecStack, debugHelpStack];
|
||||
verticalLayoutableStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space
|
||||
|
||||
ASLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(100, 10, 10, 10) child:verticalLayoutableStack];
|
||||
@@ -202,28 +242,18 @@
|
||||
// _flexBasisBtn.selected = self.layoutableToEdit.flexShrink;
|
||||
// _flexBasisValue.attributedString = [self attributedStringFromString: (_flexBasisBtn.selected) ? @"YES" : @"NO"];
|
||||
|
||||
|
||||
NSUInteger alignSelfValue = [self.layoutableToEdit alignSelf];
|
||||
_alignSelfBtn.selected = alignSelfValue ? YES : NO;
|
||||
NSString *newTitle = [@"alignSelf:" stringByAppendingString:[self alignSelfName:alignSelfValue]];
|
||||
[_alignSelfBtn setAttributedTitle:[self attributedStringFromString:newTitle] forState:ASControlStateNormal];
|
||||
|
||||
// _alignItemsBtn.selected = YES;
|
||||
// if ([[self layoutSpec] isKindOfClass:[ASStackLayoutSpec class]]) {
|
||||
// NSUInteger alignItemsValue = [(ASStackLayoutSpec *)[self layoutSpec] alignItems];
|
||||
// newTitle = [@"alignItems:" stringByAppendingString:[self typeDisplayNameItems:alignItemsValue]];
|
||||
if ([self layoutSpec]) {
|
||||
_alignItemsBtn.enabled = YES;
|
||||
// NSUInteger alignItemsValue = [[self layoutSpec] alignItems];
|
||||
// newTitle = [@"alignItems:" stringByAppendingString:[self alignSelfName:alignItemsValue]];
|
||||
// [_alignItemsBtn setAttributedTitle:[self attributedStringFromString:newTitle] forState:ASControlStateNormal];
|
||||
// }
|
||||
//
|
||||
// if ([layoutable isKindOfClass:[ASLayoutSpec class]]) {
|
||||
// return [self attributedStringFromString:[(ASLayoutSpec *)layoutable asciiArtString]];
|
||||
// } else if ([layoutable isKindOfClass:[ASDisplayNode class]]) {
|
||||
// return [self attributedStringFromString:[(ASControlNode *)layoutable asciiArtString]];
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
@@ -261,7 +291,7 @@
|
||||
_alignSelfBtn.enabled = NO;
|
||||
_spacingBeforeBtn.enabled = NO;
|
||||
_spacingAfterBtn.enabled = NO;
|
||||
_alignItemsBtn.enabled = NO;
|
||||
_alignItemsBtn.enabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +408,34 @@
|
||||
[self updateInspectorWithLayoutable];
|
||||
}
|
||||
|
||||
- (void)setVizNodeInsets:(ASButtonNode *)sender
|
||||
{
|
||||
BOOL newState = !sender.selected;
|
||||
|
||||
if (newState == YES) {
|
||||
self.vizNodeInsetSize = 0;
|
||||
[self.delegate toggleVizualization:NO]; // FIXME
|
||||
[self.delegate toggleVizualization:YES]; // FIXME
|
||||
_vizNodeBordersBtn.selected = YES;
|
||||
|
||||
} else {
|
||||
self.vizNodeInsetSize = 10;
|
||||
[self.delegate toggleVizualization:NO]; // FIXME
|
||||
[self.delegate toggleVizualization:YES]; // FIXME
|
||||
}
|
||||
|
||||
sender.selected = newState;
|
||||
}
|
||||
|
||||
- (void)setVizNodeBorders:(ASButtonNode *)sender
|
||||
{
|
||||
BOOL newState = !sender.selected;
|
||||
|
||||
[self.delegate toggleVizualization:newState]; // FIXME
|
||||
|
||||
sender.selected = newState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
||||
@@ -129,7 +129,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
|
||||
{
|
||||
if ([child isKindOfClass:[ASLayoutSpec class]]) {
|
||||
[(ASLayoutSpec *)child setShouldVisualize:self.shouldVisualize];
|
||||
NSLog(@"%@ %@ %d", self, child, self.shouldVisualize);
|
||||
// NSLog(@"%@ %@ %d", self, child, self.shouldVisualize);
|
||||
}
|
||||
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
|
||||
self.layoutChildren[identifier] = [self layoutableToAddFromLayoutable:child];
|
||||
@@ -143,7 +143,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
|
||||
for (id<ASLayoutable> child in children) {
|
||||
if ([child isKindOfClass:[ASLayoutSpec class]]) {
|
||||
[(ASLayoutSpec *)child setShouldVisualize:self.shouldVisualize];
|
||||
NSLog(@"%@ %@ %d", self, child, self.shouldVisualize);
|
||||
// NSLog(@"%@ %@ %d", self, child, self.shouldVisualize);
|
||||
}
|
||||
[finalChildren addObject:[self layoutableToAddFromLayoutable:child]];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */; };
|
||||
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; };
|
||||
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; };
|
||||
7602C7651CA4F83100D0D917 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7602C7641CA4F83100D0D917 /* Utilities.m */; };
|
||||
7602C7671CA4FB5300D0D917 /* resizeHandle.png in Resources */ = {isa = PBXBuildFile; fileRef = 7602C7661CA4FB5300D0D917 /* resizeHandle.png */; };
|
||||
76466F321C9DFFC4006C4D2D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 76466F2B1C9DFFC4006C4D2D /* AppDelegate.m */; };
|
||||
76466F331C9DFFC4006C4D2D /* ColorNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 76466F2D1C9DFFC4006C4D2D /* ColorNode.m */; };
|
||||
76466F341C9DFFC4006C4D2D /* PlaygroundNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 76466F2F1C9DFFC4006C4D2D /* PlaygroundNode.m */; };
|
||||
@@ -29,6 +31,9 @@
|
||||
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; };
|
||||
6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; };
|
||||
7602C7631CA4F83100D0D917 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
|
||||
7602C7641CA4F83100D0D917 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
|
||||
7602C7661CA4FB5300D0D917 /* resizeHandle.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = resizeHandle.png; sourceTree = "<group>"; };
|
||||
76466F2A1C9DFFC4006C4D2D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
76466F2B1C9DFFC4006C4D2D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
76466F2C1C9DFFC4006C4D2D /* ColorNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorNode.h; sourceTree = "<group>"; };
|
||||
@@ -92,6 +97,8 @@
|
||||
76466F2F1C9DFFC4006C4D2D /* PlaygroundNode.m */,
|
||||
76466F2C1C9DFFC4006C4D2D /* ColorNode.h */,
|
||||
76466F2D1C9DFFC4006C4D2D /* ColorNode.m */,
|
||||
7602C7631CA4F83100D0D917 /* Utilities.h */,
|
||||
7602C7641CA4F83100D0D917 /* Utilities.m */,
|
||||
05E2128419D4DB510098F589 /* Supporting Files */,
|
||||
);
|
||||
path = Sample;
|
||||
@@ -102,6 +109,7 @@
|
||||
children = (
|
||||
0585427F19D4DBE100606EA6 /* Default-568h@2x.png */,
|
||||
6C2C82AA19EE274300767484 /* Default-667h@2x.png */,
|
||||
7602C7661CA4FB5300D0D917 /* resizeHandle.png */,
|
||||
6C2C82AB19EE274300767484 /* Default-736h@3x.png */,
|
||||
05E2128519D4DB510098F589 /* Info.plist */,
|
||||
05E2128619D4DB510098F589 /* main.m */,
|
||||
@@ -189,6 +197,7 @@
|
||||
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */,
|
||||
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */,
|
||||
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */,
|
||||
7602C7671CA4FB5300D0D917 /* resizeHandle.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -249,6 +258,7 @@
|
||||
files = (
|
||||
76466F321C9DFFC4006C4D2D /* AppDelegate.m in Sources */,
|
||||
05E2128719D4DB510098F589 /* main.m in Sources */,
|
||||
7602C7651CA4F83100D0D917 /* Utilities.m in Sources */,
|
||||
76466F331C9DFFC4006C4D2D /* ColorNode.m in Sources */,
|
||||
76466F341C9DFFC4006C4D2D /* PlaygroundNode.m in Sources */,
|
||||
76F58D5C1C9E15C1004512CC /* PlaygroundContainerNode.m in Sources */,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
|
||||
splitViewController.viewControllers = [NSArray arrayWithObjects:masterNav, detailNav, nil];
|
||||
splitViewController.delegate = detailViewController;
|
||||
splitViewController.maximumPrimaryColumnWidth = 200;
|
||||
|
||||
detailViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
@implementation PlaygroundContainerNode
|
||||
{
|
||||
PlaygroundNode *_playgroundNode;
|
||||
ASDisplayNode *_resizeHandle;
|
||||
ASImageNode *_resizeHandle;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
@@ -28,8 +28,9 @@
|
||||
|
||||
_playgroundNode = [[PlaygroundNode alloc] init];
|
||||
|
||||
_resizeHandle = [[ASDisplayNode alloc] init];
|
||||
_resizeHandle.backgroundColor = [UIColor greenColor];
|
||||
_resizeHandle = [[ASImageNode alloc] init];
|
||||
_resizeHandle.image = [UIImage imageNamed:@"resizeHandle"];
|
||||
_resizeHandle.userInteractionEnabled = YES;
|
||||
[self.view addSubnode:_resizeHandle];
|
||||
|
||||
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(resizePlayground:)];
|
||||
@@ -43,7 +44,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
#define RESIZE_HANDLE_SIZE 10
|
||||
#define RESIZE_HANDLE_SIZE 30
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
@@ -62,7 +63,8 @@
|
||||
_playgroundNode.flexGrow = YES;
|
||||
_playgroundNode.flexShrink = YES;
|
||||
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_playgroundNode]];
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10);
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_playgroundNode];
|
||||
}
|
||||
|
||||
- (void)resizePlayground:(UIGestureRecognizer *)sender
|
||||
|
||||
@@ -10,15 +10,24 @@
|
||||
#import "ColorNode.h"
|
||||
#import "AsyncDisplayKit+Debug.h"
|
||||
#import "ASLayoutableInspectorNode.h"
|
||||
#import "Utilities.h"
|
||||
|
||||
#define USER_IMAGE_HEIGHT 60
|
||||
#define HORIZONTAL_BUFFER 10
|
||||
#define VERTICAL_BUFFER 5
|
||||
#define FONT_SIZE 20
|
||||
|
||||
@implementation PlaygroundNode
|
||||
{
|
||||
NSArray *_colorNodes;
|
||||
ASDisplayNode *_individualColorNode;
|
||||
ASTextNode *_textNode1;
|
||||
ASTextNode *_textNode2;
|
||||
ASTextNode *_textNode3;
|
||||
ASNetworkImageNode *_userAvatarImageView;
|
||||
ASNetworkImageNode *_photoImageView;
|
||||
ASTextNode *_userNameLabel;
|
||||
ASTextNode *_photoLocationLabel;
|
||||
ASTextNode *_photoTimeIntervalSincePostLabel;
|
||||
ASTextNode *_photoLikesLabel;
|
||||
ASTextNode *_photoDescriptionLabel;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init
|
||||
@@ -26,95 +35,153 @@
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
|
||||
|
||||
self.backgroundColor = [UIColor whiteColor];
|
||||
self.usesImplicitHierarchyManagement = YES;
|
||||
// self.clipsToBounds = YES; // make outside bounds semi-transparent
|
||||
|
||||
ColorNode *node = [[ColorNode alloc] init];
|
||||
ColorNode *node2 = [[ColorNode alloc] init];
|
||||
ColorNode *node3 = [[ColorNode alloc] init];
|
||||
_colorNodes = @[node, node2, node3];
|
||||
|
||||
_individualColorNode = [[ColorNode alloc] init];
|
||||
_individualColorNode.backgroundColor = [UIColor orangeColor];
|
||||
_userAvatarImageView = [[ASNetworkImageNode alloc] init];
|
||||
_userAvatarImageView.URL = [NSURL URLWithString:@"https://s-media-cache-ak0.pinimg.com/avatars/503h_1458880322_140.jpg"];
|
||||
|
||||
// user interaction off by default
|
||||
_textNode1 = [[ASTextNode alloc] init];
|
||||
_textNode1.attributedString = [[NSAttributedString alloc] initWithString:@"test"];
|
||||
_textNode1.backgroundColor = [UIColor greenColor];
|
||||
_textNode1.userInteractionEnabled = YES;
|
||||
[_textNode1 addTarget:self action:@selector(textTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
// FIXME: autocomplete for this line seems broken
|
||||
[_userAvatarImageView setImageModificationBlock:^UIImage *(UIImage *image) {
|
||||
CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
|
||||
return [image makeCircularImageWithSize:profileImageSize];
|
||||
}];
|
||||
|
||||
_textNode2 = [[ASTextNode alloc] init];
|
||||
_textNode2.attributedString = [[NSAttributedString alloc] initWithString:@"Hhhhhhhhhheeeeeeeeeelllllloooooooo"];
|
||||
_textNode2.backgroundColor = [UIColor greenColor];
|
||||
_textNode2.userInteractionEnabled = YES;
|
||||
[_textNode2 addTarget:self action:@selector(textTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
_userNameLabel = [[ASTextNode alloc] init];
|
||||
_userNameLabel.attributedString = [self usernameAttributedStringWithFontSize:FONT_SIZE];
|
||||
|
||||
_textNode3 = [[ASTextNode alloc] init];
|
||||
_textNode3.attributedString = [[NSAttributedString alloc] initWithString:@"another test text node"];
|
||||
_textNode3.backgroundColor = [UIColor greenColor];
|
||||
_textNode3.userInteractionEnabled = YES;
|
||||
[_textNode3 addTarget:self action:@selector(textTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
_photoLocationLabel = [[ASTextNode alloc] init];
|
||||
_photoLocationLabel.maximumNumberOfLines = 1;
|
||||
_photoLocationLabel.attributedString = [self locationAttributedStringWithFontSize:FONT_SIZE];
|
||||
|
||||
_photoTimeIntervalSincePostLabel = [[ASTextNode alloc] init];
|
||||
_photoTimeIntervalSincePostLabel.attributedString = [self uploadDateAttributedStringWithFontSize:FONT_SIZE];
|
||||
|
||||
_photoImageView = [[ASNetworkImageNode alloc] init];
|
||||
_photoImageView.URL = [NSURL URLWithString:@"https://s-media-cache-ak0.pinimg.com/564x/9f/5b/3a/9f5b3a35640bc7a5d484b66124c48c46.jpg"];
|
||||
|
||||
_photoLikesLabel = [[ASTextNode alloc] init];
|
||||
_photoLikesLabel.attributedString = [self likesAttributedStringWithFontSize:FONT_SIZE];
|
||||
|
||||
_photoDescriptionLabel = [[ASTextNode alloc] init];
|
||||
_photoDescriptionLabel.attributedString = [self descriptionAttributedStringWithFontSize:FONT_SIZE];
|
||||
_photoDescriptionLabel.maximumNumberOfLines = 3;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)textTapped:(UIGestureRecognizer *)sender
|
||||
{
|
||||
[ASLayoutableInspectorNode sharedInstance].layoutableToEdit = (ASTextNode *)sender;
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
NSMutableArray *children = [[NSMutableArray alloc] init];
|
||||
for (ASDisplayNode *node in _colorNodes) {
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(10, 10, 10, 10);
|
||||
node.flexGrow = YES;
|
||||
ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:node];
|
||||
insetSpec.flexGrow = YES;
|
||||
[children addObject:insetSpec];
|
||||
// username / photo location header vertical stack
|
||||
|
||||
_userNameLabel.flexShrink = YES;
|
||||
_photoLocationLabel.flexShrink = YES;
|
||||
|
||||
ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
headerSubStack.flexShrink = YES;
|
||||
|
||||
if (_photoLocationLabel.attributedString) {
|
||||
[headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]];
|
||||
} else {
|
||||
[headerSubStack setChildren:@[_userNameLabel]];
|
||||
}
|
||||
|
||||
[children addObject:_textNode1];
|
||||
[children addObject:_textNode2];
|
||||
[children addObject:_textNode3];
|
||||
// header stack
|
||||
|
||||
_textNode1.flexShrink = YES;
|
||||
_textNode2.flexShrink = YES;
|
||||
_textNode3.flexShrink = YES;
|
||||
|
||||
ASStackLayoutSpec *innerStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
innerStack.children = children;
|
||||
innerStack.flexGrow = YES;
|
||||
innerStack.flexShrink = YES;
|
||||
|
||||
// _individualColorNode.preferredFrameSize = CGSizeMake(100, 600);
|
||||
_individualColorNode.flexGrow = YES;
|
||||
_individualColorNode.flexShrink = YES;
|
||||
_userAvatarImageView.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
|
||||
_photoTimeIntervalSincePostLabel.spacingBefore = HORIZONTAL_BUFFER; // hack to remove double spaces around spacer
|
||||
|
||||
ASStackLayoutSpec *outerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
outerStack.flexGrow = YES;
|
||||
outerStack.flexShrink = YES;
|
||||
outerStack.children = @[innerStack, _individualColorNode];
|
||||
outerStack.alignItems = ASStackLayoutAlignItemsStretch;
|
||||
UIEdgeInsets avatarInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
|
||||
ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageView];
|
||||
|
||||
return outerStack;
|
||||
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||
spacer.flexGrow = YES;
|
||||
spacer.flexShrink = YES;
|
||||
|
||||
ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
headerStack.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horizontal stack
|
||||
headerStack.justifyContent = ASStackLayoutJustifyContentStart; // justify content to the left side of the header stack
|
||||
headerStack.flexShrink = YES;
|
||||
headerStack.flexGrow = YES;
|
||||
|
||||
[headerStack setChildren:@[avatarInset, headerSubStack, spacer, _photoTimeIntervalSincePostLabel]];
|
||||
|
||||
// header inset stack
|
||||
|
||||
UIEdgeInsets insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER);
|
||||
ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:headerStack];
|
||||
headerWithInset.flexShrink = YES;
|
||||
headerWithInset.flexGrow = YES;
|
||||
|
||||
// footer stack
|
||||
|
||||
ASStackLayoutSpec *footerStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
footerStack.spacing = VERTICAL_BUFFER;
|
||||
|
||||
[footerStack setChildren:@[_photoLikesLabel, _photoDescriptionLabel]];
|
||||
|
||||
// footer inset stack
|
||||
|
||||
UIEdgeInsets footerInsets = UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER);
|
||||
ASInsetLayoutSpec *footerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:footerInsets child:footerStack];
|
||||
|
||||
// vertical stack
|
||||
|
||||
CGFloat cellWidth = constrainedSize.max.width;
|
||||
_photoImageView.preferredFrameSize = CGSizeMake(cellWidth, cellWidth); // constrain photo frame size
|
||||
|
||||
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // sretch headerStack to fill horizontal space
|
||||
[verticalStack setChildren:@[headerWithInset, _photoImageView, footerWithInset]];
|
||||
verticalStack.flexShrink = YES;
|
||||
|
||||
return verticalStack;
|
||||
}
|
||||
|
||||
//- (ASSizeRange)playgroundConstrainedSize
|
||||
//{
|
||||
// if (ASRangeIsEmpty(_playgroundConstrainedSize)) {
|
||||
// CGSize maxSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
|
||||
// _playgroundConstrainedSize = ASSizeRangeMake(maxSize, maxSize);
|
||||
// }
|
||||
// return _playgroundConstrainedSize;
|
||||
//}
|
||||
//
|
||||
//- (ASSizeRange)nodeConstrainedSize
|
||||
//{
|
||||
// return self.playgroundConstrainedSize;
|
||||
//}
|
||||
#pragma mark - helper methods
|
||||
|
||||
- (NSAttributedString *)usernameAttributedStringWithFontSize:(CGFloat)size
|
||||
{
|
||||
return [NSAttributedString attributedStringWithString:@"hannahmbanana"
|
||||
fontSize:size
|
||||
color:[UIColor darkBlueColor]
|
||||
firstWordColor:nil];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)locationAttributedStringWithFontSize:(CGFloat)size
|
||||
{
|
||||
return [NSAttributedString attributedStringWithString:@"San Fransisco, CA"
|
||||
fontSize:size
|
||||
color:[UIColor lightBlueColor]
|
||||
firstWordColor:nil];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)uploadDateAttributedStringWithFontSize:(CGFloat)size
|
||||
{
|
||||
return [NSAttributedString attributedStringWithString:@"30m"
|
||||
fontSize:size
|
||||
color:[UIColor lightGrayColor]
|
||||
firstWordColor:nil];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)likesAttributedStringWithFontSize:(CGFloat)size
|
||||
{
|
||||
return [NSAttributedString attributedStringWithString:@"♥︎ 17 likes"
|
||||
fontSize:size
|
||||
color:[UIColor darkBlueColor]
|
||||
firstWordColor:nil];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)descriptionAttributedStringWithFontSize:(CGFloat)size
|
||||
{
|
||||
NSString *string = [NSString stringWithFormat:@"hannahtroisi check out this cool pic from the internet!"];
|
||||
NSAttributedString *attrString = [NSAttributedString attributedStringWithString:string
|
||||
fontSize:size
|
||||
color:[UIColor darkGrayColor]
|
||||
firstWordColor:[UIColor darkBlueColor]];
|
||||
return attrString;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
39
examples/ASLayoutSpecPlayground/Sample/Utilities.h
Normal file
39
examples/ASLayoutSpecPlayground/Sample/Utilities.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Utilities.h
|
||||
// Flickrgram
|
||||
//
|
||||
// Created by Hannah Troisi on 3/9/16.
|
||||
// Copyright © 2016 Hannah Troisi. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIColor (Additions)
|
||||
|
||||
+ (UIColor *)darkBlueColor;
|
||||
+ (UIColor *)lightBlueColor;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIImage (Additions)
|
||||
|
||||
+ (UIImage *)followingButtonStretchableImageForCornerRadius:(CGFloat)cornerRadius following:(BOOL)followingEnabled;
|
||||
|
||||
- (UIImage *)makeCircularImageWithSize:(CGSize)size;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSString (Additions)
|
||||
|
||||
// returns a user friendly elapsed time such as '50s', '6m' or '3w'
|
||||
+ (NSString *)elapsedTimeStringSinceDate:(NSString *)uploadDateString;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSAttributedString (Additions)
|
||||
|
||||
+ (NSAttributedString *)attributedStringWithString:(NSString *)string fontSize:(CGFloat)size
|
||||
color:(UIColor *)color firstWordColor:(UIColor *)firstWordColor;
|
||||
|
||||
@end
|
||||
188
examples/ASLayoutSpecPlayground/Sample/Utilities.m
Normal file
188
examples/ASLayoutSpecPlayground/Sample/Utilities.m
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// Utilities.m
|
||||
// Flickrgram
|
||||
//
|
||||
// Created by Hannah Troisi on 3/9/16.
|
||||
// Copyright © 2016 Hannah Troisi. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Utilities.h"
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#define StrokeRoundedImages 0
|
||||
|
||||
@implementation UIColor (Additions)
|
||||
|
||||
+ (UIColor *)darkBlueColor
|
||||
{
|
||||
return [UIColor colorWithRed:18.0/255.0 green:86.0/255.0 blue:136.0/255.0 alpha:1.0];
|
||||
}
|
||||
|
||||
+ (UIColor *)lightBlueColor
|
||||
{
|
||||
return [UIColor colorWithRed:0.0 green:122.0/255.0 blue:1.0 alpha:1.0];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIImage (Additions)
|
||||
|
||||
+ (UIImage *)followingButtonStretchableImageForCornerRadius:(CGFloat)cornerRadius following:(BOOL)followingEnabled
|
||||
{
|
||||
CGSize unstretchedSize = CGSizeMake(2 * cornerRadius + 1, 2 * cornerRadius + 1);
|
||||
CGRect rect = (CGRect) {CGPointZero, unstretchedSize};
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];
|
||||
|
||||
// create a graphics context for the following status button
|
||||
UIGraphicsBeginImageContextWithOptions(unstretchedSize, NO, 0);
|
||||
|
||||
[path addClip];
|
||||
|
||||
if (followingEnabled) {
|
||||
|
||||
[[UIColor whiteColor] setFill];
|
||||
[path fill];
|
||||
|
||||
path.lineWidth = 3;
|
||||
[[UIColor lightBlueColor] setStroke];
|
||||
[path stroke];
|
||||
|
||||
} else {
|
||||
|
||||
[[UIColor lightBlueColor] setFill];
|
||||
[path fill];
|
||||
}
|
||||
|
||||
UIImage *followingBtnImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
UIImage *followingBtnImageStretchable = [followingBtnImage stretchableImageWithLeftCapWidth:cornerRadius
|
||||
topCapHeight:cornerRadius];
|
||||
return followingBtnImageStretchable;
|
||||
}
|
||||
|
||||
- (UIImage *)makeCircularImageWithSize:(CGSize)size
|
||||
{
|
||||
// make a CGRect with the image's size
|
||||
CGRect circleRect = (CGRect) {CGPointZero, size};
|
||||
|
||||
// begin the image context since we're not in a drawRect:
|
||||
UIGraphicsBeginImageContextWithOptions(circleRect.size, NO, 0);
|
||||
|
||||
// create a UIBezierPath circle
|
||||
UIBezierPath *circle = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:circleRect.size.width/2];
|
||||
|
||||
// clip to the circle
|
||||
[circle addClip];
|
||||
|
||||
// draw the image in the circleRect *AFTER* the context is clipped
|
||||
[self drawInRect:circleRect];
|
||||
|
||||
// create a border (for white background pictures)
|
||||
#if StrokeRoundedImages
|
||||
circle.lineWidth = 1;
|
||||
[[UIColor darkGrayColor] set];
|
||||
[circle stroke];
|
||||
#endif
|
||||
|
||||
// get an image from the image context
|
||||
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
|
||||
// end the image context since we're not in a drawRect:
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return roundedImage;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSString (Additions)
|
||||
|
||||
// Returns a user-visible date time string that corresponds to the
|
||||
// specified RFC 3339 date time string. Note that this does not handle
|
||||
// all possible RFC 3339 date time strings, just one of the most common
|
||||
// styles.
|
||||
+ (NSDate *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString
|
||||
{
|
||||
NSDateFormatter * rfc3339DateFormatter;
|
||||
NSLocale * enUSPOSIXLocale;
|
||||
|
||||
// Convert the RFC 3339 date time string to an NSDate.
|
||||
|
||||
rfc3339DateFormatter = [[NSDateFormatter alloc] init];
|
||||
|
||||
enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||
|
||||
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
|
||||
[rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ'"];
|
||||
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
|
||||
|
||||
return [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
|
||||
}
|
||||
|
||||
+ (NSString *)elapsedTimeStringSinceDate:(NSString *)uploadDateString
|
||||
{
|
||||
// early return if no post date string
|
||||
if (!uploadDateString)
|
||||
{
|
||||
return @"NO POST DATE";
|
||||
}
|
||||
|
||||
NSDate *postDate = [self userVisibleDateTimeStringForRFC3339DateTimeString:uploadDateString];
|
||||
|
||||
if (!postDate) {
|
||||
return @"DATE CONVERSION ERROR";
|
||||
}
|
||||
|
||||
NSDate *currentDate = [NSDate date];
|
||||
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
|
||||
NSUInteger seconds = [[calendar components:NSCalendarUnitSecond fromDate:postDate toDate:currentDate options:0] second];
|
||||
NSUInteger minutes = [[calendar components:NSCalendarUnitMinute fromDate:postDate toDate:currentDate options:0] minute];
|
||||
NSUInteger hours = [[calendar components:NSCalendarUnitHour fromDate:postDate toDate:currentDate options:0] hour];
|
||||
NSUInteger days = [[calendar components:NSCalendarUnitDay fromDate:postDate toDate:currentDate options:0] day];
|
||||
|
||||
NSString *elapsedTime;
|
||||
|
||||
if (days > 7) {
|
||||
elapsedTime = [NSString stringWithFormat:@"%luw", (long)ceil(days/7.0)];
|
||||
} else if (days > 0) {
|
||||
elapsedTime = [NSString stringWithFormat:@"%lud", (long)days];
|
||||
} else if (hours > 0) {
|
||||
elapsedTime = [NSString stringWithFormat:@"%luh", (long)hours];
|
||||
} else if (minutes > 0) {
|
||||
elapsedTime = [NSString stringWithFormat:@"%lum", (long)minutes];
|
||||
} else if (seconds > 0) {
|
||||
elapsedTime = [NSString stringWithFormat:@"%lus", (long)seconds];
|
||||
} else if (seconds == 0) {
|
||||
elapsedTime = @"1s";
|
||||
} else {
|
||||
elapsedTime = @"ERROR";
|
||||
}
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSAttributedString (Additions)
|
||||
|
||||
+ (NSAttributedString *)attributedStringWithString:(NSString *)string fontSize:(CGFloat)size
|
||||
color:(nullable UIColor *)color firstWordColor:(nullable UIColor *)firstWordColor
|
||||
{
|
||||
NSDictionary *attributes = @{NSForegroundColorAttributeName: color ? : [UIColor blackColor],
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:size]};
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:string];
|
||||
[attributedString addAttributes:attributes range:NSMakeRange(0, string.length)];
|
||||
|
||||
if (firstWordColor) {
|
||||
NSRange firstSpaceRange = [string rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
NSRange firstWordRange = NSMakeRange(0, firstSpaceRange.location);
|
||||
[attributedString addAttribute:NSForegroundColorAttributeName value:firstWordColor range:firstWordRange];
|
||||
}
|
||||
|
||||
return attributedString;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -62,4 +62,11 @@
|
||||
[self.view setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)toggleVizualization:(BOOL)toggle
|
||||
{
|
||||
NSLog(@"shouldVisualizeLayoutSpecs:%d", toggle);
|
||||
[self.node shouldVisualizeLayoutSpecs:toggle];
|
||||
[self.view setNeedsLayout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
BIN
examples/ASLayoutSpecPlayground/Sample/resizeHandle.png
Normal file
BIN
examples/ASLayoutSpecPlayground/Sample/resizeHandle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
Reference in New Issue
Block a user