Added sample project using networking and infinite scrolling and random cats

This commit is contained in:
Sam Stow
2016-01-23 14:28:37 -08:00
parent 64ad48a248
commit 3b4785cf24
31 changed files with 1915 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'AsyncDisplayKit', :path => '../..'

View File

@@ -0,0 +1,405 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 25FDEC911BF31EE700CEB123 /* ItemNode.m */; };
7A83848E1C34359D002CDD08 /* ItemViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A83848D1C34359D002CDD08 /* ItemViewModel.m */; };
7A8384941C343680002CDD08 /* BlurbNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A8384921C343680002CDD08 /* BlurbNode.m */; };
7A8384971C344057002CDD08 /* ItemStyles.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A8384961C344057002CDD08 /* ItemStyles.m */; };
7ACD5F891C415B7500E7BE16 /* LoadingNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD5F881C415B7500E7BE16 /* LoadingNode.m */; };
7ACD5F961C4847C000E7BE16 /* PlaceholderNetworkImageNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD5F951C4847C000E7BE16 /* PlaceholderNetworkImageNode.m */; };
9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */; };
AC3C4A641A11F47200143C57 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A631A11F47200143C57 /* main.m */; };
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; };
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; };
AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC3C4A8D1A11F80C00143C57 /* Images.xcassets */; };
FABD6D156A3EB118497E5CE6 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F02BAF78E68BC56FD8C161B7 /* libPods.a */; };
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
25FDEC901BF31EE700CEB123 /* ItemNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ItemNode.h; sourceTree = "<group>"; };
25FDEC911BF31EE700CEB123 /* ItemNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ItemNode.m; sourceTree = "<group>"; };
2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
7A83848C1C34359D002CDD08 /* ItemViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ItemViewModel.h; sourceTree = "<group>"; };
7A83848D1C34359D002CDD08 /* ItemViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ItemViewModel.m; sourceTree = "<group>"; };
7A8384911C343680002CDD08 /* BlurbNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlurbNode.h; sourceTree = "<group>"; };
7A8384921C343680002CDD08 /* BlurbNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlurbNode.m; sourceTree = "<group>"; };
7A8384951C344057002CDD08 /* ItemStyles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ItemStyles.h; sourceTree = "<group>"; };
7A8384961C344057002CDD08 /* ItemStyles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ItemStyles.m; sourceTree = "<group>"; };
7ACD5F871C415B7500E7BE16 /* LoadingNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadingNode.h; sourceTree = "<group>"; };
7ACD5F881C415B7500E7BE16 /* LoadingNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoadingNode.m; sourceTree = "<group>"; };
7ACD5F941C4847C000E7BE16 /* PlaceholderNetworkImageNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaceholderNetworkImageNode.h; sourceTree = "<group>"; };
7ACD5F951C4847C000E7BE16 /* PlaceholderNetworkImageNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaceholderNetworkImageNode.m; sourceTree = "<group>"; };
9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launchboard.storyboard; sourceTree = "<group>"; };
AC3C4A5E1A11F47200143C57 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
AC3C4A621A11F47200143C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
AC3C4A631A11F47200143C57 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
AC3C4A651A11F47200143C57 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
AC3C4A661A11F47200143C57 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
AC3C4A681A11F47200143C57 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
AC3C4A691A11F47200143C57 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
F02BAF78E68BC56FD8C161B7 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PresentingViewController.h; sourceTree = "<group>"; };
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PresentingViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
AC3C4A5B1A11F47200143C57 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FABD6D156A3EB118497E5CE6 /* libPods.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
90A2B9C5397C46134C8A793B /* Pods */ = {
isa = PBXGroup;
children = (
2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */,
CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
AC3C4A551A11F47200143C57 = {
isa = PBXGroup;
children = (
AC3C4A601A11F47200143C57 /* Sample */,
AC3C4A5F1A11F47200143C57 /* Products */,
90A2B9C5397C46134C8A793B /* Pods */,
D6E38FF0CB18E3F55CF06437 /* Frameworks */,
);
sourceTree = "<group>";
};
AC3C4A5F1A11F47200143C57 /* Products */ = {
isa = PBXGroup;
children = (
AC3C4A5E1A11F47200143C57 /* Sample.app */,
);
name = Products;
sourceTree = "<group>";
};
AC3C4A601A11F47200143C57 /* Sample */ = {
isa = PBXGroup;
children = (
AC3C4A651A11F47200143C57 /* AppDelegate.h */,
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
AC3C4A681A11F47200143C57 /* ViewController.h */,
AC3C4A691A11F47200143C57 /* ViewController.m */,
7ACD5F941C4847C000E7BE16 /* PlaceholderNetworkImageNode.h */,
7ACD5F951C4847C000E7BE16 /* PlaceholderNetworkImageNode.m */,
FC3FCA7E1C2B1564009F6D6D /* PresentingViewController.h */,
FC3FCA7F1C2B1564009F6D6D /* PresentingViewController.m */,
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
AC3C4A611A11F47200143C57 /* Supporting Files */,
25FDEC901BF31EE700CEB123 /* ItemNode.h */,
25FDEC911BF31EE700CEB123 /* ItemNode.m */,
7A8384951C344057002CDD08 /* ItemStyles.h */,
7A8384961C344057002CDD08 /* ItemStyles.m */,
7A83848C1C34359D002CDD08 /* ItemViewModel.h */,
7A83848D1C34359D002CDD08 /* ItemViewModel.m */,
7A8384911C343680002CDD08 /* BlurbNode.h */,
7A8384921C343680002CDD08 /* BlurbNode.m */,
7ACD5F871C415B7500E7BE16 /* LoadingNode.h */,
7ACD5F881C415B7500E7BE16 /* LoadingNode.m */,
);
indentWidth = 2;
path = Sample;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
AC3C4A611A11F47200143C57 /* Supporting Files */ = {
isa = PBXGroup;
children = (
AC3C4A621A11F47200143C57 /* Info.plist */,
AC3C4A631A11F47200143C57 /* main.m */,
9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
D6E38FF0CB18E3F55CF06437 /* Frameworks */ = {
isa = PBXGroup;
children = (
F02BAF78E68BC56FD8C161B7 /* libPods.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
AC3C4A5D1A11F47200143C57 /* Sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = AC3C4A811A11F47200143C57 /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */,
AC3C4A5A1A11F47200143C57 /* Sources */,
AC3C4A5B1A11F47200143C57 /* Frameworks */,
AC3C4A5C1A11F47200143C57 /* Resources */,
A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
B4CD33E927E6F4EE5DD6CCF0 /* Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Sample;
productName = Sample;
productReference = AC3C4A5E1A11F47200143C57 /* Sample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
AC3C4A561A11F47200143C57 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
AC3C4A5D1A11F47200143C57 = {
CreatedOnToolsVersion = 6.1;
};
};
};
buildConfigurationList = AC3C4A591A11F47200143C57 /* Build configuration list for PBXProject "Sample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = AC3C4A551A11F47200143C57;
productRefGroup = AC3C4A5F1A11F47200143C57 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
AC3C4A5D1A11F47200143C57 /* Sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
AC3C4A5C1A11F47200143C57 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */,
AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
A6902C454C7661D0D277AC62 /* 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;
};
B4CD33E927E6F4EE5DD6CCF0 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
F868CFBB21824CC9521B6588 /* 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 */
AC3C4A5A1A11F47200143C57 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
25FDEC921BF31EE700CEB123 /* ItemNode.m in Sources */,
7ACD5F891C415B7500E7BE16 /* LoadingNode.m in Sources */,
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
7A8384971C344057002CDD08 /* ItemStyles.m in Sources */,
FC3FCA801C2B1564009F6D6D /* PresentingViewController.m in Sources */,
7A8384941C343680002CDD08 /* BlurbNode.m in Sources */,
7A83848E1C34359D002CDD08 /* ItemViewModel.m in Sources */,
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
7ACD5F961C4847C000E7BE16 /* PlaceholderNetworkImageNode.m in Sources */,
AC3C4A641A11F47200143C57 /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
AC3C4A7F1A11F47200143C57 /* 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;
};
AC3C4A801A11F47200143C57 /* 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;
};
AC3C4A821A11F47200143C57 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
INFOPLIST_FILE = Sample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
AC3C4A831A11F47200143C57 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = CD1ABB23007FEDB31D8C1978 /* Pods.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
INFOPLIST_FILE = Sample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
AC3C4A591A11F47200143C57 /* Build configuration list for PBXProject "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AC3C4A7F1A11F47200143C57 /* Debug */,
AC3C4A801A11F47200143C57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AC3C4A811A11F47200143C57 /* Build configuration list for PBXNativeTarget "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AC3C4A821A11F47200143C57 /* Debug */,
AC3C4A831A11F47200143C57 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = AC3C4A561A11F47200143C57 /* Project object */;
}

View File

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

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC3C4A5D1A11F47200143C57"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC3C4A5D1A11F47200143C57"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC3C4A5D1A11F47200143C57"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC3C4A5D1A11F47200143C57"
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,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>
#define SIMULATE_WEB_RESPONSE 0
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,51 @@
/* 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 "PresentingViewController.h"
#import "ViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[UINavigationController alloc] init];
[self pushNewViewControllerAnimated:NO];
[self.window makeKeyAndVisible];
return YES;
}
- (void)pushNewViewControllerAnimated:(BOOL)animated
{
UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
#if SIMULATE_WEB_RESPONSE
UIViewController *viewController = [[PresentingViewController alloc] init];
#else
UIViewController *viewController = [[ViewController alloc] init];
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
#endif
[navController pushViewController:viewController animated:animated];
}
- (void)pushNewViewController
{
[self pushNewViewControllerAnimated:YES];
}
@end

View File

@@ -0,0 +1,21 @@
/* 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 <AsyncDisplayKit/AsyncDisplayKit.h>
/**
* Simple node that displays a placekitten.com attribution.
*/
@interface BlurbNode : ASCellNode
+ (CGFloat)desiredHeightForWidth:(CGFloat)width;
@end

View File

@@ -0,0 +1,112 @@
/* 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 "BlurbNode.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
static CGFloat kFixedHeight = 75.0f;
static CGFloat kTextPadding = 10.0f;
@interface BlurbNode () <ASTextNodeDelegate>
{
ASTextNode *_textNode;
}
@end
@implementation BlurbNode
#pragma mark -
#pragma mark ASCellNode.
+ (CGFloat)desiredHeightForWidth:(CGFloat)width {
return kFixedHeight;
}
- (instancetype)init
{
if (!(self = [super init]))
return nil;
self.backgroundColor = [UIColor lightGrayColor];
// create a text node
_textNode = [[ASTextNode alloc] init];
_textNode.maximumNumberOfLines = 2;
// configure the node to support tappable links
_textNode.delegate = self;
_textNode.userInteractionEnabled = YES;
// generate an attributed string using the custom link attribute specified above
NSString *blurb = @"Kittens courtesy lorempixel.com \U0001F638 \nTitles courtesy of catipsum.com";
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb];
[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Light" size:16.0f] range:NSMakeRange(0, blurb.length)];
[string addAttributes:@{
NSLinkAttributeName: [NSURL URLWithString:@"http://lorempixel.com/"],
NSForegroundColorAttributeName: [UIColor blueColor],
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
}
range:[blurb rangeOfString:@"lorempixel.com"]];
[string addAttributes:@{
NSLinkAttributeName: [NSURL URLWithString:@"http://www.catipsum.com/"],
NSForegroundColorAttributeName: [UIColor blueColor],
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
} range:[blurb rangeOfString:@"catipsum.com"]];
_textNode.attributedString = string;
// add it as a subnode, and we're done
[self addSubnode:_textNode];
return self;
}
- (void)didLoad
{
// enable highlighting now that self.layer has loaded -- see ASHighlightOverlayLayer.h
self.layer.as_allowsHighlightDrawing = YES;
[super didLoad];
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASCenterLayoutSpec *centerSpec = [[ASCenterLayoutSpec alloc] init];
centerSpec.centeringOptions = ASCenterLayoutSpecCenteringX;
centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionMinimumY;
centerSpec.child = _textNode;
UIEdgeInsets padding =UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding);
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:padding child:centerSpec];
}
#pragma mark -
#pragma mark ASTextNodeDelegate methods.
- (BOOL)textNode:(ASTextNode *)richTextNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point
{
// opt into link highlighting -- tap and hold the link to try it! must enable highlighting on a layer, see -didLoad
return YES;
}
- (void)textNode:(ASTextNode *)richTextNode tappedLinkAttribute:(NSString *)attribute value:(NSURL *)URL atPoint:(CGPoint)point textRange:(NSRange)textRange
{
// the node tapped a link, open it
[[UIApplication sharedApplication] openURL:URL];
}
@end

View File

@@ -0,0 +1,39 @@
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "Default-568h@2x.png",
"minimum-system-version" : "7.0",
"subtype" : "retina4",
"scale" : "2x"
},
{
"idiom" : "iphone",
"scale" : "1x",
"orientation" : "portrait"
},
{
"idiom" : "iphone",
"scale" : "2x",
"orientation" : "portrait"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "Default-568h@2x.png",
"subtype" : "retina4",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "cat_face.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -0,0 +1,59 @@
<?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>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>http://lorempixel.com</key>
<string></string>
</dict>
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIcons</key>
<dict/>
<key>CFBundleIcons~ipad</key>
<dict/>
<key>CFBundleIdentifier</key>
<string>com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)</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>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>Launchboard</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,21 @@
/* 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 <AsyncDisplayKit/AsyncDisplayKit.h>
#import "ItemViewModel.h"
@interface ItemNode : ASCellNode
- initWithViewModel:(ItemViewModel *)viewModel;
+ (CGSize)sizeForWidth:(CGFloat)width;
+ (CGSize)preferredViewSize;
@end

View File

@@ -0,0 +1,361 @@
/* 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 "ItemNode.h"
#import "ItemStyles.h"
#import "PlaceholderNetworkImageNode.h"
const CGFloat kFixedLabelsAreaHeight = 96.0;
const CGFloat kDesignWidth = 320.0;
const CGFloat kDesignHeight = 299.0;
const CGFloat kBadgeHeight = 34.0;
const CGFloat kSoldOutGBHeight = 50.0;
@interface ItemNode() <ASNetworkImageNodeDelegate>
@property (nonatomic, strong) ItemViewModel *viewModel;
@property (nonatomic, strong) PlaceholderNetworkImageNode *dealImageView;
@property (nonatomic, strong) ASTextNode *titleLabel;
@property (nonatomic, strong) ASTextNode *firstInfoLabel;
@property (nonatomic, strong) ASTextNode *distanceLabel;
@property (nonatomic, strong) ASTextNode *secondInfoLabel;
@property (nonatomic, strong) ASTextNode *originalPriceLabel;
@property (nonatomic, strong) ASTextNode *finalPriceLabel;
@property (nonatomic, strong) ASTextNode *soldOutLabelFlat;
@property (nonatomic, strong) ASDisplayNode *soldOutLabelBackground;
@property (nonatomic, strong) ASDisplayNode *soldOutOverlay;
@property (nonatomic, strong) ASTextNode *badge;
@end
@implementation ItemNode
- (instancetype)initWithViewModel:(ItemViewModel *)viewModel
{
self = [super init];
if (self != nil) {
_viewModel = viewModel;
[self setup];
[self updateLabels];
[self updateBackgroundColor];
}
return self;
}
+ (BOOL)isRTL {
return [UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
}
- (void)setup {
self.dealImageView = [[PlaceholderNetworkImageNode alloc] init];
self.dealImageView.delegate = self;
self.dealImageView.placeholderEnabled = YES;
self.dealImageView.placeholderImageOverride = [ItemStyles placeholderImage];
self.dealImageView.defaultImage = [ItemStyles placeholderImage];
self.dealImageView.contentMode = UIViewContentModeScaleToFill;
self.dealImageView.placeholderFadeDuration = 0.0;
self.dealImageView.layerBacked = YES;
self.titleLabel = [[ASTextNode alloc] init];
self.titleLabel.maximumNumberOfLines = 2;
self.titleLabel.alignSelf = ASStackLayoutAlignSelfStart;
self.titleLabel.flexGrow = YES;
self.titleLabel.layerBacked = YES;
self.firstInfoLabel = [[ASTextNode alloc] init];
self.firstInfoLabel.maximumNumberOfLines = 1;
self.firstInfoLabel.layerBacked = YES;
self.secondInfoLabel = [[ASTextNode alloc] init];
self.secondInfoLabel.maximumNumberOfLines = 1;
self.secondInfoLabel.layerBacked = YES;
self.distanceLabel = [[ASTextNode alloc] init];
self.distanceLabel.maximumNumberOfLines = 1;
self.distanceLabel.layerBacked = YES;
self.originalPriceLabel = [[ASTextNode alloc] init];
self.originalPriceLabel.maximumNumberOfLines = 1;
self.originalPriceLabel.layerBacked = YES;
self.finalPriceLabel = [[ASTextNode alloc] init];
self.finalPriceLabel.maximumNumberOfLines = 1;
self.finalPriceLabel.layerBacked = YES;
self.badge = [[ASTextNode alloc] init];
self.badge.hidden = YES;
self.badge.layerBacked = YES;
self.soldOutLabelFlat = [[ASTextNode alloc] init];
self.soldOutLabelFlat.layerBacked = YES;
self.soldOutLabelBackground = [[ASDisplayNode alloc] init];
self.soldOutLabelBackground.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(1), ASRelativeDimensionMakeWithPoints(kSoldOutGBHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(1), ASRelativeDimensionMakeWithPoints(kSoldOutGBHeight)));
self.soldOutLabelBackground.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9];
self.soldOutLabelBackground.flexGrow = YES;
self.soldOutLabelBackground.layerBacked = YES;
self.soldOutOverlay = [[ASDisplayNode alloc] init];
self.soldOutOverlay.flexGrow = YES;
self.soldOutOverlay.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];
self.soldOutOverlay.layerBacked = YES;
[self addSubnode:self.dealImageView];
[self addSubnode:self.titleLabel];
[self addSubnode:self.firstInfoLabel];
[self addSubnode:self.secondInfoLabel];
[self addSubnode:self.originalPriceLabel];
[self addSubnode:self.finalPriceLabel];
[self addSubnode:self.distanceLabel];
[self addSubnode:self.badge];
[self addSubnode:self.soldOutLabelBackground];
[self addSubnode:self.soldOutLabelFlat];
[self addSubnode:self.soldOutOverlay];
self.soldOutOverlay.hidden = YES;
self.soldOutLabelBackground.hidden = YES;
self.soldOutLabelFlat.hidden = YES;
[self addSubnode:self.soldOutOverlay];
if ([ItemNode isRTL]) {
self.titleLabel.alignSelf = ASStackLayoutAlignSelfEnd;
self.firstInfoLabel.alignSelf = ASStackLayoutAlignSelfEnd;
self.distanceLabel.alignSelf = ASStackLayoutAlignSelfEnd;
self.secondInfoLabel.alignSelf = ASStackLayoutAlignSelfEnd;
self.originalPriceLabel.alignSelf = ASStackLayoutAlignSelfStart;
self.finalPriceLabel.alignSelf = ASStackLayoutAlignSelfStart;
} else {
self.firstInfoLabel.alignSelf = ASStackLayoutAlignSelfStart;
self.distanceLabel.alignSelf = ASStackLayoutAlignSelfStart;
self.secondInfoLabel.alignSelf = ASStackLayoutAlignSelfStart;
self.originalPriceLabel.alignSelf = ASStackLayoutAlignSelfEnd;
self.finalPriceLabel.alignSelf = ASStackLayoutAlignSelfEnd;
}
}
- (void)updateLabels {
// Set Title text
if (self.viewModel.titleText) {
self.titleLabel.attributedString = [[NSAttributedString alloc] initWithString:self.viewModel.titleText attributes:[ItemStyles titleStyle]];
}
if (self.viewModel.firstInfoText) {
self.firstInfoLabel.attributedString = [[NSAttributedString alloc] initWithString:self.viewModel.firstInfoText attributes:[ItemStyles subtitleStyle]];
}
if (self.viewModel.secondInfoText) {
self.secondInfoLabel.attributedString = [[NSAttributedString alloc] initWithString:self.viewModel.secondInfoText attributes:[ItemStyles secondInfoStyle]];
}
if (self.viewModel.originalPriceText) {
self.originalPriceLabel.attributedString = [[NSAttributedString alloc] initWithString:self.viewModel.originalPriceText attributes:[ItemStyles originalPriceStyle]];
}
if (self.viewModel.finalPriceText) {
self.finalPriceLabel.attributedString = [[NSAttributedString alloc] initWithString:self.viewModel.finalPriceText attributes:[ItemStyles finalPriceStyle]];
}
if (self.viewModel.distanceLabelText) {
NSString *format = [ItemNode isRTL] ? @"%@ •" : @"• %@";
NSString *distanceText = [NSString stringWithFormat:format, self.viewModel.distanceLabelText];
self.distanceLabel.attributedString = [[NSAttributedString alloc] initWithString:distanceText attributes:[ItemStyles distanceStyle]];
}
BOOL isSoldOut = self.viewModel.soldOutText != nil;
if (isSoldOut) {
NSString *soldOutText = self.viewModel.soldOutText;
self.soldOutLabelFlat.attributedString = [[NSAttributedString alloc] initWithString:soldOutText attributes:[ItemStyles soldOutStyle]];
}
self.soldOutOverlay.hidden = !isSoldOut;
self.soldOutLabelFlat.hidden = !isSoldOut;
self.soldOutLabelBackground.hidden = !isSoldOut;
BOOL hasBadge = self.viewModel.badgeText != nil;
if (hasBadge) {
self.badge.attributedString = [[NSAttributedString alloc] initWithString:self.viewModel.badgeText attributes:[ItemStyles badgeStyle]];
self.badge.backgroundColor = [ItemStyles badgeColor];
}
self.badge.hidden = !hasBadge;
}
- (void)updateBackgroundColor
{
if (self.highlighted) {
self.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.3];
} else if (self.selected) {
self.backgroundColor = [UIColor lightGrayColor];
} else {
self.backgroundColor = [UIColor whiteColor];
}
}
- (void)imageNode:(ASNetworkImageNode *)imageNode didLoadImage:(UIImage *)image {
}
- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
[self updateBackgroundColor];
}
- (void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
[self updateBackgroundColor];
}
#pragma mark - superclass
- (void)displayWillStart {
[super displayWillStart];
[self fetchData];
}
- (void)fetchData {
[super fetchData];
if (self.viewModel) {
[self loadImage];
}
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
ASLayoutSpec *textSpec = [self textSpec];
ASLayoutSpec *imageSpec = [self imageSpecWithSize:constrainedSize];
ASOverlayLayoutSpec *soldOutOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imageSpec overlay:[self soldOutLabelSpec]];
NSArray *stackChildren = @[soldOutOverImage, textSpec];
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:stackChildren];
ASOverlayLayoutSpec *soldOutOverlay = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:mainStack overlay:self.soldOutOverlay];
return soldOutOverlay;
}
- (ASLayoutSpec *)textSpec {
CGFloat kInsetHorizontal = 16.0;
CGFloat kInsetTop = 6.0;
CGFloat kInsetBottom = 0.0;
UIEdgeInsets textInsets = UIEdgeInsetsMake(kInsetTop, kInsetHorizontal, kInsetBottom, kInsetHorizontal);
ASLayoutSpec *verticalSpacer = [[ASLayoutSpec alloc] init];
verticalSpacer.flexGrow = YES;
ASLayoutSpec *horizontalSpacer1 = [[ASLayoutSpec alloc] init];
horizontalSpacer1.flexGrow = YES;
ASLayoutSpec *horizontalSpacer2 = [[ASLayoutSpec alloc] init];
horizontalSpacer2.flexGrow = YES;
NSArray *info1Children = @[self.firstInfoLabel, self.distanceLabel, horizontalSpacer1, self.originalPriceLabel];
NSArray *info2Children = @[self.secondInfoLabel, horizontalSpacer2, self.finalPriceLabel];
if ([ItemNode isRTL]) {
info1Children = [[info1Children reverseObjectEnumerator] allObjects];
info2Children = [[info2Children reverseObjectEnumerator] allObjects];
}
ASStackLayoutSpec *info1Stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:1.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsBaselineLast children:info1Children];
ASStackLayoutSpec *info2Stack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentCenter alignItems:ASStackLayoutAlignItemsBaselineLast children:info2Children];
ASStackLayoutSpec *textStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0.0 justifyContent:ASStackLayoutJustifyContentEnd alignItems:ASStackLayoutAlignItemsStretch children:@[self.titleLabel, verticalSpacer, info1Stack, info2Stack]];
ASInsetLayoutSpec *textWrapper = [ASInsetLayoutSpec insetLayoutSpecWithInsets:textInsets child:textStack];
textWrapper.flexGrow = YES;
return textWrapper;
}
- (ASLayoutSpec *)imageSpecWithSize:(ASSizeRange)constrainedSize {
CGFloat imageRatio = [self imageRatioFromSize:constrainedSize.max];
ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:self.dealImageView];
self.badge.layoutPosition = CGPointMake(0, constrainedSize.max.height - kFixedLabelsAreaHeight - kBadgeHeight);
self.badge.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(0), ASRelativeDimensionMakeWithPoints(kBadgeHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithPercent(1), ASRelativeDimensionMakeWithPoints(kBadgeHeight)));
ASStaticLayoutSpec *badgePosition = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.badge]];
ASOverlayLayoutSpec *badgeOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imagePlace overlay:badgePosition];
badgeOverImage.flexGrow = YES;
return badgeOverImage;
}
- (ASLayoutSpec *)soldOutLabelSpec {
ASCenterLayoutSpec *centerSoldOutLabel = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionMinimumXY child:self.soldOutLabelFlat];
ASStaticLayoutSpec *soldOutBG = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.soldOutLabelBackground]];
ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:soldOutBG];
ASBackgroundLayoutSpec *soldOutLabelOverBackground = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:centerSoldOutLabel background:centerSoldOut];
return soldOutLabelOverBackground;
}
+ (CGSize)sizeForWidth:(CGFloat)width {
CGFloat height = [self scaledHeightForPreferredSize:[self preferredViewSize] scaledWidth:width];
return CGSizeMake(width, height);
}
+ (CGSize)preferredViewSize {
return CGSizeMake(kDesignWidth, kDesignHeight);
}
+ (CGFloat)scaledHeightForPreferredSize:(CGSize)preferredSize scaledWidth:(CGFloat)scaledWidth {
CGFloat scale = scaledWidth / kDesignWidth;
CGFloat scaledHeight = ceilf(scale * (kDesignHeight - kFixedLabelsAreaHeight)) + kFixedLabelsAreaHeight;
return scaledHeight;
}
#pragma mark - view operations
- (CGFloat)imageRatioFromSize:(CGSize)size {
CGFloat imageHeight = size.height - kFixedLabelsAreaHeight;
CGFloat imageRatio = imageHeight / size.width;
return imageRatio;
}
- (CGSize)imageSize {
if (!CGSizeEqualToSize(self.dealImageView.frame.size, CGSizeZero)) {
return self.dealImageView.frame.size;
} else if (!CGSizeEqualToSize(self.calculatedSize, CGSizeZero)) {
CGFloat imageRatio = [self imageRatioFromSize:self.calculatedSize];
CGFloat imageWidth = self.calculatedSize.width;
return CGSizeMake(imageWidth, imageRatio * imageWidth);
} else {
return CGSizeZero;
}
}
- (void)loadImage {
CGSize imageSize = [self imageSize];
if (CGSizeEqualToSize(CGSizeZero, imageSize)) {
return;
}
NSURL *url = [self.viewModel imageURLWithSize:imageSize];
// if we're trying to set the deal image to what it already was, skip the work
if ([[url absoluteString] isEqualToString:[self.dealImageView.URL absoluteString]]) {
return;
}
// Clear the flag that says we've loaded our image
[self.dealImageView setURL:url];
}
@end

View File

@@ -0,0 +1,23 @@
//
// ItemStyles.h
// Sample
//
// Created by Samuel Stow on 12/30/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ItemStyles : NSObject
+ (NSDictionary *)titleStyle;
+ (NSDictionary *)subtitleStyle;
+ (NSDictionary *)distanceStyle;
+ (NSDictionary *)secondInfoStyle;
+ (NSDictionary *)originalPriceStyle;
+ (NSDictionary *)finalPriceStyle;
+ (NSDictionary *)soldOutStyle;
+ (NSDictionary *)badgeStyle;
+ (UIColor *)badgeColor;
+ (UIImage *)placeholderImage;
@end

View File

@@ -0,0 +1,93 @@
//
// ItemStyles.m
// Sample
//
// Created by Samuel Stow on 12/30/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import "ItemStyles.h"
const CGFloat kTitleFontSize = 20.0;
const CGFloat kInfoFontSize = 14.0;
UIColor *kTitleColor;
UIColor *kInfoColor;
UIColor *kFinalPriceColor;
UIFont *kTitleFont;
UIFont *kInfoFont;
@implementation ItemStyles
+ (void)initialize {
if (self == [ItemStyles class]) {
kTitleColor = [UIColor darkGrayColor];
kInfoColor = [UIColor grayColor];
kFinalPriceColor = [UIColor greenColor];
kTitleFont = [UIFont boldSystemFontOfSize:kTitleFontSize];
kInfoFont = [UIFont systemFontOfSize:kInfoFontSize];
}
}
+ (NSDictionary *)titleStyle {
// Title Label
return @{ NSFontAttributeName:kTitleFont,
NSForegroundColorAttributeName:kTitleColor };
}
+ (NSDictionary *)subtitleStyle {
// First Subtitle
return @{ NSFontAttributeName:kInfoFont,
NSForegroundColorAttributeName:kInfoColor };
}
+ (NSDictionary *)distanceStyle {
// Distance Label
return @{ NSFontAttributeName:kInfoFont,
NSForegroundColorAttributeName:kInfoColor};
}
+ (NSDictionary *)secondInfoStyle {
// Second Subtitle
return @{ NSFontAttributeName:kInfoFont,
NSForegroundColorAttributeName:kInfoColor};
}
+ (NSDictionary *)originalPriceStyle {
// Original price
return @{ NSFontAttributeName:kInfoFont,
NSForegroundColorAttributeName:kInfoColor,
NSStrikethroughStyleAttributeName:@(NSUnderlineStyleSingle)};
}
+ (NSDictionary *)finalPriceStyle {
// Discounted / Claimable price label
return @{ NSFontAttributeName:kTitleFont,
NSForegroundColorAttributeName:kFinalPriceColor};
}
+ (NSDictionary *)soldOutStyle {
// Setup Sold Out Label
return @{ NSFontAttributeName:kTitleFont,
NSForegroundColorAttributeName:kTitleColor};
}
+ (NSDictionary *)badgeStyle {
// Setup Sold Out Label
return @{ NSFontAttributeName:kTitleFont,
NSForegroundColorAttributeName:[UIColor whiteColor]};
}
+ (UIColor *)badgeColor {
return [[UIColor purpleColor] colorWithAlphaComponent:0.4];
}
+ (UIImage *)placeholderImage {
static UIImage *__catFace = nil;
if (!__catFace) {
__catFace = [UIImage imageNamed:@"cat_face"];
}
return __catFace;
}
@end

View File

@@ -0,0 +1,27 @@
//
// GPDealViewModel.h
// Groupon
//
// Created by Samuel Stow on 12/29/15.
// Copyright © 2015 Groupon Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ItemViewModel : NSObject
+ (instancetype)randomItem;
@property (nonatomic, copy) NSString *titleText;
@property (nonatomic, copy) NSString *firstInfoText;
@property (nonatomic, copy) NSString *secondInfoText;
@property (nonatomic, copy) NSString *originalPriceText;
@property (nonatomic, copy) NSString *finalPriceText;
@property (nonatomic, copy) NSString *soldOutText;
@property (nonatomic, copy) NSString *distanceLabelText;
@property (nonatomic, copy) NSString *badgeText;
- (NSURL *)imageURLWithSize:(CGSize)size;
@end

View File

@@ -0,0 +1,99 @@
//
// GPDealViewModel.m
// Groupon
//
// Created by Samuel Stow on 12/29/15.
// Copyright © 2015 Groupon Inc. All rights reserved.
//
#import "ItemViewModel.h"
NSArray *titles;
NSArray *firstInfos;
NSArray *badges;
@interface ItemViewModel()
@property (nonatomic, assign) NSInteger catNumber;
@property (nonatomic, assign) NSInteger labelNumber;
@end
@implementation ItemViewModel
+ (instancetype)randomItem {
return [[ItemViewModel alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
_titleText = [self randomObjectFromArray:titles];
_firstInfoText = [self randomObjectFromArray:firstInfos];
_secondInfoText = [NSString stringWithFormat:@"%zd+ bought", [self randomNumberInRange:5 to:6000]];
_originalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:40 to:90]];
_finalPriceText = [NSString stringWithFormat:@"$%zd", [self randomNumberInRange:5 to:30]];
BOOL isSoldOut = arc4random() % 5 == 0;
_soldOutText = isSoldOut ? @"SOLD OUT" : nil;
_distanceLabelText = [NSString stringWithFormat:@"%zd mi", [self randomNumberInRange:1 to:20]];
BOOL isBadged = arc4random() % 2 == 0;
if (isBadged) {
_badgeText = [self randomObjectFromArray:badges];
}
_catNumber = [self randomNumberInRange:1 to:10];
_labelNumber = [self randomNumberInRange:1 to:10000];
}
return self;
}
- (NSURL *)imageURLWithSize:(CGSize)size {
NSString *imageText = [NSString stringWithFormat:@"Fun cat pic %zd", self.labelNumber];
NSString *urlString = [NSString stringWithFormat:@"http://lorempixel.com/%zd/%zd/cats/%zd/%@",
(NSInteger)roundl(size.width),
(NSInteger)roundl(size.height), self.catNumber, imageText];
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
return [NSURL URLWithString:urlString];
}
// titles courtesy of http://www.catipsum.com/
+ (void)initialize {
titles = @[@"Leave fur on owners clothes intrigued by the shower",
@"Meowwww",
@"Immediately regret falling into bathtub stare out the window",
@"Jump launch to pounce upon little yarn mouse, bare fangs at toy run hide in litter box until treats are fed",
@"Sleep nap",
@"Lick butt",
@"Chase laser lick arm hair present belly, scratch hand when stroked"];
firstInfos = @[@"Kitty Shop",
@"Cat's r us",
@"Fantastic Felines",
@"The Cat Shop",
@"Cat in a hat",
@"Cat-tastic"
];
badges = @[@"ADORABLE",
@"BOUNCES",
@"HATES CUCUMBERS",
@"SCRATCHY"
];
}
- (id)randomObjectFromArray:(NSArray *)strings
{
u_int32_t ipsumCount = (u_int32_t)[strings count];
u_int32_t location = arc4random_uniform(ipsumCount);
return strings[location];
}
- (uint32_t)randomNumberInRange:(uint32_t)start to:(uint32_t)end {
return start + arc4random_uniform(end - start);
}
@end

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
</dependencies>
<scenes/>
</document>

View File

@@ -0,0 +1,15 @@
//
// LoadingNode.h
// Sample
//
// Created by Samuel Stow on 1/9/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface LoadingNode : ASCellNode
+ (CGFloat)desiredHeightForWidth:(CGFloat)width;
@end

View File

@@ -0,0 +1,68 @@
//
// LoadingNode.m
// Sample
//
// Created by Samuel Stow on 1/9/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "LoadingNode.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASHighlightOverlayLayer.h>
#import <AsyncDisplayKit/ASInsetLayoutSpec.h>
#import <AsyncDisplayKit/ASCenterLayoutSpec.h>
static CGFloat kFixedHeight = 200.0f;
@interface LoadingNode ()
{
ASDisplayNode *_loadingSpinner;
}
@end
@implementation LoadingNode
#pragma mark -
#pragma mark ASCellNode.
+ (CGFloat)desiredHeightForWidth:(CGFloat)width {
return kFixedHeight;
}
- (instancetype)init
{
if (!(self = [super init]))
return nil;
_loadingSpinner = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[spinner startAnimating];
return spinner;
}];
_loadingSpinner.preferredFrameSize = CGSizeMake(50, 50);
// add it as a subnode, and we're done
[self addSubnode:_loadingSpinner];
return self;
}
- (void)layout {
[super layout];
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASCenterLayoutSpec *centerSpec = [[ASCenterLayoutSpec alloc] init];
centerSpec.centeringOptions = ASCenterLayoutSpecCenteringXY;
centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionDefault;
centerSpec.child = _loadingSpinner;
return centerSpec;
}
@end

View File

@@ -0,0 +1,15 @@
//
// PlacholderNetworkImageNode.h
// Sample
//
// Created by Samuel Stow on 1/14/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface PlaceholderNetworkImageNode : ASNetworkImageNode
@property (nonatomic, strong) UIImage *placeholderImageOverride;
@end

View File

@@ -0,0 +1,18 @@
//
// PlacholderNetworkImageNode.m
// Sample
//
// Created by Samuel Stow on 1/14/16.
// Copyright © 2016 Facebook. All rights reserved.
//
#import "PlaceholderNetworkImageNode.h"
@implementation PlaceholderNetworkImageNode
- (UIImage *)placeholderImage {
return self.placeholderImageOverride;
}
@end

View File

@@ -0,0 +1,13 @@
//
// PresentingViewController.h
// Sample
//
// Created by Tom King on 12/23/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface PresentingViewController : UIViewController
@end

View File

@@ -0,0 +1,30 @@
//
// PresentingViewController.m
// Sample
//
// Created by Tom King on 12/23/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import "PresentingViewController.h"
#import "ViewController.h"
@interface PresentingViewController ()
@end
@implementation PresentingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Details" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
}
- (void)pushNewViewController
{
ViewController *controller = [[ViewController alloc] init];
[self.navigationController pushViewController:controller animated:true];
}
@end

View File

@@ -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 <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

View File

@@ -0,0 +1,244 @@
/* 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 "ViewController.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "ItemNode.h"
#import "BlurbNode.h"
#import "LoadingNode.h"
static const NSTimeInterval kWebResponseDelay = 1.0;
static const BOOL kSimulateWebResponse = YES;
static const NSInteger kBatchSize = 20;
static const CGFloat kHorizontalSectionPadding = 10.0f;
static const CGFloat kVerticalSectionPadding = 20.0f;
@interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout>
{
ASCollectionView *_collectionView;
NSMutableArray *_data;
}
@end
@implementation ViewController
#pragma mark -
#pragma mark UIViewController.
- (instancetype)init
{
self = [super init];
if (self) {
self.title = @"Cat Deals";
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView.asyncDataSource = self;
_collectionView.asyncDelegate = self;
_collectionView.backgroundColor = [UIColor grayColor];
_collectionView.leadingScreensForBatching = 2;
ASRangeTuningParameters fetchDataTuning;
fetchDataTuning.leadingBufferScreenfuls = 2;
fetchDataTuning.trailingBufferScreenfuls = 1;
[_collectionView setTuningParameters:fetchDataTuning forRangeType:ASLayoutRangeTypeFetchData];
ASRangeTuningParameters preRenderTuning;
preRenderTuning.leadingBufferScreenfuls = 1;
preRenderTuning.trailingBufferScreenfuls = 0.5;
[_collectionView setTuningParameters:preRenderTuning forRangeType:ASLayoutRangeTypeDisplay];
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
_data = [[NSMutableArray alloc] init];
self.navigationItem.leftItemsSupplementBackButton = YES;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:_collectionView];
[self fetchMoreCatsWithCompletion:nil];
}
- (void)fetchMoreCatsWithCompletion:(void (^)(BOOL))completion {
if (kSimulateWebResponse) {
__weak typeof(self) weakSelf = self;
void(^mockWebService)() = ^{
NSLog(@"ViewController \"got data from a web service\"");
ViewController *strongSelf = weakSelf;
if (strongSelf != nil)
{
NSLog(@"ViewController is not nil");
[strongSelf appendMoreItems:kBatchSize completion:completion];
NSLog(@"ViewController finished updating collectionView");
}
else {
NSLog(@"ViewController is nil - won't update collectionView");
}
};
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kWebResponseDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), mockWebService);
} else {
[self appendMoreItems:kBatchSize completion:completion];
}
}
- (void)appendMoreItems:(NSInteger)numberOfNewItems completion:(void (^)(BOOL))completion {
NSArray *newData = [self getMoreData:numberOfNewItems];
dispatch_async(dispatch_get_main_queue(), ^{
[_collectionView performBatchUpdates:^{
[_data addObjectsFromArray:newData];
NSArray *addedIndexPaths = [self indexPathsForObjects:newData];
[_collectionView insertItemsAtIndexPaths:addedIndexPaths];
} completion:completion];
});
}
- (NSArray *)getMoreData:(NSInteger)count {
NSMutableArray *data = [NSMutableArray array];
for (int i = 0; i < count; i++) {
[data addObject:[ItemViewModel randomItem]];
}
return data;
}
- (NSArray *)indexPathsForObjects:(NSArray *)data {
NSMutableArray *indexPaths = [NSMutableArray array];
NSInteger section = 0;
for (ItemViewModel *viewModel in data) {
NSInteger item = [_data indexOfObject:viewModel];
NSAssert(item < [_data count] && item != NSNotFound, @"Item should be in _data");
[indexPaths addObject:[NSIndexPath indexPathForItem:item inSection:section]];
}
return indexPaths;
}
- (void)viewWillLayoutSubviews
{
_collectionView.frame = self.view.bounds;
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[_collectionView.collectionViewLayout invalidateLayout];
}
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (void)reloadTapped
{
[_collectionView reloadData];
}
#pragma mark -
#pragma mark ASCollectionView data source.
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath
{
ItemViewModel *viewModel = _data[indexPath.item];
return [[ItemNode alloc] initWithViewModel:viewModel];
}
- (ASCellNode *)collectionView:(UICollectionView *)collectionView nodeForSupplementaryElementOfKind:(nonnull NSString *)kind atIndexPath:(nonnull NSIndexPath *)indexPath {
if ([kind isEqualToString:UICollectionElementKindSectionHeader] && indexPath.section == 0) {
return [[BlurbNode alloc] init];
} else if ([kind isEqualToString:UICollectionElementKindSectionFooter] && indexPath.section == 0) {
return [[LoadingNode alloc] init];
}
return nil;
}
- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
if (section == 0) {
CGFloat width = CGRectGetWidth(self.view.frame) - 2 * kHorizontalSectionPadding;
return CGSizeMake(width, [BlurbNode desiredHeightForWidth:width]);
}
return CGSizeZero;
}
- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {
if (section == 0) {
CGFloat width = CGRectGetWidth(self.view.frame);
return CGSizeMake(width, [LoadingNode desiredHeightForWidth:width]);
}
return CGSizeZero;
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath {
CGFloat collectionViewWidth = CGRectGetWidth(self.view.frame) - 2 * kHorizontalSectionPadding;
CGFloat oneItemWidth = [ItemNode preferredViewSize].width;
NSInteger numColumns = floor(collectionViewWidth / oneItemWidth);
// Number of columns should be at least 1
numColumns = MAX(1, numColumns);
CGFloat totalSpaceBetweenColumns = (numColumns - 1) * kHorizontalSectionPadding;
CGFloat itemWidth = ((collectionViewWidth - totalSpaceBetweenColumns) / numColumns);
CGSize itemSize = [ItemNode sizeForWidth:itemWidth];
return ASSizeRangeMake(itemSize, itemSize);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [_data count];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView
{
// lock the data source
// The data source should not be change until it is unlocked.
}
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView
{
// unlock the data source to enable data source updating.
}
- (void)collectionView:(UICollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context
{
NSLog(@"fetch additional content");
[self fetchMoreCatsWithCompletion:^(BOOL finished){
[context completeBatchFetching:YES];
}];
}
- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(kVerticalSectionPadding, kHorizontalSectionPadding, kVerticalSectionPadding, kHorizontalSectionPadding);
}
-(void)dealloc
{
NSLog(@"ViewController is deallocing");
}
@end

View File

@@ -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 <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

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>