From 8b0dbf72886893f09e64ab797f76f59ba2c81b43 Mon Sep 17 00:00:00 2001 From: Ryan Nystrom Date: Tue, 25 Nov 2014 13:33:40 -0800 Subject: [PATCH] Manually display nodes Added a sample project that will demonstrate how to manually display nodes. Removed the UIWindow hack that coupled display of nodes with Core Animation transactions. --- .gitignore | 2 + AsyncDisplayKit/ASDisplayNode.h | 7 + AsyncDisplayKit/ASDisplayNode.mm | 21 + AsyncDisplayKit/Details/ASRangeController.mm | 36 +- examples/WaitWaitDontShowMe/Podfile | 3 + .../Sample.xcodeproj/project.pbxproj | 377 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../contents.xcworkspacedata | 10 + .../WaitWaitDontShowMe/Sample/AppDelegate.h | 20 + .../WaitWaitDontShowMe/Sample/AppDelegate.m | 48 +++ .../Sample/Base.lproj/LaunchScreen.xib | 34 ++ .../Sample/CheapController.h | 16 + .../Sample/CheapController.m | 46 +++ .../Sample/ExpensiveController.h | 18 + .../Sample/ExpensiveController.m | 123 ++++++ .../AppIcon.appiconset/Contents.json | 68 ++++ .../cheap.imageset/Contents.json | 23 ++ .../Images.xcassets/cheap.imageset/cheap.png | Bin 0 -> 1265 bytes .../cheap.imageset/cheap@2x.png | Bin 0 -> 1745 bytes .../cheap.imageset/cheap@3x.png | Bin 0 -> 1530 bytes .../expensive.imageset/Contents.json | 23 ++ .../expensive.imageset/expensive.png | Bin 0 -> 1634 bytes .../expensive.imageset/expensive@2x.png | Bin 0 -> 2610 bytes .../expensive.imageset/expensive@3x.png | Bin 0 -> 1645 bytes examples/WaitWaitDontShowMe/Sample/Info.plist | 45 +++ .../Sample/PlaceholderTextNode.h | 16 + .../Sample/PlaceholderTextNode.m | 58 +++ examples/WaitWaitDontShowMe/Sample/PostNode.h | 18 + examples/WaitWaitDontShowMe/Sample/PostNode.m | 138 +++++++ .../WaitWaitDontShowMe/Sample/ShareNode.h | 16 + .../WaitWaitDontShowMe/Sample/ShareNode.m | 72 ++++ examples/WaitWaitDontShowMe/Sample/main.m | 19 + 32 files changed, 1240 insertions(+), 24 deletions(-) create mode 100644 examples/WaitWaitDontShowMe/Podfile create mode 100644 examples/WaitWaitDontShowMe/Sample.xcodeproj/project.pbxproj create mode 100644 examples/WaitWaitDontShowMe/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/WaitWaitDontShowMe/Sample.xcworkspace/contents.xcworkspacedata create mode 100644 examples/WaitWaitDontShowMe/Sample/AppDelegate.h create mode 100644 examples/WaitWaitDontShowMe/Sample/AppDelegate.m create mode 100644 examples/WaitWaitDontShowMe/Sample/Base.lproj/LaunchScreen.xib create mode 100644 examples/WaitWaitDontShowMe/Sample/CheapController.h create mode 100644 examples/WaitWaitDontShowMe/Sample/CheapController.m create mode 100644 examples/WaitWaitDontShowMe/Sample/ExpensiveController.h create mode 100644 examples/WaitWaitDontShowMe/Sample/ExpensiveController.m create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/Contents.json create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap.png create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap@2x.png create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap@3x.png create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/expensive.imageset/Contents.json create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/expensive.imageset/expensive.png create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/expensive.imageset/expensive@2x.png create mode 100644 examples/WaitWaitDontShowMe/Sample/Images.xcassets/expensive.imageset/expensive@3x.png create mode 100644 examples/WaitWaitDontShowMe/Sample/Info.plist create mode 100644 examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.h create mode 100644 examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.m create mode 100644 examples/WaitWaitDontShowMe/Sample/PostNode.h create mode 100644 examples/WaitWaitDontShowMe/Sample/PostNode.m create mode 100644 examples/WaitWaitDontShowMe/Sample/ShareNode.h create mode 100644 examples/WaitWaitDontShowMe/Sample/ShareNode.m create mode 100644 examples/WaitWaitDontShowMe/Sample/main.m diff --git a/.gitignore b/.gitignore index 23da184da8..f14b49ea6b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ docs/htdocs docs/.sass-cache *.swp + +*.lock diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 481d493f53..269c5a5530 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -294,6 +294,13 @@ */ @property (nonatomic, assign) BOOL shouldRasterizeDescendants; +/** + * @abstract Calls -setNeedsDisplay and -displayIfNeeded on the node's backing store and recursively calls -display on + * all subnodes forcing the entire node hierarchy to be displayed. This method must be called on the main thread but + * there are plans to allow this on any thread. + */ +- (void)display; + /** * @abstract Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index a797ae2139..0165011a76 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -437,6 +437,27 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) _contentsScaleForDisplay = contentsScaleForDisplay; } +- (void)display +{ + ASDisplayNodeAssertMainThread(); + + // rendering a backing store requires a node be laid out + [self layout]; + + for (ASDisplayNode *node in self.subnodes) { + [node display]; + } + + CALayer *layer = [self isLayerBacked] ? self.layer : self.view.layer; + + if (layer.contents) { + return; + } + + [layer setNeedsDisplay]; + [layer displayIfNeeded]; +} + - (void)displayImmediately { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 0526ae78c7..52e25c631e 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -91,22 +91,6 @@ return sizingQueue; } -+ (UIView *)workingView -{ - // we add nodes' views to this invisible window to start async rendering - static UIWindow *workingWindow = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - workingWindow = [[UIWindow alloc] initWithFrame:CGRectZero]; - workingWindow.windowLevel = UIWindowLevelNormal - 1000; - workingWindow.userInteractionEnabled = NO; - workingWindow.clipsToBounds = YES; - workingWindow.hidden = YES; - }); - - return workingWindow; -} - #pragma mark - #pragma mark Helpers. @@ -191,7 +175,7 @@ static BOOL ASRangeIsValid(NSRange range) NSInteger index = [self indexForIndexPath:node.asyncdisplaykit_indexPath]; if (NSLocationInRange(index, _workingRange)) { // move the node's view to the working range area, so its rendering persists - [self moveNodeToWorkingView:node]; + [self moveNodeToWorkingRange:node]; } else { // this node isn't in the working range, remove it from the view hierarchy [self removeNodeFromWorkingView:node]; @@ -205,20 +189,22 @@ static BOOL ASRangeIsValid(NSRange range) [node recursiveSetPreventOrCancelDisplay:YES]; [node.view removeFromSuperview]; - + // since this class usually manages large or infinite data sets, the working range // directly bounds memory usage by requiring redrawing any content that falls outside the range. [node recursivelyReclaimMemory]; - + [_workingIndexPaths removeObject:node.asyncdisplaykit_indexPath]; } -- (void)moveNodeToWorkingView:(ASCellNode *)node +- (void)moveNodeToWorkingRange:(ASCellNode *)node { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(node, @"invalid argument"); - [self moveNode:node toView:[ASRangeController workingView]]; + // if node is in the working range it should not actively be in view + [node.view removeFromSuperview]; + [_workingIndexPaths addObject:node.asyncdisplaykit_indexPath]; } @@ -231,7 +217,7 @@ static BOOL ASRangeIsValid(NSRange range) [CATransaction begin]; [view addSubview:node.view]; - + [CATransaction commit]; } @@ -481,7 +467,7 @@ static NSRange ASCalculateWorkingRange(ASRangeTuningParameters params, ASScrollD _nodeSizes, [_delegate rangeControllerViewportSize:self]); } - + [self setWorkingRange:workingRange]; } @@ -513,7 +499,7 @@ static NSRange ASCalculateWorkingRange(ASRangeTuningParameters params, ASScrollD // if a node in the working range is still sizing, the sizing logic will add it to the working range for us later ASCellNode *node = [self sizedNodeForIndexPath:indexPath]; if (node) { - [self moveNodeToWorkingView:node]; + [self moveNodeToWorkingRange:node]; } else { ASDisplayNodeAssert(_sizedNodeCount != _totalNodeCount, @"logic error"); } @@ -577,6 +563,8 @@ static NSRange ASCalculateWorkingRange(ASRangeTuningParameters params, ASScrollD for (NSIndexPath *indexPath in indexPaths) { ASCellNode *node = _nodes[indexPath]; _nodeSizes[[self indexForIndexPath:indexPath]] = [NSValue valueWithCGSize:node.calculatedSize]; + + [node display]; } ASDisplayNodeAssert(_nodeSizes.count == _sizedNodeCount, @"logic error"); diff --git a/examples/WaitWaitDontShowMe/Podfile b/examples/WaitWaitDontShowMe/Podfile new file mode 100644 index 0000000000..6c012e3c04 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Podfile @@ -0,0 +1,3 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +pod 'AsyncDisplayKit', :path => '../..' diff --git a/examples/WaitWaitDontShowMe/Sample.xcodeproj/project.pbxproj b/examples/WaitWaitDontShowMe/Sample.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..fca9a7e9b7 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,377 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2930E8401A1FCA890032D7B3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2930E83F1A1FCA890032D7B3 /* main.m */; }; + 2930E8431A1FCA890032D7B3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2930E8421A1FCA890032D7B3 /* AppDelegate.m */; }; + 2930E8451A1FCA890032D7B3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2930E8441A1FCA890032D7B3 /* Images.xcassets */; }; + 2930E8481A1FCA890032D7B3 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2930E8461A1FCA890032D7B3 /* LaunchScreen.xib */; }; + 2930E85F1A1FCABC0032D7B3 /* ExpensiveController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2930E85E1A1FCABC0032D7B3 /* ExpensiveController.m */; }; + 2930E8621A1FF18A0032D7B3 /* PostNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 2930E8611A1FF18A0032D7B3 /* PostNode.m */; }; + 2930E8651A1FF58A0032D7B3 /* ShareNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 2930E8641A1FF58A0032D7B3 /* ShareNode.m */; }; + 2983606D1A200AE50022B4F8 /* PlaceholderTextNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 2983606C1A200AE50022B4F8 /* PlaceholderTextNode.m */; }; + 29CF6DDF1A250F960031DF86 /* CheapController.m in Sources */ = {isa = PBXBuildFile; fileRef = 29CF6DDE1A250F960031DF86 /* CheapController.m */; }; + 7A7B2A305C39C4CA59331E10 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DABEBB92D527C244C86ADF06 /* libPods.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2930E83A1A1FCA890032D7B3 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 2930E83E1A1FCA890032D7B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2930E83F1A1FCA890032D7B3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 2930E8411A1FCA890032D7B3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 2930E8421A1FCA890032D7B3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 2930E8441A1FCA890032D7B3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 2930E8471A1FCA890032D7B3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 2930E85D1A1FCABC0032D7B3 /* ExpensiveController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExpensiveController.h; sourceTree = ""; }; + 2930E85E1A1FCABC0032D7B3 /* ExpensiveController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExpensiveController.m; sourceTree = ""; }; + 2930E8601A1FF18A0032D7B3 /* PostNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostNode.h; sourceTree = ""; }; + 2930E8611A1FF18A0032D7B3 /* PostNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostNode.m; sourceTree = ""; }; + 2930E8631A1FF58A0032D7B3 /* ShareNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareNode.h; sourceTree = ""; }; + 2930E8641A1FF58A0032D7B3 /* ShareNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShareNode.m; sourceTree = ""; }; + 2983606B1A200AE50022B4F8 /* PlaceholderTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaceholderTextNode.h; sourceTree = ""; }; + 2983606C1A200AE50022B4F8 /* PlaceholderTextNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaceholderTextNode.m; sourceTree = ""; }; + 29CF6DDD1A250F960031DF86 /* CheapController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CheapController.h; sourceTree = ""; }; + 29CF6DDE1A250F960031DF86 /* CheapController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CheapController.m; sourceTree = ""; }; + AC624CB0EACE6790C185F790 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + BFA8076C256CAC9E6E153731 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + DABEBB92D527C244C86ADF06 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2930E8371A1FCA890032D7B3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A7B2A305C39C4CA59331E10 /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2930E8311A1FCA890032D7B3 = { + isa = PBXGroup; + children = ( + 2930E83C1A1FCA890032D7B3 /* Sample */, + 2930E83B1A1FCA890032D7B3 /* Products */, + E4382CEF3F9728AD310690EE /* Pods */, + 3AD89B2B22B71B86CDE10916 /* Frameworks */, + ); + sourceTree = ""; + }; + 2930E83B1A1FCA890032D7B3 /* Products */ = { + isa = PBXGroup; + children = ( + 2930E83A1A1FCA890032D7B3 /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 2930E83C1A1FCA890032D7B3 /* Sample */ = { + isa = PBXGroup; + children = ( + 2930E8411A1FCA890032D7B3 /* AppDelegate.h */, + 2930E8421A1FCA890032D7B3 /* AppDelegate.m */, + 2930E8441A1FCA890032D7B3 /* Images.xcassets */, + 2930E8461A1FCA890032D7B3 /* LaunchScreen.xib */, + 2930E83D1A1FCA890032D7B3 /* Supporting Files */, + 2930E85D1A1FCABC0032D7B3 /* ExpensiveController.h */, + 2930E85E1A1FCABC0032D7B3 /* ExpensiveController.m */, + 29CF6DDD1A250F960031DF86 /* CheapController.h */, + 29CF6DDE1A250F960031DF86 /* CheapController.m */, + 2930E8601A1FF18A0032D7B3 /* PostNode.h */, + 2930E8611A1FF18A0032D7B3 /* PostNode.m */, + 2983606B1A200AE50022B4F8 /* PlaceholderTextNode.h */, + 2983606C1A200AE50022B4F8 /* PlaceholderTextNode.m */, + 2930E8631A1FF58A0032D7B3 /* ShareNode.h */, + 2930E8641A1FF58A0032D7B3 /* ShareNode.m */, + ); + path = Sample; + sourceTree = ""; + }; + 2930E83D1A1FCA890032D7B3 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 2930E83E1A1FCA890032D7B3 /* Info.plist */, + 2930E83F1A1FCA890032D7B3 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 3AD89B2B22B71B86CDE10916 /* Frameworks */ = { + isa = PBXGroup; + children = ( + DABEBB92D527C244C86ADF06 /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + E4382CEF3F9728AD310690EE /* Pods */ = { + isa = PBXGroup; + children = ( + BFA8076C256CAC9E6E153731 /* Pods.debug.xcconfig */, + AC624CB0EACE6790C185F790 /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 2930E8391A1FCA890032D7B3 /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2930E8571A1FCA890032D7B3 /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + 21550E146D80CC7748448A11 /* Check Pods Manifest.lock */, + 2930E8361A1FCA890032D7B3 /* Sources */, + 2930E8371A1FCA890032D7B3 /* Frameworks */, + 2930E8381A1FCA890032D7B3 /* Resources */, + 15832E3E36BB198D0274923F /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 2930E83A1A1FCA890032D7B3 /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 2930E8321A1FCA890032D7B3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = "Ryan Nystrom"; + TargetAttributes = { + 2930E8391A1FCA890032D7B3 = { + CreatedOnToolsVersion = 6.1; + }; + }; + }; + buildConfigurationList = 2930E8351A1FCA890032D7B3 /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 2930E8311A1FCA890032D7B3; + productRefGroup = 2930E83B1A1FCA890032D7B3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2930E8391A1FCA890032D7B3 /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2930E8381A1FCA890032D7B3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2930E8481A1FCA890032D7B3 /* LaunchScreen.xib in Resources */, + 2930E8451A1FCA890032D7B3 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 15832E3E36BB198D0274923F /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 21550E146D80CC7748448A11 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2930E8361A1FCA890032D7B3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2930E85F1A1FCABC0032D7B3 /* ExpensiveController.m in Sources */, + 2930E8621A1FF18A0032D7B3 /* PostNode.m in Sources */, + 29CF6DDF1A250F960031DF86 /* CheapController.m in Sources */, + 2983606D1A200AE50022B4F8 /* PlaceholderTextNode.m in Sources */, + 2930E8651A1FF58A0032D7B3 /* ShareNode.m in Sources */, + 2930E8431A1FCA890032D7B3 /* AppDelegate.m in Sources */, + 2930E8401A1FCA890032D7B3 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 2930E8461A1FCA890032D7B3 /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 2930E8471A1FCA890032D7B3 /* Base */, + ); + name = LaunchScreen.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 2930E8551A1FCA890032D7B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2930E8561A1FCA890032D7B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 2930E8581A1FCA890032D7B3 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BFA8076C256CAC9E6E153731 /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 2930E8591A1FCA890032D7B3 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AC624CB0EACE6790C185F790 /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2930E8351A1FCA890032D7B3 /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2930E8551A1FCA890032D7B3 /* Debug */, + 2930E8561A1FCA890032D7B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2930E8571A1FCA890032D7B3 /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2930E8581A1FCA890032D7B3 /* Debug */, + 2930E8591A1FCA890032D7B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 2930E8321A1FCA890032D7B3 /* Project object */; +} diff --git a/examples/WaitWaitDontShowMe/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/WaitWaitDontShowMe/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..a80c038249 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/WaitWaitDontShowMe/Sample.xcworkspace/contents.xcworkspacedata b/examples/WaitWaitDontShowMe/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..7b5a2f3050 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples/WaitWaitDontShowMe/Sample/AppDelegate.h b/examples/WaitWaitDontShowMe/Sample/AppDelegate.h new file mode 100644 index 0000000000..db4bc0a921 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/AppDelegate.h @@ -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 + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/examples/WaitWaitDontShowMe/Sample/AppDelegate.m b/examples/WaitWaitDontShowMe/Sample/AppDelegate.m new file mode 100644 index 0000000000..41e998390b --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/AppDelegate.m @@ -0,0 +1,48 @@ +/* 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 "AppDelegate.h" + +#import "ExpensiveController.h" +#import "CheapController.h" + +#define AS_DEMO_PRELOADING 1 + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + CGRect screenBounds = [[UIScreen mainScreen] bounds]; + UIWindow *window = [[UIWindow alloc] initWithFrame:screenBounds]; + + CheapController *cheap = [[CheapController alloc] init]; + cheap.tabBarItem.title = @"Cheap"; + cheap.tabBarItem.image = [UIImage imageNamed:@"cheap"]; + + ExpensiveController *expensive = [[ExpensiveController alloc] init]; + expensive.tabBarItem.title = @"Expensive"; + expensive.tabBarItem.image = [UIImage imageNamed:@"expensive"]; + +#if AS_DEMO_PRELOADING + [expensive preloadForSize:screenBounds.size]; +#endif + + UITabBarController *tab = [[UITabBarController alloc] init]; + tab.viewControllers = @[cheap, expensive]; + + window.rootViewController = tab; + [window makeKeyAndVisible]; + [self setWindow:window]; + + return YES; +} + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/Base.lproj/LaunchScreen.xib b/examples/WaitWaitDontShowMe/Sample/Base.lproj/LaunchScreen.xib new file mode 100644 index 0000000000..7b3d959858 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/Base.lproj/LaunchScreen.xib @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/WaitWaitDontShowMe/Sample/CheapController.h b/examples/WaitWaitDontShowMe/Sample/CheapController.h new file mode 100644 index 0000000000..e837f84dad --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/CheapController.h @@ -0,0 +1,16 @@ +/* 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 + +@interface CheapController : UIViewController + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/CheapController.m b/examples/WaitWaitDontShowMe/Sample/CheapController.m new file mode 100644 index 0000000000..0dc612f085 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/CheapController.m @@ -0,0 +1,46 @@ +/* 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 "CheapController.h" + +@interface CheapController () +{ + UILabel *_cheapLabel; +} + +@end + +@implementation CheapController + +- (instancetype)init +{ + if (self = [super init]) { + _cheapLabel = [[UILabel alloc] init]; + _cheapLabel.text = @"I'm a cheap date."; + [_cheapLabel sizeToFit]; + } + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; + [self.view addSubview:_cheapLabel]; +} + +- (void)viewDidLayoutSubviews +{ + _cheapLabel.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)); +} + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/ExpensiveController.h b/examples/WaitWaitDontShowMe/Sample/ExpensiveController.h new file mode 100644 index 0000000000..ceee31c475 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/ExpensiveController.h @@ -0,0 +1,18 @@ +/* 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 + +@interface ExpensiveController : UIViewController + +- (void)preloadForSize:(CGSize)size; + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/ExpensiveController.m b/examples/WaitWaitDontShowMe/Sample/ExpensiveController.m new file mode 100644 index 0000000000..d6e498b90c --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/ExpensiveController.m @@ -0,0 +1,123 @@ +/* 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 "ExpensiveController.h" + +#import + +#import "PostNode.h" + +static NSUInteger const kNumberOfPosts = 20; + +@interface ExpensiveController () +{ + ASTableView *_tableView; +} + +@end + +@implementation ExpensiveController + +- (instancetype)init +{ + if (self = [super init]) { + _tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + _tableView.asyncDataSource = self; + _tableView.asyncDelegate = self; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self.view addSubview:_tableView]; + + if (self.tabBarController) { + UIEdgeInsets insets = _tableView.contentInset; + insets.bottom = CGRectGetHeight(self.tabBarController.tabBar.bounds); + _tableView.contentInset = insets; + _tableView.scrollIndicatorInsets = insets; + } +} + +- (void)viewDidLayoutSubviews +{ + CGRect bounds = self.view.bounds; + _tableView.frame = bounds; +} + +- (BOOL)prefersStatusBarHidden +{ + return YES; +} + + +#pragma mark - +#pragma mark Public Actions + +- (void)preloadForSize:(CGSize)size +{ + // without setting the frame the size is CGRectZero which will likely cause layout asserts to fail + _tableView.frame = (CGRect){ CGPointZero, size }; + [_tableView reloadData]; +} + + +#pragma mark - +#pragma mark Getters + ++ (NSString *)randomPost +{ + static NSArray *posts = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + posts = @[ + @"Nulla vitae elit libero, a **pharetra augue**. Curabitur blandit tempus porttitor. Donec sed odio dui. Donec id elit non mi porta gravida at eget metus. Donec ullamcorper nulla non metus auctor fringilla. Cras mattis consectetur purus sit amet fermentum.", + @"Donec id elit non mi porta gravida at **eget metus**. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. **Donec ullamcorper nulla** non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue. Donec id elit non mi porta gravida at eget metus.\nFusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Donec ullamcorper nulla non metus auctor fringilla.", + @"Nullam id dolor id nibh ultricies vehicula ut id **elit**. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Praesent commodo cursus magna, vel scelerisque nisl **consectetur et**.\nNullam id dolor id nibh ultricies vehicula ut id elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + @"Sed posuere consectetur est at lobortis. Etiam porta sem malesuada magna mollis euismod. Curabitur blandit tempus **porttitor**. Nullam id dolor id nibh ultricies vehicula ut id elit.\nCurabitur blandit tempus porttitor.", + @"Aenean lacinia bibendum nulla sed consectetur. Morbi leo risus, porta ac consectetur ac, **vestibulum at eros**. Vestibulum id ligula porta felis euismod semper. Nullam id dolor id nibh ultricies vehicula ut id elit.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla.", + @"Nullam quis risus eget urna mollis ornare vel eu leo.\nSed posuere consectetur est at lobortis. Fusce dapibus, tellus ac cursus **commodo**, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Nulla vitae **elit libero**, a pharetra augue.", + ]; + }); + + NSUInteger randomIndex = arc4random() % posts.count; + return posts[randomIndex]; +} + + +#pragma mark - +#pragma mark ASTableViewDataSource + +- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-indexPath.row * 60 * 60 * 24]; + NSString *text = [self.class randomPost]; + PostNode *node = [[PostNode alloc] initWithDate:date text:text]; + return node; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return kNumberOfPosts; +} + +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath +{ + return NO; +} + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..36d2c80d88 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/Contents.json b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/Contents.json new file mode 100644 index 0000000000..72f3b47af1 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "cheap.png" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "cheap@2x.png" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "cheap@3x.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap.png b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap.png new file mode 100644 index 0000000000000000000000000000000000000000..4028627950cb9604a05a434c945cc618886bc5bf GIT binary patch literal 1265 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX1|+Qw)-3{3k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuU&_u!9QqR!T z%-qskN5Kfl*Eg`xH!{#Qu(UEYwlXkMfC43;ZAB?*RzWUqP`iLUTcwPWk^(Dz{qpj1 zy>er{{GxPyLrY6beFGzXBO_g)3fZE`@j@w*YQzUNJP7fB~jokyxN_sAmB35=^15FMg%Dxp39RB|)hO_hL1;IHa;5 zRX-@TIKQ+g85nVC${?!>telHd6HD@oLh|!-V4)b0kzbNuoRMFk;OqX$Y%oHn2BR6AXb2B3|7ef<6Lst_EXIG$zp)rW< zWa?@R)9aF-T$-DjR|3Q!gk%wD&O2NjNBDb^X*NfWpeW4o#*GUSTt-0#7pBK<-MTgK z`nuT3FJ8aqwy68FL&n_v`#b;nb{i)q@tw4*`|&|>{oil3j;WNxqo!JBHf2-*0bkzqI9y-~YF7-&!Cae2@`}O_3eEX_Z zuUKcP=`B#@S-W=a!l5 i+gfoYsff{`fsx_5v%~4ll?$Cgg^;JKpUXO@geCwT#m$-k literal 0 HcmV?d00001 diff --git a/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap@2x.png b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/cheap.imageset/cheap@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b71b084345f32967f5f31a4fb0d3100487f30adf GIT binary patch literal 1745 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 DP?mLV literal 0 HcmV?d00001 diff --git a/examples/WaitWaitDontShowMe/Sample/Images.xcassets/expensive.imageset/expensive@2x.png b/examples/WaitWaitDontShowMe/Sample/Images.xcassets/expensive.imageset/expensive@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c517803f8137603e961ef942a529c6ae9d3f674c GIT binary patch literal 2610 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 K&t;ucLK6VNOm=<% literal 0 HcmV?d00001 diff --git a/examples/WaitWaitDontShowMe/Sample/Info.plist b/examples/WaitWaitDontShowMe/Sample/Info.plist new file mode 100644 index 0000000000..c13033acbc --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.whoisryannystrom.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.h b/examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.h new file mode 100644 index 0000000000..b73494457a --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.h @@ -0,0 +1,16 @@ +/* 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 + +@interface PlaceholderTextNode : ASTextNode + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.m b/examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.m new file mode 100644 index 0000000000..06f2c1e7bd --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/PlaceholderTextNode.m @@ -0,0 +1,58 @@ +/* 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 "PlaceholderTextNode.h" + +#import + +@interface PlaceholderTextNode () +{ + CALayer *_placeholderLayer; + BOOL _displayFinished; +} + +@end + +@implementation PlaceholderTextNode + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + _placeholderLayer = [CALayer layer]; + _placeholderLayer.backgroundColor = [UIColor colorWithWhite:0.7f alpha:0.5f].CGColor; + + return self; +} + +- (void)willEnterHierarchy +{ + [super willEnterHierarchy]; + + if (!_displayFinished) { + [self.layer addSublayer:_placeholderLayer]; + } +} + +- (void)layout +{ + _placeholderLayer.frame = (CGRect){ CGPointZero, self.calculatedSize }; +} + +- (void)displayDidFinish +{ + _displayFinished = YES; + [_placeholderLayer removeFromSuperlayer]; + [super displayDidFinish]; +} + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/PostNode.h b/examples/WaitWaitDontShowMe/Sample/PostNode.h new file mode 100644 index 0000000000..d64f2b97d4 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/PostNode.h @@ -0,0 +1,18 @@ +/* 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 + +@interface PostNode : ASCellNode + +- (instancetype)initWithDate:(NSDate *)date text:(NSString *)text; + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/PostNode.m b/examples/WaitWaitDontShowMe/Sample/PostNode.m new file mode 100644 index 0000000000..5a67c2cd91 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/PostNode.m @@ -0,0 +1,138 @@ +/* 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 "PostNode.h" + +#import + +#import "ShareNode.h" +#import "PlaceholderTextNode.h" + +static CGFloat const kTopPadding = 15.f; +static CGFloat const kOuterPadding = 10.f; +static CGFloat const kTextPadding = 10.f; + +@interface PostNode () +{ + PlaceholderTextNode *_textNode; + PlaceholderTextNode *_dateNode; + ShareNode *_shareNode; + ASDisplayNode *_divider; +} + +@end + +@implementation PostNode + ++ (NSDateFormatter *)dateFormatter +{ + NSString * const formatterKey = @"dateFormatter"; + NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary]; + NSDateFormatter *dateFormatter = threadDictionary[formatterKey]; + + if (!dateFormatter) { + dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.locale = [NSLocale currentLocale]; + dateFormatter.dateStyle = NSDateFormatterLongStyle; + dateFormatter.timeStyle = NSDateFormatterNoStyle; + threadDictionary[formatterKey] = dateFormatter; + } + + return dateFormatter; +} + +- (instancetype)initWithDate:(NSDate *)date text:(NSString *)text +{ + if (!(self = [super init])) + return nil; + + NSString *dateString = [[self.class dateFormatter] stringFromDate:date]; + + _dateNode = [[PlaceholderTextNode alloc] init]; + _dateNode.attributedString = [[NSAttributedString alloc] initWithString:dateString attributes:[self dateTextStyle]]; + [self addSubnode:_dateNode]; + + _textNode = [[PlaceholderTextNode alloc] init]; + _textNode.attributedString = [self styledTextString:text]; + [self addSubnode:_textNode]; + + _divider = [[ASDisplayNode alloc] init]; + _divider.backgroundColor = [UIColor lightGrayColor]; + [self addSubnode:_divider]; + + _shareNode = [[ShareNode alloc] init]; + [self addSubnode:_shareNode]; + + return self; +} + +// purposefully expensive for demo +- (NSAttributedString *)styledTextString:(NSString *)originalText +{ + NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:originalText attributes:[self textStyle]]; + + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\*\\*[\\w\\s]+\\*\\*" options:kNilOptions error:nil]; + + [regex enumerateMatchesInString:originalText options:kNilOptions range:NSMakeRange(0,originalText.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + NSRange range = [result rangeAtIndex:0]; + [attributedText addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Bold" size:16.0f] range:range]; + }]; + + return attributedText; +} + +- (NSDictionary *)dateTextStyle +{ + UIFont *font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:12.0f]; + UIColor *color = [UIColor colorWithWhite:0.7f alpha:1.f]; + + return @{ NSFontAttributeName: font, + NSForegroundColorAttributeName: color }; +} + +- (NSDictionary *)textStyle +{ + UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:16.0f]; + + NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + style.paragraphSpacing = 0.5 * font.lineHeight; + style.hyphenationFactor = 1.0; + + return @{ NSFontAttributeName: font, + NSParagraphStyleAttributeName: style }; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + CGSize dateSize = [_dateNode measure:CGSizeMake((constrainedSize.width - 2 * kOuterPadding) / 2.f, constrainedSize.height)]; + CGSize textSize = [_textNode measure:CGSizeMake(constrainedSize.width - 2 * kOuterPadding, constrainedSize.height)]; + + [_shareNode measure:constrainedSize]; + + return CGSizeMake(constrainedSize.width, dateSize.height + textSize.height + 2 * kTopPadding + kTextPadding); +} + +- (void)layout +{ + CGFloat pixelHeight = 1.0f / [[UIScreen mainScreen] scale]; + _divider.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, pixelHeight); + + CGSize dateSize = _dateNode.calculatedSize; + _dateNode.frame = CGRectMake(kOuterPadding, kTopPadding, dateSize.width, dateSize.height); + + CGSize textSize = _textNode.calculatedSize; + _textNode.frame = CGRectMake(kOuterPadding, CGRectGetMaxY(_dateNode.frame) + kTextPadding, textSize.width, textSize.height); + + CGSize iconSize = _shareNode.calculatedSize; + _shareNode.frame = CGRectMake(self.calculatedSize.width - kOuterPadding - iconSize.width, kTopPadding, iconSize.width, iconSize.width); +} + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/ShareNode.h b/examples/WaitWaitDontShowMe/Sample/ShareNode.h new file mode 100644 index 0000000000..b118d6dea5 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/ShareNode.h @@ -0,0 +1,16 @@ +/* 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 + +@interface ShareNode : ASControlNode + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/ShareNode.m b/examples/WaitWaitDontShowMe/Sample/ShareNode.m new file mode 100644 index 0000000000..512b8134b0 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/ShareNode.m @@ -0,0 +1,72 @@ +/* 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 "ShareNode.h" + +#import + +static NSUInteger const kRingCount = 3; +static CGFloat const kRingStrokeWidth = 1.f; +static CGSize const kIconSize = (CGSize){ 40.f, 10.f }; + +@interface ShareNode () +{ + ASImageNode *_iconNode; +} + +@end + +@implementation ShareNode + ++ (UIImage *)drawIcon +{ + UIGraphicsBeginImageContextWithOptions(kIconSize, NO, [UIScreen mainScreen].scale); + + [[UIColor colorWithRed:0.f green:122/255.f blue:1.f alpha:1.f] setStroke]; + + for (NSUInteger i = 0; i < kRingCount; i++) { + CGFloat x = i * kIconSize.width / kRingCount; + CGRect frame = CGRectMake(x, 0.f, kIconSize.height, kIconSize.height); + CGRect strokeFrame = CGRectInset(frame, kRingStrokeWidth, kRingStrokeWidth); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeFrame cornerRadius:kIconSize.height / 2.f]; + [path setLineWidth:kRingStrokeWidth]; + [path stroke]; + } + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + _iconNode = [[ASImageNode alloc] init]; + _iconNode.image = [self.class drawIcon]; + [self addSubnode:_iconNode]; + + return self; +} + +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + return kIconSize; +} + +- (void)layout +{ + _iconNode.frame = (CGRect){ CGPointZero, self.calculatedSize }; +} + +@end diff --git a/examples/WaitWaitDontShowMe/Sample/main.m b/examples/WaitWaitDontShowMe/Sample/main.m new file mode 100644 index 0000000000..6559d60028 --- /dev/null +++ b/examples/WaitWaitDontShowMe/Sample/main.m @@ -0,0 +1,19 @@ +/* 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 +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +}