[ASLayoutSpec] Initial commit to support visualizing layout specs (with Playground app). (#2554)

* Initial ASLayoutSpecPlayground commit

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

* Halfway through moving debug features out of ASDK framework files and into debug files. Project builds.

* [ASLayoutSpecPlayground] Created new Inspector node, cleaning up internal implementation to start formalizing support for layout spec visualization.

* Workaround for ensuring creation of visualizerNode for ALL layoutspecs

* continued development

* Layout Inspector Work in Progress

* Resizing the playground works in the shrink direction, not for grow.

* added new ASLayoutableInspectorNode features

* Cleaned up examples code.

* Cleaning up  code.

* more code cleanup

* [ASLayoutableInspector] Transition to an ASTableNode-based architecture to support larger numbers of buttons / customizable types.

* [ASLayoutableInspector] Support different layoutable property types to set up buttons that can edit all of them.

* Huy debugging

* Refactored layout inspector code for extensibility.

* Properly lock layoutableContextMap

* Fix context handling in ASDisplayNode:measureWithSizeRange

* Fix ASLayoutSpecPlayground:ViewController:toggleVisualization

* added slider to InspectorCell

* [ASLayoutSpecPlayground] Improvements to propagation of visualize mode, resize handle, minor cleanup.

* Fix to ASEnvironment

* [ASLayoutSpecPlayground] Fix a few minor issues from the merge with latest master.

* Implement layout spec cache

* add pager ndoe

* add more examples

* add more layout examples

* [ASLayoutPlayground] Fix merge issues

* [ASLayoutPlayground] Fix up the example project from the 2.0 API changes.

* [ASLayoutPlayground] Some fixes (#2411)

* [ASLayoutPlayground]: Some fixes
* Fixed crash when tapping descender.
* Fixed setting the item to inspect.
* Fixed button states in inspector node.
* Added sliders for spacingBefore, spacingAfter, ascender.

* [ASLayoutSpecPlayground] Deselect the buttons when editing is over.

* [ASLayoutSpecPlayground] Changed flexGrow/Shrink's values from YES/NO to 1.0/0.0

* [Project] Create new Debug/ directory for advanced tools dedicated to debugging.

* [LayoutSpecPlayground] Rename project without AS in title, to be consistent with LayoutSpecExamples.

* [Bulid] Fix Xcode project to use new Debug subdirectory / group.

* [Bulid] Fix a small merge error.

* [Build] Fix build issue for Framework target.

* [Bulid] Fix podspec to expose InspectorNode header; Remove old-cocoapods emojis from ASDKgram :)

* Move aside ASLayoutSpecPlayground-Swift to match master

* [LayoutSpecPlayground] Cleanup implementation in several files, xcodeproj, etc.

* [ASControlNode] Add comment for new assertion, to be enabled in a separate diff.
This commit is contained in:
appleguy
2016-11-08 20:16:16 -08:00
committed by GitHub
parent a1156e108f
commit 55b5dff80c
42 changed files with 3526 additions and 18 deletions

View File

@@ -25,6 +25,7 @@ Pod::Spec.new do |spec|
'AsyncDisplayKit/Details/**/*.h',
'AsyncDisplayKit/Layout/*.h',
'Base/*.h',
'AsyncDisplayKit/Debug/ASLayoutElementInspectorNode.h',
'AsyncDisplayKit/TextKit/ASTextNodeTypes.h',
'AsyncDisplayKit/TextKit/ASTextKitComponents.h'
]

View File

@@ -472,14 +472,24 @@
DBDB83971C6E879900D0098C /* ASPagerFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = DBDB83931C6E879900D0098C /* ASPagerFlowLayout.m */; };
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE0702FC1C3671E900D7DE62 /* libAsyncDisplayKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09AC195D04C000B7D73C /* libAsyncDisplayKit.a */; };
DE4843DB1C93EAB100A1F33B /* ASLayoutTransition.mm in Sources */ = {isa = PBXBuildFile; fileRef = E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */; };
DE4843DC1C93EAC100A1F33B /* ASLayoutTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */; };
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DE84918D1C8FFF2B003D89E9 /* ASRunLoopQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */; };
DE89C1701DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.h in Headers */ = {isa = PBXBuildFile; fileRef = DE89C16A1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.h */; };
DE89C1711DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DE89C16B1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m */; };
DE89C1731DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m in Sources */ = {isa = PBXBuildFile; fileRef = DE89C16B1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m */; };
DE89C1741DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DE89C16C1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE89C1751DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m in Sources */ = {isa = PBXBuildFile; fileRef = DE89C16D1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m */; };
DE89C1771DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m in Sources */ = {isa = PBXBuildFile; fileRef = DE89C16D1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m */; };
DE89C1781DCEB9CC00D49D74 /* ASLayoutSpec+Debug.h in Headers */ = {isa = PBXBuildFile; fileRef = DE89C16E1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.h */; };
DE89C1791DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m in Sources */ = {isa = PBXBuildFile; fileRef = DE89C16F1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m */; };
DE89C17B1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m in Sources */ = {isa = PBXBuildFile; fileRef = DE89C16F1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m */; };
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; };
DE8BEAC31C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */; };
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */; };
DEB8ED7C1DD003D300DBDE55 /* ASLayoutTransition.mm in Sources */ = {isa = PBXBuildFile; fileRef = E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */; };
DEB8ED7E1DD007F400DBDE55 /* ASLayoutElementInspectorNode.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DE89C16C1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.h */; };
DEC146B71C37A16A004A0EE7 /* ASCollectionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = DEC146B41C37A16A004A0EE7 /* ASCollectionInternal.h */; };
DEC146B81C37A16A004A0EE7 /* ASCollectionInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC146B51C37A16A004A0EE7 /* ASCollectionInternal.m */; };
DEC146B91C37A16A004A0EE7 /* ASCollectionInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC146B51C37A16A004A0EE7 /* ASCollectionInternal.m */; };
@@ -662,6 +672,7 @@
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
DEB8ED7E1DD007F400DBDE55 /* ASLayoutElementInspectorNode.h in CopyFiles */,
69127CFE1DD2B387004BF6E2 /* ASEventLog.h in CopyFiles */,
693117CE1DC7C72700DE4784 /* ASDisplayNode+Deprecated.h in CopyFiles */,
69F381A51DA4630D00CF2278 /* NSArray+Diffing.h in CopyFiles */,
@@ -1016,8 +1027,8 @@
69F10C851C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASRangeControllerUpdateRangeProtocol+Beta.h"; sourceTree = "<group>"; };
69FEE53C1D95A9AF0086F066 /* ASLayoutElementStyleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutElementStyleTests.m; sourceTree = "<group>"; };
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = "<group>"; };
764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit+Debug.h"; sourceTree = "<group>"; };
764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AsyncDisplayKit+Debug.m"; sourceTree = "<group>"; };
764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "AsyncDisplayKit+Debug.h"; path = "../AsyncDisplayKit+Debug.h"; sourceTree = "<group>"; };
764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "AsyncDisplayKit+Debug.m"; path = "../AsyncDisplayKit+Debug.m"; sourceTree = "<group>"; };
7A06A7381C35F08800FE8DAA /* ASRelativeLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRelativeLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm; sourceTree = "<group>"; };
7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRelativeLayoutSpec.h; path = AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h; sourceTree = "<group>"; };
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
@@ -1166,6 +1177,12 @@
DBDB83921C6E879900D0098C /* ASPagerFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPagerFlowLayout.h; sourceTree = "<group>"; };
DBDB83931C6E879900D0098C /* ASPagerFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPagerFlowLayout.m; sourceTree = "<group>"; };
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+FrameworkPrivate.h"; sourceTree = "<group>"; };
DE89C16A1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementInspectorCell.h; sourceTree = "<group>"; };
DE89C16B1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutElementInspectorCell.m; sourceTree = "<group>"; };
DE89C16C1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementInspectorNode.h; sourceTree = "<group>"; };
DE89C16D1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutElementInspectorNode.m; sourceTree = "<group>"; };
DE89C16E1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASLayoutSpec+Debug.h"; sourceTree = "<group>"; };
DE89C16F1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASLayoutSpec+Debug.m"; sourceTree = "<group>"; };
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDelegateProxy.h; sourceTree = "<group>"; };
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDelegateProxy.m; sourceTree = "<group>"; };
DEC146B41C37A16A004A0EE7 /* ASCollectionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASCollectionInternal.h; path = Details/ASCollectionInternal.h; sourceTree = "<group>"; };
@@ -1360,12 +1377,11 @@
ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */,
9CFFC6BF1CCAC73C006A6476 /* ASViewController.mm */,
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */,
764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */,
764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */,
DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */,
68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */,
68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */,
92074A5E1CC8B9DD00918F75 /* tvOS */,
DE89C1691DCEB9CC00D49D74 /* Debug */,
058D09E1195D050800B7D73C /* Details */,
058D0A01195D050800B7D73C /* Private */,
AC6456051B0A333200CF11B8 /* Layout */,
@@ -1757,6 +1773,21 @@
name = "Supporting Files";
sourceTree = "<group>";
};
DE89C1691DCEB9CC00D49D74 /* Debug */ = {
isa = PBXGroup;
children = (
DE89C16A1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.h */,
DE89C16B1DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m */,
DE89C16C1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.h */,
DE89C16D1DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m */,
DE89C16E1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.h */,
DE89C16F1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m */,
764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */,
764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */,
);
path = Debug;
sourceTree = "<group>";
};
FD40E2760492F0CAAEAD552D /* Pods */ = {
isa = PBXGroup;
children = (
@@ -1788,6 +1819,7 @@
68355B411CB57A6C001D4E68 /* ASImageContainerProtocolCategories.h in Headers */,
7630FFA81C9E267E007A7C0E /* ASVideoNode.h in Headers */,
B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */,
DE89C1741DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.h in Headers */,
B13CA1011C52004900E031AB /* ASCollectionNode+Beta.h in Headers */,
254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */,
B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */,
@@ -1898,6 +1930,7 @@
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */,
34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */,
254C6B741BF94DF4003EC431 /* ASTextNodeWordKerner.h in Headers */,
DE89C1781DCEB9CC00D49D74 /* ASLayoutSpec+Debug.h in Headers */,
DB55C2671C641AE4004EDCF5 /* ASContextTransitioning.h in Headers */,
68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */,
CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */,
@@ -1925,6 +1958,7 @@
044284FE1BAA387800D16268 /* ASStackLayoutSpecUtilities.h in Headers */,
34EFC7751B701D2400AD841F /* ASStackPositionedLayout.h in Headers */,
69E1006E1CA89CB600D88C1B /* ASEnvironmentInternal.h in Headers */,
DE89C1701DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.h in Headers */,
34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */,
9C6BB3B31B8CC9C200F13F52 /* ASAbsoluteLayoutElement.h in Headers */,
34EFC7731B701D0700AD841F /* ASAbsoluteLayoutSpec.h in Headers */,
@@ -2152,6 +2186,7 @@
buildActionMask = 2147483647;
files = (
058D0A22195D050800B7D73C /* _ASAsyncTransaction.mm in Sources */,
E52405B31C8FEF03004DC8E7 /* ASLayoutTransition.mm in Sources */,
8B0768B41CE752EC002E1453 /* ASDefaultPlaybackButton.m in Sources */,
E55D86321CA8A14000A0C26F /* ASLayoutElement.mm in Sources */,
68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
@@ -2185,6 +2220,7 @@
DBC452DC1C5BF64600B16017 /* NSArray+Diffing.m in Sources */,
AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */,
9CFFC6C21CCAC768006A6476 /* ASTableNode.mm in Sources */,
DE89C1751DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m in Sources */,
205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */,
68FC85EB1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */,
058D0A13195D050800B7D73C /* ASControlNode.mm in Sources */,
@@ -2205,6 +2241,7 @@
0587F9BE1A7309ED00AFF0BA /* ASEditableTextNode.mm in Sources */,
464052231A3F83C40061C0BA /* ASFlowLayoutController.mm in Sources */,
257754C41BEE458E00737CA5 /* ASTextNodeWordKerner.m in Sources */,
DE89C1791DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m in Sources */,
058D0A1A195D050800B7D73C /* ASHighlightOverlayLayer.mm in Sources */,
058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */,
CC3B208B1C3F7A5400798563 /* ASWeakSet.m in Sources */,
@@ -2231,9 +2268,9 @@
0442850F1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */,
7A06A73A1C35F08800FE8DAA /* ASRelativeLayoutSpec.mm in Sources */,
6907C2591DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m in Sources */,
E52405B31C8FEF03004DC8E7 /* ASLayoutTransition.mm in Sources */,
69CB62AD1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */,
257754AB1BEE44CD00737CA5 /* ASTextKitEntityAttribute.m in Sources */,
DE89C1711DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m in Sources */,
055F1A3919ABD413004DAFF1 /* ASRangeController.mm in Sources */,
044285091BAA63FE00D16268 /* ASBatchFetching.m in Sources */,
257754AE1BEE44CD00737CA5 /* ASTextKitRenderer+Positioning.mm in Sources */,
@@ -2339,10 +2376,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DEB8ED7C1DD003D300DBDE55 /* ASLayoutTransition.mm in Sources */,
9F98C0261DBE29E000476D92 /* ASControlTargetAction.m in Sources */,
9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */,
8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */,
DE4843DB1C93EAB100A1F33B /* ASLayoutTransition.mm in Sources */,
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */,
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */,
@@ -2404,6 +2441,7 @@
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */,
92074A641CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */,
DE89C17B1DCEB9CC00D49D74 /* ASLayoutSpec+Debug.m in Sources */,
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */,
68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */,
B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */,
@@ -2424,6 +2462,7 @@
B35062071B010EFD0018CF92 /* ASNetworkImageNode.mm in Sources */,
34EFC76D1B701CF100AD841F /* ASOverlayLayoutSpec.mm in Sources */,
044285101BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */,
DE89C1731DCEB9CC00D49D74 /* ASLayoutElementInspectorCell.m in Sources */,
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */,
0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */,
68FC85E61CE29B9400EDD713 /* ASNavigationController.m in Sources */,
@@ -2459,6 +2498,7 @@
697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */,
B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */,
044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */,
DE89C1771DCEB9CC00D49D74 /* ASLayoutElementInspectorNode.m in Sources */,
254C6B8A1BF94F8A003EC431 /* ASTextKitRenderer+TextChecking.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -10,6 +10,8 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
#pragma once
NS_ASSUME_NONNULL_BEGIN
/**

View File

@@ -14,6 +14,8 @@
#import "AsyncDisplayKit+Debug.h"
#import "ASInternalHelpers.h"
#import "ASControlTargetAction.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASLayoutElementInspectorNode.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
@@ -241,6 +243,9 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
{
NSParameterAssert(action);
NSParameterAssert(controlEventMask != 0);
// This assertion would likely be helpful to users who aren't familiar with the implications of layer-backing.
// However, it would represent an API change (in debug) as it did not used to assert.
// ASDisplayNodeAssert(!self.isLayerBacked, @"ASControlNode is layer backed, will never be able to call target in target:action: pair.");
ASDN::MutexLocker l(_controlLock);
@@ -448,4 +453,21 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
return _debugHighlightOverlay;
}
// methods for visualizing ASLayoutSpecs
- (void)setHierarchyState:(ASHierarchyState)hierarchyState
{
[super setHierarchyState:hierarchyState];
if (self.shouldVisualizeLayoutSpecs) {
[self addTarget:self action:@selector(inspectElement) forControlEvents:ASControlNodeEventTouchUpInside];
} else {
[self removeTarget:self action:@selector(inspectElement) forControlEvents:ASControlNodeEventTouchUpInside];
}
}
- (void)inspectElement
{
[ASLayoutElementInspectorNode sharedInstance].layoutElementToEdit = self;
}
@end

View File

@@ -8,6 +8,8 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#pragma once
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/_ASAsyncTransactionContainer.h>

View File

@@ -12,6 +12,7 @@
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASDisplayNode+Beta.h"
#import "AsyncDisplayKit+Debug.h"
#import <objc/runtime.h>
@@ -49,6 +50,10 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
@protocol CALayerDelegate;
@interface ASDisplayNode () <UIGestureRecognizerDelegate, _ASDisplayLayerDelegate, _ASTransitionContextCompletionDelegate>
{
BOOL _shouldCacheLayoutSpec;
ASLayoutSpec *_layoutSpec;
}
/**
*
@@ -717,6 +722,23 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self cancelLayoutTransition];
BOOL didCreateNewContext = NO;
BOOL didOverrideExistingContext = NO;
BOOL shouldVisualizeLayout = ASHierarchyStateIncludesVisualizeLayout(_hierarchyState);
ASLayoutElementContext context;
if (ASLayoutElementContextIsNull(ASLayoutElementGetCurrentContext())) {
context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID, shouldVisualizeLayout);
ASLayoutElementSetCurrentContext(context);
didCreateNewContext = YES;
} else {
context = ASLayoutElementGetCurrentContext();
if (context.needsVisualizeNode != shouldVisualizeLayout) {
context.needsVisualizeNode = shouldVisualizeLayout;
ASLayoutElementSetCurrentContext(context);
didOverrideExistingContext = YES;
}
}
// Prepare for layout transition
auto previousLayout = _calculatedDisplayNodeLayout;
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
@@ -724,6 +746,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
constrainedSize,
parentSize
);
if (didCreateNewContext) {
ASLayoutElementClearCurrentContext();
} else if (didOverrideExistingContext) {
context.needsVisualizeNode = !context.needsVisualizeNode;
ASLayoutElementSetCurrentContext(context);
}
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
pendingLayout:pendingLayout
previousLayout:previousLayout];
@@ -831,9 +861,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASLayout *newLayout;
{
ASLayoutElementSetCurrentContext(ASLayoutElementContextMake(transitionID, NO));
ASDN::MutexLocker l(__instanceLock__);
BOOL shouldVisualizeLayout = ASHierarchyStateIncludesVisualizeLayout(_hierarchyState);
ASLayoutElementSetCurrentContext(ASLayoutElementContextMake(transitionID, shouldVisualizeLayout));
BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO);
self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x
newLayout = [self calculateLayoutThatFits:constrainedSize
@@ -2438,7 +2470,12 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
layoutSpec = [[ASLayoutSpec alloc] init];
}
if (_shouldCacheLayoutSpec) {
_layoutSpec = layoutSpec;
} else {
ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec);
}
layoutSpec.parent = self;
layoutSpec.isMutable = NO;
}
@@ -2486,7 +2523,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
__ASDisplayNodeCheckForLayoutMethodOverrides;
BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;
if (_layoutSpecBlock != NULL) {
if (_shouldCacheLayoutSpec && _layoutSpec != nil) {
return _layoutSpec;
} else if (_layoutSpecBlock != NULL) {
return ({
ASDN::MutexLocker l(__instanceLock__);
ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
@@ -3700,4 +3740,42 @@ ASLayoutElementStyleForwarding
self.automaticallyManagesSubnodes = enabled;
}
#pragma mark - ASDisplayNode(LayoutDebugging)
- (void)setShouldVisualizeLayoutSpecs:(BOOL)shouldVisualizeLayoutSpecs
{
ASDN::MutexLocker l(__instanceLock__);
if (shouldVisualizeLayoutSpecs != [self shouldVisualizeLayoutSpecs]) {
if (shouldVisualizeLayoutSpecs) {
[self enterHierarchyState:ASHierarchyStateVisualizeLayout];
} else {
[self exitHierarchyState:ASHierarchyStateVisualizeLayout];
}
[self setNeedsLayout];
}
}
- (BOOL)shouldVisualizeLayoutSpecs
{
ASDN::MutexLocker l(__instanceLock__);
return ASHierarchyStateIncludesVisualizeLayout(_hierarchyState);
}
- (void)setShouldCacheLayoutSpec:(BOOL)shouldCacheLayoutSpec
{
ASDN::MutexLocker l(__instanceLock__);
if (_shouldCacheLayoutSpec != shouldCacheLayoutSpec) {
_shouldCacheLayoutSpec = shouldCacheLayoutSpec;
if (_shouldCacheLayoutSpec == NO) {
_layoutSpec = nil;
}
}
}
- (BOOL)shouldCacheLayoutSpec
{
ASDN::MutexLocker l(__instanceLock__);
return _shouldCacheLayoutSpec;
}
@end

View File

@@ -10,12 +10,19 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import "ASDisplayNode.h"
#import "ASControlNode.h"
#import "ASImageNode.h"
#import "ASRangeController.h"
NS_ASSUME_NONNULL_BEGIN
@interface ASDisplayNode (Visualization)
@property (nonatomic, assign) BOOL shouldVisualizeLayoutSpecs;
@property (nonatomic, assign) BOOL shouldCacheLayoutSpec;
@end
@interface ASImageNode (Debugging)
/**

View File

@@ -0,0 +1,28 @@
//
// ASLayoutElementInspectorCell.h
// AsyncDisplayKit
//
// Created by Hannah Troisi on 3/27/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/ASCellNode.h>
typedef NS_ENUM(NSInteger, ASLayoutElementPropertyType) {
ASLayoutElementPropertyFlexGrow = 0,
ASLayoutElementPropertyFlexShrink,
ASLayoutElementPropertyAlignSelf,
ASLayoutElementPropertyFlexBasis,
ASLayoutElementPropertySpacingBefore,
ASLayoutElementPropertySpacingAfter,
ASLayoutElementPropertyAscender,
ASLayoutElementPropertyDescender,
ASLayoutElementPropertyCount
};
@interface ASLayoutElementInspectorCell : ASCellNode
- (instancetype)initWithProperty:(ASLayoutElementPropertyType)property layoutElementToEdit:(id<ASLayoutElement>)layoutable NS_DESIGNATED_INITIALIZER;
@end

View File

@@ -0,0 +1,569 @@
//
// ASLayoutElementInspectorCell.m
// AsyncDisplayKit
//
// Created by Hannah Troisi on 3/27/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "ASLayoutElementInspectorCell.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
typedef NS_ENUM(NSInteger, CellDataType) {
CellDataTypeBool,
CellDataTypeFloat,
};
__weak static ASLayoutElementInspectorCell *__currentlyOpenedCell = nil;
@protocol InspectorCellEditingBubbleProtocol <NSObject>
- (void)valueChangedToIndex:(NSUInteger)index;
@end
@interface ASLayoutElementInspectorCellEditingBubble : ASDisplayNode
@property (nonatomic, strong, readwrite) id<InspectorCellEditingBubbleProtocol> delegate;
- (instancetype)initWithEnumOptions:(BOOL)yes enumStrings:(NSArray<NSString *> *)options currentOptionIndex:(NSUInteger)currentOption;
- (instancetype)initWithSliderMinValue:(CGFloat)min maxValue:(CGFloat)max currentValue:(CGFloat)current
;@end
@interface ASLayoutElementInspectorCell () <InspectorCellEditingBubbleProtocol>
@end
@implementation ASLayoutElementInspectorCell
{
ASLayoutElementPropertyType _propertyType;
CellDataType _dataType;
id<ASLayoutElement> _layoutElementToEdit;
ASButtonNode *_buttonNode;
ASTextNode *_textNode;
ASTextNode *_textNode2;
ASLayoutElementInspectorCellEditingBubble *_textBubble;
}
#pragma mark - Lifecycle
- (instancetype)initWithProperty:(ASLayoutElementPropertyType)property layoutElementToEdit:(id<ASLayoutElement>)layoutElement
{
self = [super init];
if (self) {
_propertyType = property;
_dataType = [ASLayoutElementInspectorCell dataTypeForProperty:property];
_layoutElementToEdit = layoutElement;
self.automaticallyManagesSubnodes = YES;
_buttonNode = [self makeBtnNodeWithTitle:[ASLayoutElementInspectorCell propertyStringForPropertyType:property]];
[_buttonNode addTarget:self action:@selector(buttonTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
_textNode = [[ASTextNode alloc] init];
_textNode.attributedText = [ASLayoutElementInspectorCell propertyValueAttributedStringForProperty:property withLayoutElement:layoutElement];
[self updateButtonStateForProperty:property withLayoutElement:layoutElement];
_textNode2 = [[ASTextNode alloc] init];
_textNode2.attributedText = [ASLayoutElementInspectorCell propertyValueDetailAttributedStringForProperty:property withLayoutElement:layoutElement];
}
return self;
}
- (void)updateButtonStateForProperty:(ASLayoutElementPropertyType)property withLayoutElement:(id<ASLayoutElement>)layoutElement
{
if (property == ASLayoutElementPropertyFlexGrow) {
_buttonNode.selected = layoutElement.style.flexGrow;
}
else if (property == ASLayoutElementPropertyFlexShrink) {
_buttonNode.selected = layoutElement.style.flexShrink;
}
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *horizontalSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
horizontalSpec.children = @[_buttonNode, _textNode];
horizontalSpec.style.flexGrow = 1.0;
horizontalSpec.alignItems = ASStackLayoutAlignItemsCenter;
horizontalSpec.justifyContent = ASStackLayoutJustifyContentSpaceBetween;
ASLayoutSpec *childSpec;
if (_textBubble) {
ASStackLayoutSpec *verticalSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalSpec.children = @[horizontalSpec, _textBubble];
verticalSpec.spacing = 8;
verticalSpec.style.flexGrow = 1.0;
_textBubble.style.flexGrow = 1.0;
childSpec = verticalSpec;
} else {
childSpec = horizontalSpec;
}
ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(2, 4, 2, 4) child:childSpec];
insetSpec.style.flexGrow =1.0;
return insetSpec;
}
+ (NSAttributedString *)propertyValueAttributedStringForProperty:(ASLayoutElementPropertyType)property withLayoutElement:(id<ASLayoutElement>)layoutElement
{
NSString *valueString;
switch (property) {
case ASLayoutElementPropertyFlexGrow:
valueString = layoutElement.style.flexGrow ? @"YES" : @"NO";
break;
case ASLayoutElementPropertyFlexShrink:
valueString = layoutElement.style.flexShrink ? @"YES" : @"NO";
break;
case ASLayoutElementPropertyAlignSelf:
valueString = [ASLayoutElementInspectorCell alignSelfEnumValueString:layoutElement.style.alignSelf];
break;
case ASLayoutElementPropertyFlexBasis:
if (layoutElement.style.flexBasis.unit && layoutElement.style.flexBasis.value) { // ENUM TYPE
valueString = [NSString stringWithFormat:@"%0.0f %@", layoutElement.style.flexBasis.value,
[ASLayoutElementInspectorCell ASRelativeDimensionEnumString:layoutElement.style.alignSelf]];
} else {
valueString = @"0 pts";
}
break;
case ASLayoutElementPropertySpacingBefore:
valueString = [NSString stringWithFormat:@"%0.0f", layoutElement.style.spacingBefore];
break;
case ASLayoutElementPropertySpacingAfter:
valueString = [NSString stringWithFormat:@"%0.0f", layoutElement.style.spacingAfter];
break;
case ASLayoutElementPropertyAscender:
valueString = [NSString stringWithFormat:@"%0.0f", layoutElement.style.ascender];
break;
case ASLayoutElementPropertyDescender:
valueString = [NSString stringWithFormat:@"%0.0f", layoutElement.style.descender];
break;
default:
valueString = @"?";
break;
}
return [ASLayoutElementInspectorCell attributedStringFromString:valueString];
}
+ (NSAttributedString *)propertyValueDetailAttributedStringForProperty:(ASLayoutElementPropertyType)property withLayoutElement:(id<ASLayoutElement>)layoutElement
{
NSString *valueString;
switch (property) {
case ASLayoutElementPropertyFlexGrow:
case ASLayoutElementPropertyFlexShrink:
case ASLayoutElementPropertyAlignSelf:
case ASLayoutElementPropertyFlexBasis:
case ASLayoutElementPropertySpacingBefore:
case ASLayoutElementPropertySpacingAfter:
case ASLayoutElementPropertyAscender:
case ASLayoutElementPropertyDescender:
default:
return nil;
}
return [ASLayoutElementInspectorCell attributedStringFromString:valueString];
}
- (void)endEditingValue
{
_textBubble = nil;
__currentlyOpenedCell = nil;
_buttonNode.selected = NO;
[self setNeedsLayout];
}
- (void)beginEditingValue
{
_textBubble.delegate = self;
__currentlyOpenedCell = self;
[self setNeedsLayout];
}
- (void)valueChangedToIndex:(NSUInteger)index
{
switch (_propertyType) {
case ASLayoutElementPropertyAlignSelf:
_layoutElementToEdit.style.alignSelf = index;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[ASLayoutElementInspectorCell alignSelfEnumValueString:index]];
break;
case ASLayoutElementPropertySpacingBefore:
_layoutElementToEdit.style.spacingBefore = (CGFloat)index;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.spacingBefore]];
break;
case ASLayoutElementPropertySpacingAfter:
_layoutElementToEdit.style.spacingAfter = (CGFloat)index;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.spacingAfter]];
break;
case ASLayoutElementPropertyAscender:
_layoutElementToEdit.style.ascender = (CGFloat)index;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.ascender]];
break;
case ASLayoutElementPropertyDescender:
_layoutElementToEdit.style.descender = (CGFloat)index;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.descender]];
break;
default:
break;
}
[self setNeedsLayout];
}
#pragma mark - gesture handling
- (void)buttonTapped:(ASButtonNode *)sender
{
BOOL selfIsEditing = (self == __currentlyOpenedCell);
[__currentlyOpenedCell endEditingValue];
if (selfIsEditing) {
sender.selected = NO;
return;
}
// NSUInteger currentAlignSelfValue;
// NSUInteger nextAlignSelfValue;
// CGFloat newValue;
sender.selected = !sender.selected;
switch (_propertyType) {
case ASLayoutElementPropertyFlexGrow:
_layoutElementToEdit.style.flexGrow = sender.isSelected ? 1.0 : 0.0;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:sender.selected ? @"YES" : @"NO"];
break;
case ASLayoutElementPropertyFlexShrink:
_layoutElementToEdit.style.flexShrink = sender.isSelected ? 1.0 : 0.0;
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:sender.selected ? @"YES" : @"NO"];
break;
case ASLayoutElementPropertyAlignSelf:
_textBubble = [[ASLayoutElementInspectorCellEditingBubble alloc] initWithEnumOptions:YES
enumStrings:[ASLayoutElementInspectorCell alignSelfEnumStringArray]
currentOptionIndex:_layoutElementToEdit.style.alignSelf];
[self beginEditingValue];
// if ([self layoutSpec]) {
// currentAlignSelfValue = [[self layoutSpec] alignSelf];
// nextAlignSelfValue = (currentAlignSelfValue + 1 <= ASStackLayoutAlignSelfStretch) ? currentAlignSelfValue + 1 : 0;
// [[self layoutSpec] setAlignSelf:nextAlignSelfValue];
//
// } else if ([self node]) {
// currentAlignSelfValue = [[self node] alignSelf];
// nextAlignSelfValue = (currentAlignSelfValue + 1 <= ASStackLayoutAlignSelfStretch) ? currentAlignSelfValue + 1 : 0;
// [[self node] setAlignSelf:nextAlignSelfValue];
// }
break;
case ASLayoutElementPropertySpacingBefore:
_textBubble = [[ASLayoutElementInspectorCellEditingBubble alloc] initWithSliderMinValue:0 maxValue:100 currentValue:_layoutElementToEdit.style.spacingBefore];
[self beginEditingValue];
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.spacingBefore]];
break;
case ASLayoutElementPropertySpacingAfter:
_textBubble = [[ASLayoutElementInspectorCellEditingBubble alloc] initWithSliderMinValue:0 maxValue:100 currentValue:_layoutElementToEdit.style.spacingAfter];
[self beginEditingValue];
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.spacingAfter]];
break;
case ASLayoutElementPropertyAscender:
_textBubble = [[ASLayoutElementInspectorCellEditingBubble alloc] initWithSliderMinValue:0 maxValue:100 currentValue:_layoutElementToEdit.style.ascender];
[self beginEditingValue];
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.ascender]];
break;
case ASLayoutElementPropertyDescender:
_textBubble = [[ASLayoutElementInspectorCellEditingBubble alloc] initWithSliderMinValue:0 maxValue:100 currentValue:_layoutElementToEdit.style.descender];
[self beginEditingValue];
_textNode.attributedText = [ASLayoutElementInspectorCell attributedStringFromString:[NSString stringWithFormat:@"%0.0f", _layoutElementToEdit.style.descender]];
break;
default:
break;
}
[self setNeedsLayout];
}
#pragma mark - cast layoutElementToEdit
- (ASDisplayNode *)node
{
if (_layoutElementToEdit.layoutElementType == ASLayoutElementTypeDisplayNode) {
return (ASDisplayNode *)_layoutElementToEdit;
}
return nil;
}
- (ASLayoutSpec *)layoutSpec
{
if (_layoutElementToEdit.layoutElementType == ASLayoutElementTypeLayoutSpec) {
return (ASLayoutSpec *)_layoutElementToEdit;
}
return nil;
}
#pragma mark - data / property type helper methods
+ (CellDataType)dataTypeForProperty:(ASLayoutElementPropertyType)property
{
switch (property) {
case ASLayoutElementPropertyFlexGrow:
case ASLayoutElementPropertyFlexShrink:
return CellDataTypeBool;
case ASLayoutElementPropertySpacingBefore:
case ASLayoutElementPropertySpacingAfter:
case ASLayoutElementPropertyAscender:
case ASLayoutElementPropertyDescender:
return CellDataTypeFloat;
default:
break;
}
return CellDataTypeBool;
}
+ (NSString *)propertyStringForPropertyType:(ASLayoutElementPropertyType)property
{
NSString *string;
switch (property) {
case ASLayoutElementPropertyFlexGrow:
string = @"FlexGrow";
break;
case ASLayoutElementPropertyFlexShrink:
string = @"FlexShrink";
break;
case ASLayoutElementPropertyAlignSelf:
string = @"AlignSelf";
break;
case ASLayoutElementPropertyFlexBasis:
string = @"FlexBasis";
break;
case ASLayoutElementPropertySpacingBefore:
string = @"SpacingBefore";
break;
case ASLayoutElementPropertySpacingAfter:
string = @"SpacingAfter";
break;
case ASLayoutElementPropertyAscender:
string = @"Ascender";
break;
case ASLayoutElementPropertyDescender:
string = @"Descender";
break;
default:
string = @"Unknown";
break;
}
return string;
}
+ (NSDictionary *)alignSelfTypeNames
{
return @{@(ASStackLayoutAlignSelfAuto) : @"Auto",
@(ASStackLayoutAlignSelfStart) : @"Start",
@(ASStackLayoutAlignSelfEnd) : @"End",
@(ASStackLayoutAlignSelfCenter) : @"Center",
@(ASStackLayoutAlignSelfStretch) : @"Stretch"};
}
+ (NSString *)alignSelfEnumValueString:(NSUInteger)type
{
return [[self class] alignSelfTypeNames][@(type)];
}
+ (NSArray <NSString *> *)alignSelfEnumStringArray
{
return @[@"ASStackLayoutAlignSelfAuto",
@"ASStackLayoutAlignSelfStart",
@"ASStackLayoutAlignSelfEnd",
@"ASStackLayoutAlignSelfCenter",
@"ASStackLayoutAlignSelfStretch"];
}
+ (NSDictionary *)ASRelativeDimensionTypeNames
{
return @{@(ASDimensionUnitPoints) : @"pts",
@(ASDimensionUnitFraction) : @"%"};
}
+ (NSString *)ASRelativeDimensionEnumString:(NSUInteger)type
{
return [[self class] ASRelativeDimensionTypeNames][@(type)];
}
#pragma mark - formatting helper methods
+ (NSAttributedString *)attributedStringFromString:(NSString *)string
{
return [ASLayoutElementInspectorCell attributedStringFromString:string withTextColor:[UIColor whiteColor]];
}
+ (NSAttributedString *)attributedStringFromString:(NSString *)string withTextColor:(nullable UIColor *)color
{
NSDictionary *attributes = @{NSForegroundColorAttributeName : color,
NSFontAttributeName : [UIFont fontWithName:@"Menlo-Regular" size:12]};
return [[NSAttributedString alloc] initWithString:string attributes:attributes];
}
- (ASButtonNode *)makeBtnNodeWithTitle:(NSString *)title
{
UIColor *orangeColor = [UIColor colorWithRed:255/255.0 green:181/255.0 blue:68/255.0 alpha:1];
UIImage *orangeStretchBtnImg = [ASLayoutElementInspectorCell imageForButtonWithBackgroundColor:orangeColor
borderColor:[UIColor whiteColor]
borderWidth:3];
UIImage *greyStretchBtnImg = [ASLayoutElementInspectorCell imageForButtonWithBackgroundColor:[UIColor darkGrayColor]
borderColor:[UIColor lightGrayColor]
borderWidth:3];
UIImage *clearStretchBtnImg = [ASLayoutElementInspectorCell imageForButtonWithBackgroundColor:[UIColor clearColor]
borderColor:[UIColor whiteColor]
borderWidth:3];
ASButtonNode *btn = [[ASButtonNode alloc] init];
btn.contentEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5);
[btn setAttributedTitle:[ASLayoutElementInspectorCell attributedStringFromString:title] forState:ASControlStateNormal];
[btn setAttributedTitle:[ASLayoutElementInspectorCell attributedStringFromString:title withTextColor:[UIColor lightGrayColor]] forState:ASControlStateDisabled];
[btn setBackgroundImage:clearStretchBtnImg forState:ASControlStateNormal];
[btn setBackgroundImage:orangeStretchBtnImg forState:ASControlStateSelected];
[btn setBackgroundImage:greyStretchBtnImg forState:ASControlStateDisabled];
return btn;
}
#define CORNER_RADIUS 3
+ (UIImage *)imageForButtonWithBackgroundColor:(UIColor *)backgroundColor borderColor:(UIColor *)borderColor borderWidth:(CGFloat)width
{
CGSize unstretchedSize = CGSizeMake(2 * CORNER_RADIUS + 1, 2 * CORNER_RADIUS + 1);
CGRect rect = (CGRect) {CGPointZero, unstretchedSize};
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:CORNER_RADIUS];
// create a graphics context for the following status button
UIGraphicsBeginImageContextWithOptions(unstretchedSize, NO, 0);
[path addClip];
[backgroundColor setFill];
[path fill];
path.lineWidth = width;
[borderColor setStroke];
[path stroke];
UIImage *btnImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [btnImage stretchableImageWithLeftCapWidth:CORNER_RADIUS topCapHeight:CORNER_RADIUS];
}
@end
@implementation ASLayoutElementInspectorCellEditingBubble
{
NSMutableArray<ASButtonNode *> *_textNodes;
ASDisplayNode *_slider;
}
- (instancetype)initWithEnumOptions:(BOOL)yes enumStrings:(NSArray<NSString *> *)options currentOptionIndex:(NSUInteger)currentOption
{
self = [super init];
if (self) {
self.automaticallyManagesSubnodes = YES;
self.backgroundColor = [UIColor colorWithRed:255/255.0 green:181/255.0 blue:68/255.0 alpha:1];
_textNodes = [[NSMutableArray alloc] init];
int index = 0;
for (NSString *optionStr in options) {
ASButtonNode *btn = [[ASButtonNode alloc] init];
[btn setAttributedTitle:[ASLayoutElementInspectorCell attributedStringFromString:optionStr] forState:ASControlStateNormal];
[btn setAttributedTitle:[ASLayoutElementInspectorCell attributedStringFromString:optionStr withTextColor:[UIColor redColor]]
forState:ASControlStateSelected];
[btn addTarget:self action:@selector(enumOptionSelected:) forControlEvents:ASControlNodeEventTouchUpInside];
btn.selected = (index == currentOption) ? YES : NO;
[_textNodes addObject:btn];
index++;
}
}
return self;
}
- (instancetype)initWithSliderMinValue:(CGFloat)min maxValue:(CGFloat)max currentValue:(CGFloat)current
{
if (self = [super init]) {
self.userInteractionEnabled = YES;
self.automaticallyManagesSubnodes = YES;
self.backgroundColor = [UIColor colorWithRed:255/255.0 green:181/255.0 blue:68/255.0 alpha:1];
__weak id weakSelf = self;
_slider = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
UISlider *slider = [[UISlider alloc] init];
slider.minimumValue = min;
slider.maximumValue = max;
slider.value = current;
[slider addTarget:weakSelf action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
return slider;
}];
_slider.userInteractionEnabled = YES;
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_slider.style.preferredSize = CGSizeMake(constrainedSize.max.width, 25);
NSMutableArray *children = [[NSMutableArray alloc] init];
if (_textNodes) {
ASStackLayoutSpec *textStack = [ASStackLayoutSpec verticalStackLayoutSpec];
textStack.children = _textNodes;
textStack.spacing = 2;
[children addObject:textStack];
}
if (_slider) {
_slider.style.flexGrow = 1.0;
[children addObject:_slider];
}
ASStackLayoutSpec *verticalStackSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackSpec.children = children;
verticalStackSpec.spacing = 2;
verticalStackSpec.style.flexGrow = 1.0;
verticalStackSpec.style.alignSelf = ASStackLayoutAlignSelfStretch;
ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(8, 8, 8, 8) child:verticalStackSpec];
return insetSpec;
}
#pragma mark - gesture handling
- (void)enumOptionSelected:(ASButtonNode *)sender
{
sender.selected = !sender.selected;
for (ASButtonNode *node in _textNodes) {
if (node != sender) {
node.selected = NO;
}
}
[self.delegate valueChangedToIndex:[_textNodes indexOfObject:sender]];
[self setNeedsLayout];
}
- (void)sliderValueChanged:(UISlider *)sender
{
[self.delegate valueChangedToIndex:roundf(sender.value)];
}
@end

View File

@@ -0,0 +1,25 @@
//
// ASLayoutElementInspectorNode.h
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@protocol ASLayoutElementInspectorNodeDelegate <NSObject>
- (void)toggleVisualization:(BOOL)toggle;
@end
@interface ASLayoutElementInspectorNode : ASDisplayNode
@property (nonatomic, strong) id<ASLayoutElement> layoutElementToEdit;
@property (nonatomic, strong) id<ASLayoutElementInspectorNodeDelegate> delegate;
@property (nonatomic, assign) CGFloat vizNodeInsetSize;
+ (instancetype)sharedInstance;
@end

View File

@@ -0,0 +1,406 @@
//
// ASLayoutElementInspectorNode.m
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "ASLayoutElementInspectorNode.h"
#import "ASLayoutElementInspectorCell.h"
#import "ASDisplayNode+Beta.h"
#import "ASLayoutSpec+Debug.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface ASLayoutElementInspectorNode () <ASTableDelegate, ASTableDataSource>
@end
@implementation ASLayoutElementInspectorNode
{
ASTableNode *_tableNode;
}
#pragma mark - class methods
+ (instancetype)sharedInstance
{
static ASLayoutElementInspectorNode *__inspector = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__inspector = [[ASLayoutElementInspectorNode alloc] init];
});
return __inspector;
}
#pragma mark - lifecycle
- (instancetype)init
{
self = [super init];
if (self) {
_tableNode = [[ASTableNode alloc] init];
_tableNode.delegate = self;
_tableNode.dataSource = self;
[self addSubnode:_tableNode]; // required because of manual layout
}
return self;
}
- (void)didLoad
{
[super didLoad];
_tableNode.view.backgroundColor = [UIColor colorWithRed:40/255.0 green:43/255.0 blue:53/255.0 alpha:1];
_tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableNode.view.allowsSelection = NO;
_tableNode.view.sectionHeaderHeight = 40;
}
- (void)layout
{
[super layout];
_tableNode.frame = self.bounds;
}
#pragma mark - intstance methods
- (void)setLayoutElementToEdit:(id<ASLayoutElement>)layoutElementToEdit
{
if (_layoutElementToEdit != layoutElementToEdit) {
_layoutElementToEdit = layoutElementToEdit;
}
[_tableNode reloadData];
}
#pragma mark - ASTableDataSource
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
NSDictionary *attributes = @{NSForegroundColorAttributeName : [UIColor colorWithRed:255/255.0 green:181/255.0 blue:68/255.0 alpha:1],
NSFontAttributeName : [UIFont fontWithName:@"Menlo-Regular" size:12]};
ASTextCellNode *textCell = [[ASTextCellNode alloc] initWithAttributes:attributes insets:UIEdgeInsetsMake(0, 4, 0, 0)];
textCell.text = [_layoutElementToEdit description];
return textCell;
} else {
return [[ASLayoutElementInspectorCell alloc] initWithProperty:(ASLayoutElementPropertyType)indexPath.row layoutElementToEdit:_layoutElementToEdit];
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
} else {
return ASLayoutElementPropertyCount;
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UILabel *headerTitle = [[UILabel alloc] initWithFrame:CGRectZero];
NSString *title;
if (section == 0) {
title = @"<Layoutable> Item";
} else {
title = @"<Layoutable> Properties";
}
NSDictionary *attributes = @{NSForegroundColorAttributeName : [UIColor whiteColor],
NSFontAttributeName : [UIFont fontWithName:@"Menlo-Bold" size:12]};
headerTitle.attributedText = [[NSAttributedString alloc] initWithString:title attributes:attributes];
return headerTitle;
}
//- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
//{
// // navigate layout hierarchy
//
// _parentNodeNavBtn.alignSelf = ASStackLayoutAlignSelfCenter;
// _childNodeNavBtn.alignSelf = ASStackLayoutAlignSelfCenter;
//
// ASStackLayoutSpec *horizontalStackNav = [ASStackLayoutSpec horizontalStackLayoutSpec];
// horizontalStackNav.style.flexGrow = 1.0;
// horizontalStackNav.alignSelf = ASStackLayoutAlignSelfCenter;
// horizontalStackNav.children = @[_siblingNodeLefttNavBtn, _siblingNodeRightNavBtn];
//
// ASStackLayoutSpec *horizontalStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
// horizontalStack.style.flexGrow = 1.0;
// ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
//
// spacer.style.flexGrow = 1.0;
// horizontalStack.children = @[_flexGrowBtn, spacer];
// _flexGrowValue.alignSelf = ASStackLayoutAlignSelfEnd; // FIXME: make framework give a warning if you use ASAlignmentBottom!!!!!
//
// ASStackLayoutSpec *horizontalStack2 = [ASStackLayoutSpec horizontalStackLayoutSpec];
// horizontalStack2.style.flexGrow = 1.0;
// horizontalStack2.children = @[_flexShrinkBtn, spacer];
// _flexShrinkValue.alignSelf = ASStackLayoutAlignSelfEnd;
//
// ASStackLayoutSpec *horizontalStack3 = [ASStackLayoutSpec horizontalStackLayoutSpec];
// horizontalStack3.style.flexGrow = 1.0;
// horizontalStack3.children = @[_flexBasisBtn, spacer, _flexBasisValue];
// _flexBasisValue.alignSelf = ASStackLayoutAlignSelfEnd;
//
// ASStackLayoutSpec *itemDescriptionStack = [ASStackLayoutSpec verticalStackLayoutSpec];
// itemDescriptionStack.children = @[_itemDescription];
// itemDescriptionStack.spacing = 5;
// itemDescriptionStack.style.flexGrow = 1.0;
//
// ASStackLayoutSpec *layoutableStack = [ASStackLayoutSpec verticalStackLayoutSpec];
// layoutableStack.children = @[_layoutablePropertiesSectionTitle, horizontalStack, horizontalStack2, horizontalStack3, _alignSelfBtn];
// layoutableStack.spacing = 5;
// layoutableStack.style.flexGrow = 1.0;
//
// ASStackLayoutSpec *layoutSpecStack = [ASStackLayoutSpec verticalStackLayoutSpec];
// layoutSpecStack.children = @[_layoutSpecPropertiesSectionTitle, _alignItemsBtn];
// layoutSpecStack.spacing = 5;
// layoutSpecStack.style.flexGrow = 1.0;
//
// ASStackLayoutSpec *debugHelpStack = [ASStackLayoutSpec verticalStackLayoutSpec];
// debugHelpStack.children = @[_debugSectionTitle, _vizNodeInsetSizeBtn, _vizNodeBordersBtn];
// debugHelpStack.spacing = 5;
// debugHelpStack.style.flexGrow = 1.0;
//
// ASStackLayoutSpec *verticalLayoutableStack = [ASStackLayoutSpec verticalStackLayoutSpec];
// verticalLayoutableStack.style.flexGrow = 1.0;
// verticalLayoutableStack.spacing = 20;
// 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];
// insetSpec.style.flexGrow = 1.0;
// return insetSpec;
//}
//
//#pragma mark - configure Inspector node for layoutable
//- (void)updateInspectorWithLayoutable
//{
// _itemDescription.attributedText = [self attributedStringFromLayoutable:_layoutElementToEdit];
//
// if ([self node]) {
// UIColor *nodeBackgroundColor = [[self node] backgroundColor];
// UIImage *colorBtnImg = [ASLayoutElementInspectorNode imageForButtonWithBackgroundColor:nodeBackgroundColor
// borderColor:[UIColor whiteColor]
// borderWidth:3];
// [_itemBackgroundColorBtn setBackgroundImage:colorBtnImg forState:ASControlStateNormal];
// } else {
// _itemBackgroundColorBtn.enabled = NO;
// }
//
// _flexGrowBtn.selected = [self.layoutElementToEdit flexGrow];
// _flexGrowValue.attributedText = [self attributedStringFromString: (_flexGrowBtn.selected) ? @"YES" : @"NO"];
//
// _flexShrinkBtn.selected = self.layoutElementToEdit.style.flexShrink;
// _flexShrinkValue.attributedText = [self attributedStringFromString: (_flexShrinkBtn.selected) ? @"YES" : @"NO"];
//
// // _flexBasisBtn.selected = self.layoutElementToEdit.style.flexShrink;
// // _flexBasisValue.attributedText = [self attributedStringFromString: (_flexBasisBtn.selected) ? @"YES" : @"NO"];
//
//
// NSUInteger alignSelfValue = [self.layoutElementToEdit alignSelf];
// NSString *newTitle = [@"alignSelf:" stringByAppendingString:[self alignSelfName:alignSelfValue]];
// [_alignSelfBtn setAttributedTitle:[self attributedStringFromString:newTitle] forState:ASControlStateNormal];
//
// if ([self layoutSpec]) {
// _alignItemsBtn.enabled = YES;
//// NSUInteger alignItemsValue = [[self layoutSpec] alignItems];
//// newTitle = [@"alignItems:" stringByAppendingString:[self alignSelfName:alignItemsValue]];
//// [_alignItemsBtn setAttributedTitle:[self attributedStringFromString:newTitle] forState:ASControlStateNormal];
// }
//
// [self setNeedsLayout];
//}
//- (void)enableInspectorNodesForLayoutable
//{
// if ([self layoutSpec]) {
//
// _itemBackgroundColorBtn.enabled = YES;
// _flexGrowBtn.enabled = YES;
// _flexShrinkBtn.enabled = YES;
// _flexBasisBtn.enabled = YES;
// _alignSelfBtn.enabled = YES;
// _spacingBeforeBtn.enabled = YES;
// _spacingAfterBtn.enabled = YES;
// _alignItemsBtn.enabled = YES;
//
// } else if ([self node]) {
//
// _itemBackgroundColorBtn.enabled = YES;
// _flexGrowBtn.enabled = YES;
// _flexShrinkBtn.enabled = YES;
// _flexBasisBtn.enabled = YES;
// _alignSelfBtn.enabled = YES;
// _spacingBeforeBtn.enabled = YES;
// _spacingAfterBtn.enabled = YES;
// _alignItemsBtn.enabled = NO;
//
// } else {
//
// _itemBackgroundColorBtn.enabled = NO;
// _flexGrowBtn.enabled = NO;
// _flexShrinkBtn.enabled = NO;
// _flexBasisBtn.enabled = NO;
// _alignSelfBtn.enabled = NO;
// _spacingBeforeBtn.enabled = NO;
// _spacingAfterBtn.enabled = NO;
// _alignItemsBtn.enabled = YES;
// }
//}
//+ (NSDictionary *)alignSelfTypeNames
//{
// return @{@(ASStackLayoutAlignSelfAuto) : @"Auto",
// @(ASStackLayoutAlignSelfStart) : @"Start",
// @(ASStackLayoutAlignSelfEnd) : @"End",
// @(ASStackLayoutAlignSelfCenter) : @"Center",
// @(ASStackLayoutAlignSelfStretch) : @"Stretch"};
//}
//
//- (NSString *)alignSelfName:(NSUInteger)type
//{
// return [[self class] alignSelfTypeNames][@(type)];
//}
//
//+ (NSDictionary *)alignItemTypeNames
//{
// return @{@(ASStackLayoutAlignItemsBaselineFirst) : @"BaselineFirst",
// @(ASStackLayoutAlignItemsBaselineLast) : @"BaselineLast",
// @(ASStackLayoutAlignItemsCenter) : @"Center",
// @(ASStackLayoutAlignItemsEnd) : @"End",
// @(ASStackLayoutAlignItemsStart) : @"Start",
// @(ASStackLayoutAlignItemsStretch) : @"Stretch"};
//}
//
//- (NSString *)alignItemName:(NSUInteger)type
//{
// return [[self class] alignItemTypeNames][@(type)];
//}
//#pragma mark - gesture handling
//- (void)changeColor:(ASButtonNode *)sender
//{
// if ([self node]) {
// NSArray *colorArray = @[[UIColor orangeColor],
// [UIColor redColor],
// [UIColor greenColor],
// [UIColor purpleColor]];
//
// UIColor *nodeBackgroundColor = [(ASDisplayNode *)self.layoutElementToEdit backgroundColor];
//
// NSUInteger colorIndex = [colorArray indexOfObject:nodeBackgroundColor];
// colorIndex = (colorIndex + 1 < [colorArray count]) ? colorIndex + 1 : 0;
//
// [[self node] setBackgroundColor: [colorArray objectAtIndex:colorIndex]];
// }
//
// [self updateInspectorWithLayoutable];
//}
//
//- (void)setFlexGrowValue:(ASButtonNode *)sender
//{
// [sender setSelected:!sender.isSelected]; // FIXME: fix ASControlNode documentation that this is automatic - unlike highlighted, it is up to the application to decide when a button should be selected or not. Selected is a more persistant thing and highlighted is for the moment, like as a user has a finger on it,
//
// if ([self layoutSpec]) {
// [[self layoutSpec] setFlexGrow:sender.isSelected];
// } else if ([self node]) {
// [[self node] setFlexGrow:sender.isSelected];
// }
//
// [self updateInspectorWithLayoutable];
//}
//
//- (void)setFlexShrinkValue:(ASButtonNode *)sender
//{
// [sender setSelected:!sender.isSelected]; // FIXME: fix ASControlNode documentation that this is automatic - unlike highlighted, it is up to the application to decide when a button should be selected or not. Selected is a more persistant thing and highlighted is for the moment, like as a user has a finger on it,
//
// if ([self layoutSpec]) {
// [[self layoutSpec] setFlexShrink:sender.isSelected];
// } else if ([self node]) {
// [[self node] setFlexShrink:sender.isSelected];
// }
//
// [self updateInspectorWithLayoutable];
//}
//
//- (void)setAlignSelfValue:(ASButtonNode *)sender
//{
// NSUInteger currentAlignSelfValue;
// NSUInteger nextAlignSelfValue;
//
// if ([self layoutSpec]) {
// currentAlignSelfValue = [[self layoutSpec] alignSelf];
// nextAlignSelfValue = (currentAlignSelfValue + 1 <= ASStackLayoutAlignSelfStretch) ? currentAlignSelfValue + 1 : 0;
// [[self layoutSpec] setAlignSelf:nextAlignSelfValue];
//
// } else if ([self node]) {
// currentAlignSelfValue = [[self node] alignSelf];
// nextAlignSelfValue = (currentAlignSelfValue + 1 <= ASStackLayoutAlignSelfStretch) ? currentAlignSelfValue + 1 : 0;
// [[self node] setAlignSelf:nextAlignSelfValue];
// }
//
// [self updateInspectorWithLayoutable];
//}
//
//- (void)setAlignItemsValue:(ASButtonNode *)sender
//{
// NSUInteger currentAlignItemsValue;
// NSUInteger nextAlignItemsValue;
//
// if ([self layoutSpec]) {
// currentAlignItemsValue = [[self layoutSpec] alignSelf];
// nextAlignItemsValue = (currentAlignItemsValue + 1 <= ASStackLayoutAlignSelfStretch) ? currentAlignItemsValue + 1 : 0;
//// [[self layoutSpec] setAlignItems:nextAlignItemsValue];
//
// } else if ([self node]) {
// currentAlignItemsValue = [[self node] alignSelf];
// nextAlignItemsValue = (currentAlignItemsValue + 1 <= ASStackLayoutAlignSelfStretch) ? currentAlignItemsValue + 1 : 0;
//// [[self node] setAlignItems:nextAlignItemsValue];
// }
//
// [self updateInspectorWithLayoutable];
//}
//- (void)setFlexBasisValue:(ASButtonNode *)sender
//{
// [sender setSelected:!sender.isSelected]; // FIXME: fix ASControlNode documentation that this is automatic - unlike highlighted, it is up to the application to decide when a button should be selected or not. Selected is a more persistant thing and highlighted is for the moment, like as a user has a finger on it,
// FIXME: finish
//}
//
//- (void)setVizNodeInsets:(ASButtonNode *)sender
//{
// BOOL newState = !sender.selected;
//
// if (newState == YES) {
// self.vizNodeInsetSize = 0;
// [self.delegate toggleVisualization:NO]; // FIXME
// [self.delegate toggleVisualization:YES]; // FIXME
// _vizNodeBordersBtn.selected = YES;
//
// } else {
// self.vizNodeInsetSize = 10;
// [self.delegate toggleVisualization:NO]; // FIXME
// [self.delegate toggleVisualization:YES]; // FIXME
// }
//
// sender.selected = newState;
//}
//
//- (void)setVizNodeBorders:(ASButtonNode *)sender
//{
// BOOL newState = !sender.selected;
//
// [self.delegate toggleVisualization:newState]; // FIXME
//
// sender.selected = newState;
//}
@end

View File

@@ -0,0 +1,21 @@
//
// ASLayoutSpec+Debug.h
// AsyncDisplayKit
//
// Created by Hannah Troisi on 3/20/16.
//
//
#pragma once
#import "ASControlNode.h"
@class ASLayoutSpec;
@interface ASLayoutSpecVisualizerNode : ASControlNode
@property (nonatomic, strong) ASLayoutSpec *layoutSpec;
- (instancetype)initWithLayoutSpec:(ASLayoutSpec *)layoutSpec;
@end

View File

@@ -0,0 +1,73 @@
//
// ASLayoutSpec+Debug.m
// AsyncDisplayKit
//
// Created by Hannah Troisi on 3/20/16.
//
//
#import "ASLayoutSpec+Debug.h"
#import "ASDisplayNode+Beta.h"
#import "AsyncDisplayKit.h"
#import "ASLayoutElementInspectorNode.h"
@implementation ASLayoutSpecVisualizerNode
- (instancetype)initWithLayoutSpec:(ASLayoutSpec *)layoutSpec
{
self = [super init];
if (self) {
self.borderWidth = 2;
self.borderColor = [[UIColor redColor] CGColor];
self.layoutSpec = layoutSpec;
self.layoutSpec.neverShouldVisualize = YES;
self.automaticallyManagesSubnodes = YES;
self.shouldCacheLayoutSpec = YES;
[self addTarget:self action:@selector(visualizerNodeTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
CGFloat insetFloat = [ASLayoutElementInspectorNode sharedInstance].vizNodeInsetSize;
UIEdgeInsets insets = UIEdgeInsetsMake(insetFloat, insetFloat, insetFloat, insetFloat);
// FIXME in framework: auto pass properties to children
ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:self.layoutSpec];
insetSpec.neverShouldVisualize = YES;
// propogate child's layoutSpec properties to the inset that we are adding
// insetSpec.style.flexGrow = _layoutSpec.style.flexGrow;
// insetSpec.style.flexShrink = _layoutSpec.style.flexShrink;
// insetSpec.alignSelf = _layoutSpec.alignSelf;
// NSLog(@"%@: vizNode = %f, child = %f", self, insetSpec.style.flexGrow, _layoutSpec.style.flexGrow);
return insetSpec;
}
- (void)setLayoutSpec:(ASLayoutSpec *)layoutSpec // FIXME: this is duplicated in InspectorNode - make it a category on ASLayoutSpec?
{
_layoutSpec = layoutSpec; // FIXME: should copy layoutSpec properities to self?
if ([layoutSpec isKindOfClass:[ASInsetLayoutSpec class]]) {
self.borderColor = [[UIColor redColor] CGColor];
} else if ([layoutSpec isKindOfClass:[ASStackLayoutSpec class]]) {
self.borderColor = [[UIColor greenColor] CGColor];
}
}
- (NSString *)description
{
return [self.layoutSpec description]; // FIXME: expand on layoutSpec description (e.g. have StackLayoutSpec return horz/vert)
}
- (void)visualizerNodeTapped:(UIGestureRecognizer *)sender
{
[[ASLayoutElementInspectorNode sharedInstance] setLayoutElementToEdit:self.layoutSpec];
}
@end

View File

@@ -60,6 +60,10 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nullable, strong, nonatomic) NSArray<id<ASLayoutElement>> *children;
@property (nonatomic, assign) BOOL shouldVisualize;
@property (nonatomic, assign) BOOL neverShouldVisualize;
- (void)recursivelySetShouldVisualize:(BOOL)visualize;
@end
/**

View File

@@ -12,6 +12,11 @@
#import "ASLayoutSpecPrivate.h"
#import "ASLayoutSpec+Subclasses.h"
#import "ASLayoutElementStylePrivate.h"
#import "ASLayoutSpec+Debug.h"
#import <objc/runtime.h>
#import <map>
#import <vector>
@implementation ASLayoutSpec
@@ -60,7 +65,34 @@
- (id<ASLayoutElement>)finalLayoutElement
{
if (ASLayoutElementGetCurrentContext().needsVisualizeNode && !self.neverShouldVisualize) {
return [[ASLayoutSpecVisualizerNode alloc] initWithLayoutSpec:self];
} else {
return self;
}
}
- (void)recursivelySetShouldVisualize:(BOOL)visualize
{
NSMutableArray *mutableChildren = [self.children mutableCopy];
for (id<ASLayoutElement>layoutElement in self.children) {
if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement;
[mutableChildren replaceObjectAtIndex:[mutableChildren indexOfObjectIdenticalTo:layoutSpec]
withObject:[[ASLayoutSpecVisualizerNode alloc] initWithLayoutSpec:layoutSpec]];
[layoutSpec recursivelySetShouldVisualize:visualize];
layoutSpec.shouldVisualize = visualize;
}
}
if ([mutableChildren count] == 1) { // HACK for wrapper layoutSpecs (e.g. insetLayoutSpec)
self.child = mutableChildren[0];
} else if ([mutableChildren count] > 1) {
self.children = mutableChildren;
}
}
#pragma mark - Style
@@ -113,6 +145,9 @@
ASDisplayNodeAssert(_childrenArray.count < 2, @"This layout spec does not support more than one child. Use the setChildren: or the setChild:AtIndex: API");
if (child) {
if (child.layoutElementType == ASLayoutElementTypeLayoutSpec) {
[(ASLayoutSpec *)child setShouldVisualize:self.shouldVisualize];
}
id<ASLayoutElement> finalLayoutElement = [self layoutElementToAddFromLayoutElement:child];
if (finalLayoutElement) {
_childrenArray[0] = finalLayoutElement;
@@ -142,7 +177,11 @@
NSUInteger i = 0;
for (id<ASLayoutElement> child in children) {
ASDisplayNodeAssert([child conformsToProtocol:NSProtocolFromString(@"ASLayoutElement")], @"Child %@ of spec %@ is not an ASLayoutElement!", child, self);
_childrenArray[i] = [self layoutElementToAddFromLayoutElement:child];
id <ASLayoutElement> finalLayoutElement = [self layoutElementToAddFromLayoutElement:child];
if (finalLayoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
[(ASLayoutSpec *)finalLayoutElement setShouldVisualize:self.shouldVisualize];
}
_childrenArray[i] = finalLayoutElement;
i += 1;
}
}

View File

@@ -46,7 +46,8 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
ASHierarchyStateTransitioningSupernodes = 1 << 2,
/** One of the supernodes of this node is performing a transition.
Any layout calculated during this state should not be applied immediately, but pending until later. */
ASHierarchyStateLayoutPending = 1 << 3
ASHierarchyStateLayoutPending = 1 << 3,
ASHierarchyStateVisualizeLayout = 1 << 4
};
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesLayoutPending(ASHierarchyState hierarchyState)
@@ -59,6 +60,11 @@ ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState
return ((hierarchyState & ASHierarchyStateRangeManaged) == ASHierarchyStateRangeManaged);
}
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesVisualizeLayout(ASHierarchyState hierarchyState)
{
return ((hierarchyState & ASHierarchyStateVisualizeLayout) == ASHierarchyStateVisualizeLayout);
}
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRasterized(ASHierarchyState hierarchyState)
{
return ((hierarchyState & ASHierarchyStateRasterized) == ASHierarchyStateRasterized);

View File

@@ -144,6 +144,9 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme
if (parentLayoutOptionsState.flexGrow == defaultState.flexGrow) {
parentLayoutOptionsState.flexGrow = layoutOptionsState.flexGrow;
}
if (parentLayoutOptionsState.flexShrink == defaultState.flexShrink) {
parentLayoutOptionsState.flexShrink = layoutOptionsState.flexShrink;
}
if (ASDimensionEqualToDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) {
parentLayoutOptionsState.flexBasis = layoutOptionsState.flexBasis;
}

View File

@@ -819,7 +819,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
NSDictionary *map = [self sectionToIndexSetMapFromChanges:changes];
NSMutableString *str = [NSMutableString stringWithString:@"{ "];
[map enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull section, NSIndexSet * _Nonnull indexSet, BOOL * _Nonnull stop) {
[str appendFormat:@"@%lu : %@ ", section.integerValue, [indexSet as_smallDescription]];
[str appendFormat:@"@%lu : %@ ", (long)section.integerValue, [indexSet as_smallDescription]];
}];
[str appendString:@"}"];
return str;

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Sample.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,6 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.1'
target 'Sample' do
pod 'AsyncDisplayKit', :path => '../..'
end

View File

@@ -0,0 +1,958 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>archiveVersion</key>
<string>1</string>
<key>classes</key>
<dict/>
<key>objectVersion</key>
<string>46</string>
<key>objects</key>
<dict>
<key>0585427F19D4DBE100606EA6</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>image.png</string>
<key>name</key>
<string>Default-568h@2x.png</string>
<key>path</key>
<string>../Default-568h@2x.png</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>0585428019D4DBE100606EA6</key>
<dict>
<key>fileRef</key>
<string>0585427F19D4DBE100606EA6</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>05E2127819D4DB510098F589</key>
<dict>
<key>children</key>
<array>
<string>05E2128319D4DB510098F589</string>
<string>05E2128219D4DB510098F589</string>
<string>1A943BF0259746F18D6E423F</string>
<string>1AE410B73DA5C3BD087ACDD7</string>
</array>
<key>indentWidth</key>
<string>2</string>
<key>isa</key>
<string>PBXGroup</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
<key>tabWidth</key>
<string>2</string>
<key>usesTabs</key>
<string>0</string>
</dict>
<key>05E2127919D4DB510098F589</key>
<dict>
<key>attributes</key>
<dict>
<key>LastUpgradeCheck</key>
<string>0720</string>
<key>ORGANIZATIONNAME</key>
<string>Facebook</string>
<key>TargetAttributes</key>
<dict>
<key>05E2128019D4DB510098F589</key>
<dict>
<key>CreatedOnToolsVersion</key>
<string>6.0.1</string>
</dict>
</dict>
</dict>
<key>buildConfigurationList</key>
<string>05E2127C19D4DB510098F589</string>
<key>compatibilityVersion</key>
<string>Xcode 3.2</string>
<key>developmentRegion</key>
<string>English</string>
<key>hasScannedForEncodings</key>
<string>0</string>
<key>isa</key>
<string>PBXProject</string>
<key>knownRegions</key>
<array>
<string>en</string>
<string>Base</string>
</array>
<key>mainGroup</key>
<string>05E2127819D4DB510098F589</string>
<key>productRefGroup</key>
<string>05E2128219D4DB510098F589</string>
<key>projectDirPath</key>
<string></string>
<key>projectReferences</key>
<array/>
<key>projectRoot</key>
<string></string>
<key>targets</key>
<array>
<string>05E2128019D4DB510098F589</string>
</array>
</dict>
<key>05E2127C19D4DB510098F589</key>
<dict>
<key>buildConfigurations</key>
<array>
<string>05E212A219D4DB510098F589</string>
<string>05E212A319D4DB510098F589</string>
</array>
<key>defaultConfigurationIsVisible</key>
<string>0</string>
<key>defaultConfigurationName</key>
<string>Release</string>
<key>isa</key>
<string>XCConfigurationList</string>
</dict>
<key>05E2127D19D4DB510098F589</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array>
<string>76466F321C9DFFC4006C4D2D</string>
<string>05E2128719D4DB510098F589</string>
<string>7602C7651CA4F83100D0D917</string>
<string>76466F341C9DFFC4006C4D2D</string>
<string>76F58D5C1C9E15C1004512CC</string>
<string>76466F351C9DFFC4006C4D2D</string>
<string>80A6A5181D88F08F00473431</string>
</array>
<key>isa</key>
<string>PBXSourcesBuildPhase</string>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
</dict>
<key>05E2127E19D4DB510098F589</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array>
<string>B971D066CC023A00C53D8575</string>
</array>
<key>isa</key>
<string>PBXFrameworksBuildPhase</string>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
</dict>
<key>05E2127F19D4DB510098F589</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array>
<string>0585428019D4DBE100606EA6</string>
<string>6C2C82AC19EE274300767484</string>
<string>6C2C82AD19EE274300767484</string>
<string>7602C7671CA4FB5300D0D917</string>
</array>
<key>isa</key>
<string>PBXResourcesBuildPhase</string>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
</dict>
<key>05E2128019D4DB510098F589</key>
<dict>
<key>buildConfigurationList</key>
<string>05E212A419D4DB510098F589</string>
<key>buildPhases</key>
<array>
<string>E080B80F89C34A25B3488E26</string>
<string>05E2127D19D4DB510098F589</string>
<string>05E2127E19D4DB510098F589</string>
<string>05E2127F19D4DB510098F589</string>
<string>F012A6F39E0149F18F564F50</string>
<string>2291CF7AF2D4B273DDAD8AAC</string>
</array>
<key>buildRules</key>
<array/>
<key>dependencies</key>
<array/>
<key>isa</key>
<string>PBXNativeTarget</string>
<key>name</key>
<string>Sample</string>
<key>productName</key>
<string>Sample</string>
<key>productReference</key>
<string>05E2128119D4DB510098F589</string>
<key>productType</key>
<string>com.apple.product-type.application</string>
</dict>
<key>05E2128119D4DB510098F589</key>
<dict>
<key>explicitFileType</key>
<string>wrapper.application</string>
<key>includeInIndex</key>
<string>0</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>path</key>
<string>Sample.app</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
</dict>
<key>05E2128219D4DB510098F589</key>
<dict>
<key>children</key>
<array>
<string>05E2128119D4DB510098F589</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Products</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>05E2128319D4DB510098F589</key>
<dict>
<key>children</key>
<array>
<string>76466F2A1C9DFFC4006C4D2D</string>
<string>76466F2B1C9DFFC4006C4D2D</string>
<string>76466F301C9DFFC4006C4D2D</string>
<string>76466F311C9DFFC4006C4D2D</string>
<string>76F58D5A1C9E15C1004512CC</string>
<string>76F58D5B1C9E15C1004512CC</string>
<string>76466F2E1C9DFFC4006C4D2D</string>
<string>76466F2F1C9DFFC4006C4D2D</string>
<string>80A6A5161D88F08F00473431</string>
<string>80A6A5171D88F08F00473431</string>
<string>7602C7631CA4F83100D0D917</string>
<string>7602C7641CA4F83100D0D917</string>
<string>05E2128419D4DB510098F589</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>path</key>
<string>Sample</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>05E2128419D4DB510098F589</key>
<dict>
<key>children</key>
<array>
<string>0585427F19D4DBE100606EA6</string>
<string>6C2C82AA19EE274300767484</string>
<string>7602C7661CA4FB5300D0D917</string>
<string>6C2C82AB19EE274300767484</string>
<string>05E2128519D4DB510098F589</string>
<string>05E2128619D4DB510098F589</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Supporting Files</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>05E2128519D4DB510098F589</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>text.plist.xml</string>
<key>path</key>
<string>Info.plist</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>05E2128619D4DB510098F589</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>main.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>05E2128719D4DB510098F589</key>
<dict>
<key>fileRef</key>
<string>05E2128619D4DB510098F589</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>05E212A219D4DB510098F589</key>
<dict>
<key>buildSettings</key>
<dict>
<key>ALWAYS_SEARCH_USER_PATHS</key>
<string>NO</string>
<key>CLANG_CXX_LANGUAGE_STANDARD</key>
<string>gnu++0x</string>
<key>CLANG_CXX_LIBRARY</key>
<string>libc++</string>
<key>CLANG_ENABLE_MODULES</key>
<string>YES</string>
<key>CLANG_ENABLE_OBJC_ARC</key>
<string>YES</string>
<key>CLANG_WARN_BOOL_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_CONSTANT_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_DIRECT_OBJC_ISA_USAGE</key>
<string>YES_ERROR</string>
<key>CLANG_WARN_EMPTY_BODY</key>
<string>YES</string>
<key>CLANG_WARN_ENUM_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_INT_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_OBJC_ROOT_CLASS</key>
<string>YES_ERROR</string>
<key>CLANG_WARN_UNREACHABLE_CODE</key>
<string>YES</string>
<key>CLANG_WARN__DUPLICATE_METHOD_MATCH</key>
<string>YES</string>
<key>CODE_SIGN_IDENTITY[sdk=iphoneos*]</key>
<string>iPhone Developer</string>
<key>COPY_PHASE_STRIP</key>
<string>NO</string>
<key>ENABLE_STRICT_OBJC_MSGSEND</key>
<string>YES</string>
<key>ENABLE_TESTABILITY</key>
<string>YES</string>
<key>GCC_C_LANGUAGE_STANDARD</key>
<string>gnu99</string>
<key>GCC_DYNAMIC_NO_PIC</key>
<string>NO</string>
<key>GCC_OPTIMIZATION_LEVEL</key>
<string>0</string>
<key>GCC_PREPROCESSOR_DEFINITIONS</key>
<array>
<string>DEBUG=1</string>
<string>$(inherited)</string>
</array>
<key>GCC_SYMBOLS_PRIVATE_EXTERN</key>
<string>NO</string>
<key>GCC_WARN_64_TO_32_BIT_CONVERSION</key>
<string>YES</string>
<key>GCC_WARN_ABOUT_RETURN_TYPE</key>
<string>YES_ERROR</string>
<key>GCC_WARN_UNDECLARED_SELECTOR</key>
<string>YES</string>
<key>GCC_WARN_UNINITIALIZED_AUTOS</key>
<string>YES_AGGRESSIVE</string>
<key>GCC_WARN_UNUSED_FUNCTION</key>
<string>YES</string>
<key>GCC_WARN_UNUSED_VARIABLE</key>
<string>YES</string>
<key>IPHONEOS_DEPLOYMENT_TARGET</key>
<string>7.1</string>
<key>MTL_ENABLE_DEBUG_INFO</key>
<string>YES</string>
<key>ONLY_ACTIVE_ARCH</key>
<string>YES</string>
<key>SDKROOT</key>
<string>iphoneos</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Debug</string>
</dict>
<key>05E212A319D4DB510098F589</key>
<dict>
<key>buildSettings</key>
<dict>
<key>ALWAYS_SEARCH_USER_PATHS</key>
<string>NO</string>
<key>CLANG_CXX_LANGUAGE_STANDARD</key>
<string>gnu++0x</string>
<key>CLANG_CXX_LIBRARY</key>
<string>libc++</string>
<key>CLANG_ENABLE_MODULES</key>
<string>YES</string>
<key>CLANG_ENABLE_OBJC_ARC</key>
<string>YES</string>
<key>CLANG_WARN_BOOL_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_CONSTANT_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_DIRECT_OBJC_ISA_USAGE</key>
<string>YES_ERROR</string>
<key>CLANG_WARN_EMPTY_BODY</key>
<string>YES</string>
<key>CLANG_WARN_ENUM_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_INT_CONVERSION</key>
<string>YES</string>
<key>CLANG_WARN_OBJC_ROOT_CLASS</key>
<string>YES_ERROR</string>
<key>CLANG_WARN_UNREACHABLE_CODE</key>
<string>YES</string>
<key>CLANG_WARN__DUPLICATE_METHOD_MATCH</key>
<string>YES</string>
<key>CODE_SIGN_IDENTITY[sdk=iphoneos*]</key>
<string>iPhone Developer</string>
<key>COPY_PHASE_STRIP</key>
<string>YES</string>
<key>ENABLE_NS_ASSERTIONS</key>
<string>NO</string>
<key>ENABLE_STRICT_OBJC_MSGSEND</key>
<string>YES</string>
<key>GCC_C_LANGUAGE_STANDARD</key>
<string>gnu99</string>
<key>GCC_WARN_64_TO_32_BIT_CONVERSION</key>
<string>YES</string>
<key>GCC_WARN_ABOUT_RETURN_TYPE</key>
<string>YES_ERROR</string>
<key>GCC_WARN_UNDECLARED_SELECTOR</key>
<string>YES</string>
<key>GCC_WARN_UNINITIALIZED_AUTOS</key>
<string>YES_AGGRESSIVE</string>
<key>GCC_WARN_UNUSED_FUNCTION</key>
<string>YES</string>
<key>GCC_WARN_UNUSED_VARIABLE</key>
<string>YES</string>
<key>IPHONEOS_DEPLOYMENT_TARGET</key>
<string>7.1</string>
<key>MTL_ENABLE_DEBUG_INFO</key>
<string>NO</string>
<key>SDKROOT</key>
<string>iphoneos</string>
<key>VALIDATE_PRODUCT</key>
<string>YES</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Release</string>
</dict>
<key>05E212A419D4DB510098F589</key>
<dict>
<key>buildConfigurations</key>
<array>
<string>05E212A519D4DB510098F589</string>
<string>05E212A619D4DB510098F589</string>
</array>
<key>defaultConfigurationIsVisible</key>
<string>0</string>
<key>defaultConfigurationName</key>
<string>Release</string>
<key>isa</key>
<string>XCConfigurationList</string>
</dict>
<key>05E212A519D4DB510098F589</key>
<dict>
<key>baseConfigurationReference</key>
<string>FDF496F367580DF9280D36EA</string>
<key>buildSettings</key>
<dict>
<key>ASSETCATALOG_COMPILER_APPICON_NAME</key>
<string>AppIcon</string>
<key>INFOPLIST_FILE</key>
<string>Sample/Info.plist</string>
<key>IPHONEOS_DEPLOYMENT_TARGET</key>
<string>7.1</string>
<key>LD_RUNPATH_SEARCH_PATHS</key>
<string>$(inherited) @executable_path/Frameworks</string>
<key>PRODUCT_BUNDLE_IDENTIFIER</key>
<string>com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>PRODUCT_NAME</key>
<string>$(TARGET_NAME)</string>
<key>TARGETED_DEVICE_FAMILY</key>
<string>1,2</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Debug</string>
</dict>
<key>05E212A619D4DB510098F589</key>
<dict>
<key>baseConfigurationReference</key>
<string>5C5154389F056C672F4E9EEA</string>
<key>buildSettings</key>
<dict>
<key>ASSETCATALOG_COMPILER_APPICON_NAME</key>
<string>AppIcon</string>
<key>INFOPLIST_FILE</key>
<string>Sample/Info.plist</string>
<key>IPHONEOS_DEPLOYMENT_TARGET</key>
<string>7.1</string>
<key>LD_RUNPATH_SEARCH_PATHS</key>
<string>$(inherited) @executable_path/Frameworks</string>
<key>PRODUCT_BUNDLE_IDENTIFIER</key>
<string>com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>PRODUCT_NAME</key>
<string>$(TARGET_NAME)</string>
<key>TARGETED_DEVICE_FAMILY</key>
<string>1,2</string>
</dict>
<key>isa</key>
<string>XCBuildConfiguration</string>
<key>name</key>
<string>Release</string>
</dict>
<key>088AA6578212BE9BFBB07B70</key>
<dict>
<key>includeInIndex</key>
<string>1</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>text.xcconfig</string>
<key>name</key>
<string>Pods.release.xcconfig</string>
<key>path</key>
<string>Pods/Target Support Files/Pods/Pods.release.xcconfig</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>1A943BF0259746F18D6E423F</key>
<dict>
<key>children</key>
<array>
<string>3D24B17D1E4A4E7A9566C5E9</string>
<string>54A6EB3DE8D9A9EC4AE2867D</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Frameworks</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>1AE410B73DA5C3BD087ACDD7</key>
<dict>
<key>children</key>
<array>
<string>C068F1D3F0CC317E895FCDAB</string>
<string>088AA6578212BE9BFBB07B70</string>
<string>FDF496F367580DF9280D36EA</string>
<string>5C5154389F056C672F4E9EEA</string>
</array>
<key>isa</key>
<string>PBXGroup</string>
<key>name</key>
<string>Pods</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>2291CF7AF2D4B273DDAD8AAC</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array/>
<key>inputPaths</key>
<array/>
<key>isa</key>
<string>PBXShellScriptBuildPhase</string>
<key>name</key>
<string>[CP] Embed Pods Frameworks</string>
<key>outputPaths</key>
<array/>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
<key>shellPath</key>
<string>/bin/sh</string>
<key>shellScript</key>
<string>"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh"
</string>
<key>showEnvVarsInLog</key>
<string>0</string>
</dict>
<key>3D24B17D1E4A4E7A9566C5E9</key>
<dict>
<key>explicitFileType</key>
<string>archive.ar</string>
<key>includeInIndex</key>
<string>0</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>path</key>
<string>libPods.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
</dict>
<key>54A6EB3DE8D9A9EC4AE2867D</key>
<dict>
<key>explicitFileType</key>
<string>archive.ar</string>
<key>includeInIndex</key>
<string>0</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>path</key>
<string>libPods-Sample.a</string>
<key>sourceTree</key>
<string>BUILT_PRODUCTS_DIR</string>
</dict>
<key>5C5154389F056C672F4E9EEA</key>
<dict>
<key>includeInIndex</key>
<string>1</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>text.xcconfig</string>
<key>name</key>
<string>Pods-Sample.release.xcconfig</string>
<key>path</key>
<string>Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>6C2C82AA19EE274300767484</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>image.png</string>
<key>path</key>
<string>Default-667h@2x.png</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>6C2C82AB19EE274300767484</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>image.png</string>
<key>path</key>
<string>Default-736h@3x.png</string>
<key>sourceTree</key>
<string>SOURCE_ROOT</string>
</dict>
<key>6C2C82AC19EE274300767484</key>
<dict>
<key>fileRef</key>
<string>6C2C82AA19EE274300767484</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>6C2C82AD19EE274300767484</key>
<dict>
<key>fileRef</key>
<string>6C2C82AB19EE274300767484</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>7602C7631CA4F83100D0D917</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>path</key>
<string>Utilities.h</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>7602C7641CA4F83100D0D917</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>Utilities.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>7602C7651CA4F83100D0D917</key>
<dict>
<key>fileRef</key>
<string>7602C7641CA4F83100D0D917</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>7602C7661CA4FB5300D0D917</key>
<dict>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>image.png</string>
<key>path</key>
<string>resizeHandle.png</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>7602C7671CA4FB5300D0D917</key>
<dict>
<key>fileRef</key>
<string>7602C7661CA4FB5300D0D917</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>76466F2A1C9DFFC4006C4D2D</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>path</key>
<string>AppDelegate.h</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76466F2B1C9DFFC4006C4D2D</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>AppDelegate.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76466F2E1C9DFFC4006C4D2D</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>path</key>
<string>PhotoPostNode.h</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76466F2F1C9DFFC4006C4D2D</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>PhotoPostNode.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76466F301C9DFFC4006C4D2D</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>path</key>
<string>ViewController.h</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76466F311C9DFFC4006C4D2D</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>ViewController.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76466F321C9DFFC4006C4D2D</key>
<dict>
<key>fileRef</key>
<string>76466F2B1C9DFFC4006C4D2D</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>76466F341C9DFFC4006C4D2D</key>
<dict>
<key>fileRef</key>
<string>76466F2F1C9DFFC4006C4D2D</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>76466F351C9DFFC4006C4D2D</key>
<dict>
<key>fileRef</key>
<string>76466F311C9DFFC4006C4D2D</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>76F58D5A1C9E15C1004512CC</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>path</key>
<string>PlaygroundContainerNode.h</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76F58D5B1C9E15C1004512CC</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>PlaygroundContainerNode.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>76F58D5C1C9E15C1004512CC</key>
<dict>
<key>fileRef</key>
<string>76F58D5B1C9E15C1004512CC</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>80A6A5161D88F08F00473431</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.h</string>
<key>path</key>
<string>LayoutExampleNodes.h</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>80A6A5171D88F08F00473431</key>
<dict>
<key>fileEncoding</key>
<string>4</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>sourcecode.c.objc</string>
<key>path</key>
<string>LayoutExampleNodes.m</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>80A6A5181D88F08F00473431</key>
<dict>
<key>fileRef</key>
<string>80A6A5171D88F08F00473431</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>B971D066CC023A00C53D8575</key>
<dict>
<key>fileRef</key>
<string>54A6EB3DE8D9A9EC4AE2867D</string>
<key>isa</key>
<string>PBXBuildFile</string>
</dict>
<key>C068F1D3F0CC317E895FCDAB</key>
<dict>
<key>includeInIndex</key>
<string>1</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>text.xcconfig</string>
<key>name</key>
<string>Pods.debug.xcconfig</string>
<key>path</key>
<string>Pods/Target Support Files/Pods/Pods.debug.xcconfig</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
<key>E080B80F89C34A25B3488E26</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array/>
<key>inputPaths</key>
<array/>
<key>isa</key>
<string>PBXShellScriptBuildPhase</string>
<key>name</key>
<string>[CP] Check Pods Manifest.lock</string>
<key>outputPaths</key>
<array/>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
<key>shellPath</key>
<string>/bin/sh</string>
<key>shellScript</key>
<string>diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" &gt; /dev/null
if [[ $? != 0 ]] ; then
cat &lt;&lt; EOM
error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
EOM
exit 1
fi
</string>
<key>showEnvVarsInLog</key>
<string>0</string>
</dict>
<key>F012A6F39E0149F18F564F50</key>
<dict>
<key>buildActionMask</key>
<string>2147483647</string>
<key>files</key>
<array/>
<key>inputPaths</key>
<array/>
<key>isa</key>
<string>PBXShellScriptBuildPhase</string>
<key>name</key>
<string>[CP] Copy Pods Resources</string>
<key>outputPaths</key>
<array/>
<key>runOnlyForDeploymentPostprocessing</key>
<string>0</string>
<key>shellPath</key>
<string>/bin/sh</string>
<key>shellScript</key>
<string>"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh"
</string>
<key>showEnvVarsInLog</key>
<string>0</string>
</dict>
<key>FDF496F367580DF9280D36EA</key>
<dict>
<key>includeInIndex</key>
<string>1</string>
<key>isa</key>
<string>PBXFileReference</string>
<key>lastKnownFileType</key>
<string>text.xcconfig</string>
<key>name</key>
<string>Pods-Sample.debug.xcconfig</string>
<key>path</key>
<string>Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig</string>
<key>sourceTree</key>
<string>&lt;group&gt;</string>
</dict>
</dict>
<key>rootObject</key>
<string>05E2127919D4DB510098F589</string>
</dict>
</plist>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Sample.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,16 @@
//
// AppDelegate.h
// ASLayoutSpecPlayground
//
// Created by Hannah Troisi on 3/11/16.
// Copyright © 2016 Hannah Troisi. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,50 @@
//
// AppDelegate.m
// ASLayoutSpecPlayground
//
// Created by Hannah Troisi on 3/11/16.
// Copyright © 2016 Hannah Troisi. All rights reserved.
//
#import "AppDelegate.h"
#import "ViewController.h"
#import "Utilities.h"
#import <AsyncDisplayKit/ASLayoutElementInspectorNode.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIViewController *rootVC = nil;
UIDevice *device = [UIDevice currentDevice];
if (device.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
ASViewController *masterVC = [[ASViewController alloc] initWithNode:[ASLayoutElementInspectorNode sharedInstance]];
masterVC.view.backgroundColor = [UIColor customOrangeColor];
UINavigationController *masterNav = [[UINavigationController alloc] initWithRootViewController:masterVC];
ViewController *detailVC = [[ViewController alloc] init];
UINavigationController *detailNav = [[UINavigationController alloc] initWithRootViewController:detailVC];
UISplitViewController *splitVC = [[UISplitViewController alloc] init];
splitVC.viewControllers = @[masterNav, detailNav];
splitVC.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
splitVC.maximumPrimaryColumnWidth = 250;
rootVC = splitVC;
} else {
// FIXME: make this work for iPhones
NSAssert(YES, @"App optimized for iPad only.");
}
[self.window setRootViewController:rootVC];
[self.window makeKeyAndVisible];
return YES;
}
@end

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict/>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,49 @@
//
// LayoutExampleNodes.h
// Sample
//
// Created by Hannah Troisi on 9/13/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface LayoutExampleNode : ASDisplayNode
- (NSAttributedString *)usernameAttributedStringWithFontSize:(CGFloat)size;
- (NSAttributedString *)locationAttributedStringWithFontSize:(CGFloat)size;
- (NSAttributedString *)uploadDateAttributedStringWithFontSize:(CGFloat)size;
- (NSAttributedString *)likesAttributedStringWithFontSize:(CGFloat)size;
- (NSAttributedString *)descriptionAttributedStringWithFontSize:(CGFloat)size;
@end
@interface HorizontalStackWithSpacer : LayoutExampleNode
@property (nonatomic, strong) ASTextNode *usernameNode;
@property (nonatomic, strong) ASTextNode *postLocationNode;
@property (nonatomic, strong) ASTextNode *postTimeNode;
@end
@interface PhotoWithInsetTextOverlay : LayoutExampleNode
@property (nonatomic, strong) ASNetworkImageNode *photoNode;
@property (nonatomic, strong) ASTextNode *titleNode;
@end
@interface PhotoWithOutsetIconOverlay : LayoutExampleNode
@property (nonatomic, strong) ASNetworkImageNode *photoNode;
@property (nonatomic, strong) ASNetworkImageNode *iconNode;
@end
@interface FlexibleSeparatorSurroundingContent : LayoutExampleNode
@property (nonatomic, strong) ASImageNode *topSeparator;
@property (nonatomic, strong) ASImageNode *bottomSeparator;
@property (nonatomic, strong) ASTextNode *textNode;
@end

View File

@@ -0,0 +1,272 @@
//
// LayoutExampleNodes.m
// Sample
//
// Created by Hannah Troisi on 9/13/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "LayoutExampleNodes.h"
#import "Utilities.h"
#import "UIImage+ASConvenience.h"
#define USER_IMAGE_HEIGHT 60
#define HORIZONTAL_BUFFER 10
#define VERTICAL_BUFFER 5
#define FONT_SIZE 20
@implementation LayoutExampleNode
- (instancetype)init
{
self = [super init];
if (self) {
self.automaticallyManagesSubnodes = YES;
self.shouldVisualizeLayoutSpecs = NO;
self.shouldCacheLayoutSpec = NO;
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
#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:@"hannahmbanana check out this cool pic from the internet!"];
NSAttributedString *attrString = [NSAttributedString attributedStringWithString:string
fontSize:size
color:[UIColor darkGrayColor]
firstWordColor:[UIColor darkBlueColor]];
return attrString;
}
@end
@implementation HorizontalStackWithSpacer
- (instancetype)init
{
self = [super init];
if (self) {
_usernameNode = [[ASTextNode alloc] init];
_usernameNode.attributedText = [self usernameAttributedStringWithFontSize:FONT_SIZE];
_postLocationNode = [[ASTextNode alloc] init];
_postLocationNode.maximumNumberOfLines = 1;
_postLocationNode.attributedText = [self locationAttributedStringWithFontSize:FONT_SIZE];
_postTimeNode = [[ASTextNode alloc] init];
_postTimeNode.attributedText = [self uploadDateAttributedStringWithFontSize:FONT_SIZE];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_usernameNode.style.flexShrink = 1.0;
_postLocationNode.style.flexShrink = 1.0;
ASStackLayoutSpec *verticalStackSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackSpec.style.flexShrink = 1.0;
// Example: see ASDKgram for how this technique can be used to animate in the location label
// once a separate network request provides the data.
if (_postLocationNode.attributedText) {
[verticalStackSpec setChildren:@[_usernameNode, _postLocationNode]];
} else {
[verticalStackSpec setChildren:@[_usernameNode]];
}
ASLayoutSpec *spacerSpec = [[ASLayoutSpec alloc] init];
spacerSpec.style.flexGrow = 1.0;
spacerSpec.style.flexShrink = 1.0;
// horizontal stack
ASStackLayoutSpec *horizontalStackSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
horizontalStackSpec.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horiz stack
horizontalStackSpec.justifyContent = ASStackLayoutJustifyContentStart; // justify content to left
horizontalStackSpec.style.flexShrink = 1.0;
horizontalStackSpec.style.flexGrow = 1.0;
[horizontalStackSpec setChildren:@[verticalStackSpec, spacerSpec, _postTimeNode]];
// inset horizontal stack
UIEdgeInsets insets = UIEdgeInsetsMake(0, 10, 0, 10);
ASInsetLayoutSpec *headerInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:horizontalStackSpec];
headerInsetSpec.style.flexShrink = 1.0;
headerInsetSpec.style.flexGrow = 1.0;
return headerInsetSpec;
}
@end
@implementation PhotoWithInsetTextOverlay
- (instancetype)init
{
self = [super init];
if (self) {
_photoNode = [[ASNetworkImageNode alloc] init];
_photoNode.URL = [NSURL URLWithString:@"http://asyncdisplaykit.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png"];
_titleNode = [[ASTextNode alloc] init];
_titleNode.maximumNumberOfLines = 2;
_titleNode.truncationAttributedText = [NSAttributedString attributedStringWithString:@"..."
fontSize:16
color:[UIColor whiteColor]
firstWordColor:nil];
_titleNode.attributedText = [NSAttributedString attributedStringWithString:@"family fall hikes"
fontSize:16
color:[UIColor whiteColor]
firstWordColor:nil];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_photoNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);
UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets
child:_titleNode];
ASOverlayLayoutSpec *textOverlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_photoNode
overlay:textInsetSpec];
return textOverlaySpec;
}
@end
@implementation PhotoWithOutsetIconOverlay
- (instancetype)init
{
self = [super init];
if (self) {
_photoNode = [[ASNetworkImageNode alloc] init];
_photoNode.URL = [NSURL URLWithString:@"http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png"];
_iconNode = [[ASNetworkImageNode alloc] init];
_iconNode.URL = [NSURL URLWithString:@"http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png"];
[_iconNode setImageModificationBlock:^UIImage *(UIImage *image) { // FIXME: in framework autocomplete for setImageModificationBlock line seems broken
CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
return [image makeCircularImageWithSize:profileImageSize withBorderWidth:10];
}];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_iconNode.style.preferredSize = CGSizeMake(40, 40);
_photoNode.style.preferredSize = CGSizeMake(150, 150);
CGFloat x = 150;
CGFloat y = 0;
_iconNode.style.layoutPosition = CGPointMake(x, y);
_photoNode.style.layoutPosition = CGPointMake(40 / 2.0, 40 / 2.0);
ASAbsoluteLayoutSpec *absoluteLayoutSpec = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[_photoNode, _iconNode]];
return absoluteLayoutSpec;
}
@end
@implementation FlexibleSeparatorSurroundingContent
- (instancetype)init
{
self = [super init];
if (self) {
self.backgroundColor = [UIColor cyanColor];
_topSeparator = [[ASImageNode alloc] init];
_topSeparator.image = [UIImage as_resizableRoundedImageWithCornerRadius:1.0
cornerColor:[UIColor blackColor]
fillColor:[UIColor blackColor]];
_textNode = [[ASTextNode alloc] init];
_textNode.attributedText = [NSAttributedString attributedStringWithString:@"this is a long text node"
fontSize:16
color:[UIColor blackColor]
firstWordColor:nil];
_bottomSeparator = [[ASImageNode alloc] init];
_bottomSeparator.image = [UIImage as_resizableRoundedImageWithCornerRadius:1.0
cornerColor:[UIColor blackColor]
fillColor:[UIColor blackColor]];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_topSeparator.style.flexGrow = 1.0;
_bottomSeparator.style.flexGrow = 1.0;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(10, 10, 10, 10);
ASInsetLayoutSpec *insetContentSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentInsets
child:_textNode];
// final vertical stack
ASStackLayoutSpec *verticalStackSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackSpec.direction = ASStackLayoutDirectionVertical;
verticalStackSpec.justifyContent = ASStackLayoutJustifyContentCenter;
verticalStackSpec.alignItems = ASStackLayoutAlignItemsStretch;
verticalStackSpec.children = @[_topSeparator, insetContentSpec, _bottomSeparator];
return verticalStackSpec;
}
@end

View File

@@ -0,0 +1,15 @@
//
// PhotoPostNode.h
// ASLayoutSpecPlayground
//
// Created by Hannah Troisi on 3/11/16.
// Copyright © 2016 Hannah Troisi. All rights reserved.
//
#import "AsyncDisplayKit.h"
@interface PhotoPostNode : ASDisplayNode
- (instancetype)initWithIndex:(NSUInteger)index;
@end

View File

@@ -0,0 +1,249 @@
//
// PhotoPostNode.m
// ASLayoutSpecPlayground
//
// Created by Hannah Troisi on 3/11/16.
// Copyright © 2016 Hannah Troisi. All rights reserved.
//
#import "PhotoPostNode.h"
#import "AsyncDisplayKit+Debug.h"
#import "Utilities.h"
#define USER_IMAGE_HEIGHT 60
#define HORIZONTAL_BUFFER 10
#define VERTICAL_BUFFER 5
#define FONT_SIZE 20
@implementation PhotoPostNode
{
NSUInteger _index;
ASNetworkImageNode *_userAvatarImageView;
ASNetworkImageNode *_photoImageView;
ASTextNode *_userNameLabel;
ASTextNode *_photoLocationLabel;
ASTextNode *_photoTimeIntervalSincePostLabel;
ASTextNode *_photoLikesLabel;
ASTextNode *_photoDescriptionLabel;
}
#pragma mark - Lifecycle
- (instancetype)initWithIndex:(NSUInteger)index
{
self = [super init];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.automaticallyManagesSubnodes = YES;
self.shouldVisualizeLayoutSpecs = YES;
self.shouldCacheLayoutSpec = YES;
_index = index;
_userAvatarImageView = [[ASNetworkImageNode alloc] init];
_userAvatarImageView.URL = [NSURL URLWithString:@"https://s-media-cache-ak0.pinimg.com/avatars/503h_1458880322_140.jpg"];
[_userAvatarImageView setImageModificationBlock:^UIImage *(UIImage *image) { // FIXME: in framework autocomplete for setImageModificationBlock line seems broken
CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
return [image makeCircularImageWithSize:profileImageSize withBorderWidth:0];
}];
_userNameLabel = [[ASTextNode alloc] init];
_userNameLabel.attributedText = [self usernameAttributedStringWithFontSize:FONT_SIZE];
_photoLocationLabel = [[ASTextNode alloc] init];
_photoLocationLabel.maximumNumberOfLines = 1;
_photoLocationLabel.attributedText = [self locationAttributedStringWithFontSize:FONT_SIZE];
_photoTimeIntervalSincePostLabel = [[ASTextNode alloc] init];
_photoTimeIntervalSincePostLabel.attributedText = [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.attributedText = [self likesAttributedStringWithFontSize:FONT_SIZE];
_photoDescriptionLabel = [[ASTextNode alloc] init];
_photoDescriptionLabel.attributedText = [self descriptionAttributedStringWithFontSize:FONT_SIZE];
_photoDescriptionLabel.maximumNumberOfLines = 3;
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
switch (_index) {
case 1:
return [self layoutSpecThatFitsNavBar:constrainedSize];
case 2:
return [self layoutSpecThatFitsASDKgram:constrainedSize];
default:
return [self layoutSpecThatFitsASDKgram:constrainedSize];
break;
}
}
- (ASLayoutSpec *)layoutSpecThatFitsNavBar:(ASSizeRange)constrainedSize
{
// username / photo location header vertical stack
_userNameLabel.style.flexShrink = 1.0;
_photoLocationLabel.style.flexShrink = 1.0;
ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec];
headerSubStack.style.flexShrink = 1.0;
if (_photoLocationLabel.attributedText) {
[headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]];
} else {
[headerSubStack setChildren:@[_userNameLabel]];
}
// header stack
_userAvatarImageView.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
_photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER; // hack to remove double spaces around spacer
UIEdgeInsets avatarInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageView];
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.style.flexGrow = 1.0;
spacer.style.flexShrink = 1.0; // FIXME: this overrides stuff :) THIS IS A SYSTEMIC ISSUE - can we make layoutSpecThatFits only run once? cache layoutSpec, just use new constrainedSize, don't put properties in layoutSpecThatFits
// separate the idea of laying out and rerunning with new constrainedSize
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.style.flexShrink = 1.0;
headerStack.style.flexGrow = 1.0;
[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.style.flexShrink = 1.0;
headerWithInset.style.flexGrow = 1.0;
return headerWithInset;
}
- (ASLayoutSpec *)layoutSpecThatFitsASDKgram:(ASSizeRange)constrainedSize
{
// username / photo location header vertical stack
_userNameLabel.style.flexShrink = 1.0;
_photoLocationLabel.style.flexShrink = 1.0;
ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec];
headerSubStack.style.flexShrink = 1.0;
if (_photoLocationLabel.attributedText) {
[headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]];
} else {
[headerSubStack setChildren:@[_userNameLabel]];
}
// header stack
_userAvatarImageView.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
_photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER; // hack to remove double spaces around spacer
UIEdgeInsets avatarInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageView];
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.style.flexGrow = 1.0;
spacer.style.flexShrink = 1.0; // FIXME: this overrides stuff :) THIS IS A SYSTEMIC ISSUE - can we make layoutSpecThatFits only run once? cache layoutSpec, just use new constrainedSize, don't put properties in layoutSpecThatFits
// separate the idea of laying out and rerunning with new constrainedSize
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.style.flexShrink = 1.0;
headerStack.style.flexGrow = 1.0;
[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.style.flexShrink = 1.0;
headerWithInset.style.flexGrow = 1.0;
// 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
ASRatioLayoutSpec *photoRatioSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:_photoImageView];
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // sretch headerStack to fill horizontal space
[verticalStack setChildren:@[headerWithInset, photoRatioSpec, footerWithInset]];
verticalStack.style.flexShrink = 1.0;
return verticalStack;
}
#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:@"hannahmbanana check out this cool pic from the internet!"];
NSAttributedString *attrString = [NSAttributedString attributedStringWithString:string
fontSize:size
color:[UIColor darkGrayColor]
firstWordColor:[UIColor darkBlueColor]];
return attrString;
}
@end

View File

@@ -0,0 +1,24 @@
//
// PlaygroundContainerNode.h
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@protocol PlaygroundContainerNodeDelegate <NSObject>
- (void)relayoutWithSize:(ASSizeRange)size;
@end
@interface PlaygroundContainerNode : ASCellNode
@property (nonatomic, weak) id<PlaygroundContainerNodeDelegate> delegate;
+ (NSUInteger)containerNodeCount;
- (instancetype)initWithIndex:(NSUInteger)index;
@end

View File

@@ -0,0 +1,124 @@
//
// PlaygroundContainerNode.m
// Sample
//
// Created by Hannah Troisi on 3/19/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "PlaygroundContainerNode.h"
#import "LayoutExampleNodes.h"
#import "PhotoPostNode.h"
#import <AsyncDisplayKit/ASLayoutElementInspectorNode.h>
#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
#define RESIZE_HANDLE_SIZE 30
@implementation PlaygroundContainerNode
{
ASDisplayNode *_playgroundNode;
ASImageNode *_resizeHandle;
CGPoint _resizeStartLocation;
}
#pragma mark - Lifecycle
+ (NSUInteger)containerNodeCount
{
return 5;
}
+ (ASDisplayNode *)nodeForIndex:(NSUInteger)index
{
switch (index) {
case 0: return [[HorizontalStackWithSpacer alloc] init];
case 1: return [[PhotoWithInsetTextOverlay alloc] init];
case 2: return [[PhotoWithOutsetIconOverlay alloc] init];
case 3: return [[FlexibleSeparatorSurroundingContent alloc] init];
case 4: return [[PhotoPostNode alloc] initWithIndex:0];
default: return [[PhotoPostNode alloc] initWithIndex:1];
}
}
- (instancetype)initWithIndex:(NSUInteger)index
{
self = [super init];
if (self) {
self.backgroundColor = [UIColor whiteColor]; //[UIColor colorWithRed:255/255.0 green:181/255.0 blue:68/255.0 alpha:1];
self.automaticallyManagesSubnodes = YES;
_playgroundNode = [[self class] nodeForIndex:index];
_resizeHandle = [[ASImageNode alloc] init];
_resizeHandle.image = [UIImage imageNamed:@"resizeHandle"];
_resizeHandle.userInteractionEnabled = YES;
// [self addSubnode:_resizeHandle];
[ASLayoutElementInspectorNode sharedInstance].style.flexBasis = ASDimensionMakeWithFraction(1.0);
[ASLayoutElementInspectorNode sharedInstance].vizNodeInsetSize = 10.0;
self.shouldVisualizeLayoutSpecs = NO;
self.shouldCacheLayoutSpec = NO;
}
return self;
}
- (void)didLoad
{
[super didLoad];
UIPanGestureRecognizer *gr = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(resizePlayground:)];
[_resizeHandle.view addGestureRecognizer:gr];
}
// manually layout _resizeHandle // FIXME: add this to an overlayStack in layoutSpecThatFits?
- (void)layout
{
[super layout];
[self.view bringSubviewToFront:_resizeHandle.view];
CGSize playgroundSize = _playgroundNode.calculatedLayout.size;
CGRect rect = CGRectZero;
rect.size = CGSizeMake(RESIZE_HANDLE_SIZE, RESIZE_HANDLE_SIZE);
rect.origin = CGPointMake(playgroundSize.width - rect.size.width, playgroundSize.height - rect.size.height);
_resizeHandle.frame = rect;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_playgroundNode.style.flexGrow = 1.0;
_playgroundNode.style.flexShrink = 1.0;
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10)
child:_playgroundNode];
}
#pragma mark - Gesture Handling
- (void)resizePlayground:(UIPanGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan) {
_resizeStartLocation = [sender locationInView:sender.view];
}
else if (sender.state == UIGestureRecognizerStateChanged) {
CGPoint location = [sender locationInView:sender.view];
CGPoint translation = CGPointMake(location.x - _resizeStartLocation.x, location.y - _resizeStartLocation.y);
[self changePlaygroundFrameWithTranslation:translation];
}
else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) {
_resizeStartLocation = CGPointZero;
}
}
- (void)changePlaygroundFrameWithTranslation:(CGPoint)translation
{
ASSizeRange constrainedSize = self.constrainedSizeForCalculatedLayout;
constrainedSize.max.width = MAX(0, constrainedSize.max.width + translation.x);
constrainedSize.max.height = MAX(0, constrainedSize.max.height + translation.y);
[self.delegate relayoutWithSize:constrainedSize];
}
@end

View File

@@ -0,0 +1,32 @@
//
// 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;
+ (UIColor *)duskColor;
+ (UIColor *)customOrangeColor;
@end
@interface UIImage (Additions)
- (UIImage *)makeCircularImageWithSize:(CGSize)size withBorderWidth:(CGFloat)width;
@end
@interface NSAttributedString (Additions)
+ (NSAttributedString *)attributedStringWithString:(NSString *)string fontSize:(CGFloat)size
color:(UIColor *)color firstWordColor:(UIColor *)firstWordColor;
@end

View File

@@ -0,0 +1,98 @@
//
// 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];
}
+ (UIColor *)duskColor
{
return [UIColor colorWithRed:255/255.0 green:181/255.0 blue:68/255.0 alpha:1];
}
+ (UIColor *)customOrangeColor
{
return [UIColor colorWithRed:40/255.0 green:43/255.0 blue:53/255.0 alpha:1.0];
}
@end
@implementation UIImage (Additions)
- (UIImage *)makeCircularImageWithSize:(CGSize)size withBorderWidth:(CGFloat)width
{
// 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];
[[UIColor whiteColor] set];
[circle fill];
// draw the image in the circleRect *AFTER* the context is clipped
[self drawInRect:circleRect];
// create a border (for white background pictures)
if (width > 0) {
circle.lineWidth = width;
[[UIColor whiteColor] set];
[circle stroke];
}
// 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 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 boldSystemFontOfSize: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

View File

@@ -0,0 +1,14 @@
//
// ViewController.h
// ASLayoutSpecPlayground
//
// Created by Hannah Troisi on 3/11/16.
// Copyright © 2016 Hannah Troisi. All rights reserved.
//
#import "AsyncDisplayKit.h"
@interface ViewController : ASViewController
@end

View File

@@ -0,0 +1,92 @@
//
// ViewController.m
// ASLayoutSpecPlayground
//
// Created by Hannah Troisi on 3/11/16.
// Copyright © 2016 Hannah Troisi. All rights reserved.
//
#import "ViewController.h"
#import "PlaygroundContainerNode.h"
#import "ASLayoutElementInspectorNode.h"
@interface ViewController () <ASPagerDelegate, ASPagerDataSource, ASLayoutElementInspectorNodeDelegate, PlaygroundContainerNodeDelegate>
@end
@implementation ViewController
{
ASPagerNode *_pagerNode;
ASSizeRange _sizeRange;
}
#pragma mark - Lifecycle
- (instancetype)init
{
_pagerNode = [[ASPagerNode alloc] init];
self = [super initWithNode:_pagerNode];
if (self) {
_pagerNode.delegate = self;
_pagerNode.dataSource = self;
self.navigationItem.title = @"ASLayoutSpec Playground";
self.edgesForExtendedLayout = UIRectEdgeNone;
[ASLayoutElementInspectorNode sharedInstance].delegate = self;
}
return self;
}
#pragma mark - ASPagerNodeDataSource
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode
{
return [PlaygroundContainerNode containerNodeCount];
}
- (ASCellNodeBlock)pagerNode:(ASPagerNode *)pagerNode nodeBlockAtIndex:(NSInteger)index
{
return ^{
PlaygroundContainerNode *containerCellNode = [[PlaygroundContainerNode alloc] initWithIndex:index];
containerCellNode.delegate = self;
return containerCellNode;
};
}
// [ASViewController] Override this method to provide a custom size range to the backing node.
// Neccessary to allow the user to stretch / shrink the size of playground container.
- (ASSizeRange)nodeConstrainedSize
{
if (CGSizeEqualToSize(_sizeRange.max, CGSizeZero)) {
return [super nodeConstrainedSize];
}
return _sizeRange;
}
- (ASSizeRange)pagerNode:(ASPagerNode *)pagerNode constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
if (CGSizeEqualToSize(_sizeRange.max, CGSizeZero)) {
return [super nodeConstrainedSize];
}
return _sizeRange;
}
#pragma mark - PlaygroundContainerNodeDelegate
- (void)relayoutWithSize:(ASSizeRange)size
{
// NSLog(@"DELEGATE constrainedSize = %@", NSStringFromCGSize(size.max));
_sizeRange = size;
[self.view setNeedsLayout];
[_pagerNode reloadData];
}
#pragma mark - ASLayoutElementInspectorNodeDelegate
- (void)toggleVisualization:(BOOL)toggle
{
NSLog(@"shouldVisualizeLayoutSpecs:%d", toggle);
[self.node setShouldVisualizeLayoutSpecs:toggle];
}
@end

View File

@@ -0,0 +1,20 @@
/* This file provided by Facebook is for non-commercial testing and evaluation
* purposes only. Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B