Add 'submodules/AsyncDisplayKit/' from commit '02bedc12816e251ad71777f9d2578329b6d2bef6'

git-subtree-dir: submodules/AsyncDisplayKit
git-subtree-mainline: d06f423e0e
git-subtree-split: 02bedc1281
This commit is contained in:
Peter
2019-06-11 18:42:43 +01:00
2160 changed files with 232035 additions and 0 deletions

View File

@@ -0,0 +1,551 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
3A2362FB1E2D33A0007E08F1 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2362FA1E2D33A0007E08F1 /* Date.swift */; };
3AB33F5E1E1F94530039F711 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F5D1E1F94530039F711 /* AppDelegate.swift */; };
3AB33F651E1F94530039F711 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AB33F641E1F94530039F711 /* Assets.xcassets */; };
3AB33F681E1F94530039F711 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3AB33F661E1F94530039F711 /* LaunchScreen.storyboard */; };
3AB33F761E1F9C330039F711 /* PhotoFeedTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F751E1F9C330039F711 /* PhotoFeedTableViewController.swift */; };
3AB33F781E1F9C400039F711 /* PhotoFeedTableNodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F771E1F9C400039F711 /* PhotoFeedTableNodeController.swift */; };
3AB33F7B1E1F9E630039F711 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F7A1E1F9E630039F711 /* UIColor.swift */; };
3AB33F811E1FDE100039F711 /* Webservice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F801E1FDE100039F711 /* Webservice.swift */; };
3AB33F831E20E81E0039F711 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F821E20E81E0039F711 /* Constants.swift */; };
3AB33F861E20E9B10039F711 /* PhotoFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F851E20E9B10039F711 /* PhotoFeedModel.swift */; };
3AB33F881E20ED460039F711 /* PhotoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F871E20ED460039F711 /* PhotoModel.swift */; };
3AB33F8C1E2106F30039F711 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F8B1E2106F30039F711 /* URL.swift */; };
3AB33F961E2269D40039F711 /* PopularPageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F951E2269D40039F711 /* PopularPageModel.swift */; };
3AB33F981E22A0080039F711 /* ParseResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F971E22A0080039F711 /* ParseResponse.swift */; };
3AB33F9E1E22D9DB0039F711 /* PhotoTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F9D1E22D9DB0039F711 /* PhotoTableViewCell.swift */; };
3AB33FA21E230A160039F711 /* NetworkImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33FA11E230A160039F711 /* NetworkImageView.swift */; };
3AB33FA41E2337850039F711 /* PhotoTableNodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33FA31E2337850039F711 /* PhotoTableNodeCell.swift */; };
692CD06E20E1A40D00D9B963 /* NumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692CD06D20E1A40D00D9B963 /* NumberFormatter.swift */; };
7E438240D2C4026931D60594 /* Pods_ASDKgram_Swift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D7D664E4FF432C4AE232A56 /* Pods_ASDKgram_Swift.framework */; };
9D4DFC5E20E1DF660067C960 /* OrderedDictionary+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D4DFC5B20E1DF660067C960 /* OrderedDictionary+Codable.swift */; };
9D4DFC5F20E1DF660067C960 /* OrderedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D4DFC5C20E1DF660067C960 /* OrderedDictionary.swift */; };
9D4DFC6020E1DF660067C960 /* OrderedDictionary+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D4DFC5D20E1DF660067C960 /* OrderedDictionary+Description.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
019E984FADA258377FC6B2D8 /* Pods-ASDKgram-Swift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ASDKgram-Swift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift.debug.xcconfig"; sourceTree = "<group>"; };
3A2362FA1E2D33A0007E08F1 /* Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
3AB33F5A1E1F94520039F711 /* ASDKgram-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ASDKgram-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; };
3AB33F5D1E1F94530039F711 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
3AB33F641E1F94530039F711 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3AB33F671E1F94530039F711 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
3AB33F691E1F94530039F711 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3AB33F751E1F9C330039F711 /* PhotoFeedTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoFeedTableViewController.swift; sourceTree = "<group>"; };
3AB33F771E1F9C400039F711 /* PhotoFeedTableNodeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoFeedTableNodeController.swift; sourceTree = "<group>"; };
3AB33F7A1E1F9E630039F711 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = "<group>"; };
3AB33F801E1FDE100039F711 /* Webservice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Webservice.swift; sourceTree = "<group>"; };
3AB33F821E20E81E0039F711 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
3AB33F851E20E9B10039F711 /* PhotoFeedModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoFeedModel.swift; sourceTree = "<group>"; };
3AB33F871E20ED460039F711 /* PhotoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoModel.swift; sourceTree = "<group>"; };
3AB33F8B1E2106F30039F711 /* URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
3AB33F951E2269D40039F711 /* PopularPageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopularPageModel.swift; sourceTree = "<group>"; };
3AB33F971E22A0080039F711 /* ParseResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseResponse.swift; sourceTree = "<group>"; };
3AB33F9D1E22D9DB0039F711 /* PhotoTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoTableViewCell.swift; sourceTree = "<group>"; };
3AB33FA11E230A160039F711 /* NetworkImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkImageView.swift; sourceTree = "<group>"; };
3AB33FA31E2337850039F711 /* PhotoTableNodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoTableNodeCell.swift; sourceTree = "<group>"; };
4D7D664E4FF432C4AE232A56 /* Pods_ASDKgram_Swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ASDKgram_Swift.framework; sourceTree = BUILT_PRODUCTS_DIR; };
692CD06D20E1A40D00D9B963 /* NumberFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberFormatter.swift; sourceTree = "<group>"; };
9D4DFC5B20E1DF660067C960 /* OrderedDictionary+Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Codable.swift"; sourceTree = "<group>"; };
9D4DFC5C20E1DF660067C960 /* OrderedDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderedDictionary.swift; sourceTree = "<group>"; };
9D4DFC5D20E1DF660067C960 /* OrderedDictionary+Description.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OrderedDictionary+Description.swift"; sourceTree = "<group>"; };
A3A86E74A8C3F06D7688AACB /* Pods-ASDKgram-Swift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ASDKgram-Swift.release.xcconfig"; path = "Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
3AB33F571E1F94520039F711 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7E438240D2C4026931D60594 /* Pods_ASDKgram_Swift.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
3AB33F511E1F94520039F711 = {
isa = PBXGroup;
children = (
3AB33F5C1E1F94530039F711 /* ASDKgram-Swift */,
3AB33F5B1E1F94520039F711 /* Products */,
78A64CA59A49BE1637214DF1 /* Pods */,
A7DD645D70CF34C7CA3B1A8B /* Frameworks */,
);
sourceTree = "<group>";
};
3AB33F5B1E1F94520039F711 /* Products */ = {
isa = PBXGroup;
children = (
3AB33F5A1E1F94520039F711 /* ASDKgram-Swift.app */,
);
name = Products;
sourceTree = "<group>";
};
3AB33F5C1E1F94530039F711 /* ASDKgram-Swift */ = {
isa = PBXGroup;
children = (
9D4DFC5A20E1DF660067C960 /* OrderedDictionary */,
3AB33F991E22CF160039F711 /* Views */,
3AB33F841E20E98C0039F711 /* Model */,
3AB33F7D1E1FDA890039F711 /* Client */,
3AB33F791E1F9E4E0039F711 /* Extensions */,
3AB33F721E1F9B650039F711 /* Controllers */,
3AB33F5D1E1F94530039F711 /* AppDelegate.swift */,
3AB33F821E20E81E0039F711 /* Constants.swift */,
3AB33F641E1F94530039F711 /* Assets.xcassets */,
3AB33F661E1F94530039F711 /* LaunchScreen.storyboard */,
3AB33F691E1F94530039F711 /* Info.plist */,
);
path = "ASDKgram-Swift";
sourceTree = "<group>";
};
3AB33F721E1F9B650039F711 /* Controllers */ = {
isa = PBXGroup;
children = (
3AB33F741E1F9B9F0039F711 /* ASDK */,
3AB33F731E1F9B950039F711 /* UIKit */,
);
name = Controllers;
sourceTree = "<group>";
};
3AB33F731E1F9B950039F711 /* UIKit */ = {
isa = PBXGroup;
children = (
3AB33F751E1F9C330039F711 /* PhotoFeedTableViewController.swift */,
);
name = UIKit;
sourceTree = "<group>";
};
3AB33F741E1F9B9F0039F711 /* ASDK */ = {
isa = PBXGroup;
children = (
3AB33F771E1F9C400039F711 /* PhotoFeedTableNodeController.swift */,
);
name = ASDK;
sourceTree = "<group>";
};
3AB33F791E1F9E4E0039F711 /* Extensions */ = {
isa = PBXGroup;
children = (
3A2362FA1E2D33A0007E08F1 /* Date.swift */,
692CD06D20E1A40D00D9B963 /* NumberFormatter.swift */,
3AB33F7A1E1F9E630039F711 /* UIColor.swift */,
3AB33F8B1E2106F30039F711 /* URL.swift */,
);
name = Extensions;
sourceTree = "<group>";
};
3AB33F7D1E1FDA890039F711 /* Client */ = {
isa = PBXGroup;
children = (
3AB33F801E1FDE100039F711 /* Webservice.swift */,
3AB33F971E22A0080039F711 /* ParseResponse.swift */,
);
name = Client;
sourceTree = "<group>";
};
3AB33F841E20E98C0039F711 /* Model */ = {
isa = PBXGroup;
children = (
3AB33F851E20E9B10039F711 /* PhotoFeedModel.swift */,
3AB33F871E20ED460039F711 /* PhotoModel.swift */,
3AB33F951E2269D40039F711 /* PopularPageModel.swift */,
);
name = Model;
sourceTree = "<group>";
};
3AB33F991E22CF160039F711 /* Views */ = {
isa = PBXGroup;
children = (
3AB33F9B1E22CF3C0039F711 /* UIKit */,
3AB33F9C1E22CF5C0039F711 /* ASDK */,
);
name = Views;
sourceTree = "<group>";
};
3AB33F9B1E22CF3C0039F711 /* UIKit */ = {
isa = PBXGroup;
children = (
3AB33F9D1E22D9DB0039F711 /* PhotoTableViewCell.swift */,
3AB33FA11E230A160039F711 /* NetworkImageView.swift */,
);
name = UIKit;
sourceTree = "<group>";
};
3AB33F9C1E22CF5C0039F711 /* ASDK */ = {
isa = PBXGroup;
children = (
3AB33FA31E2337850039F711 /* PhotoTableNodeCell.swift */,
);
name = ASDK;
sourceTree = "<group>";
};
78A64CA59A49BE1637214DF1 /* Pods */ = {
isa = PBXGroup;
children = (
019E984FADA258377FC6B2D8 /* Pods-ASDKgram-Swift.debug.xcconfig */,
A3A86E74A8C3F06D7688AACB /* Pods-ASDKgram-Swift.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
9D4DFC5A20E1DF660067C960 /* OrderedDictionary */ = {
isa = PBXGroup;
children = (
9D4DFC5B20E1DF660067C960 /* OrderedDictionary+Codable.swift */,
9D4DFC5C20E1DF660067C960 /* OrderedDictionary.swift */,
9D4DFC5D20E1DF660067C960 /* OrderedDictionary+Description.swift */,
);
path = OrderedDictionary;
sourceTree = "<group>";
};
A7DD645D70CF34C7CA3B1A8B /* Frameworks */ = {
isa = PBXGroup;
children = (
4D7D664E4FF432C4AE232A56 /* Pods_ASDKgram_Swift.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
3AB33F591E1F94520039F711 /* ASDKgram-Swift */ = {
isa = PBXNativeTarget;
buildConfigurationList = 3AB33F6C1E1F94530039F711 /* Build configuration list for PBXNativeTarget "ASDKgram-Swift" */;
buildPhases = (
A5A729883237749EE5D2DB1C /* [CP] Check Pods Manifest.lock */,
3AB33F561E1F94520039F711 /* Sources */,
3AB33F571E1F94520039F711 /* Frameworks */,
3AB33F581E1F94520039F711 /* Resources */,
154783123A953C3AFB9805CF /* [CP] Embed Pods Frameworks */,
3A7BEDD71E254278005769D4 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = "ASDKgram-Swift";
productName = "ASDKgram-Swift";
productReference = 3AB33F5A1E1F94520039F711 /* ASDKgram-Swift.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
3AB33F521E1F94520039F711 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0820;
LastUpgradeCheck = 0910;
ORGANIZATIONNAME = "Calum Harris";
TargetAttributes = {
3AB33F591E1F94520039F711 = {
CreatedOnToolsVersion = 8.2;
DevelopmentTeam = B3H446T9U7;
LastSwiftMigration = 0820;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 3AB33F551E1F94520039F711 /* Build configuration list for PBXProject "ASDKgram-Swift" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 3AB33F511E1F94520039F711;
productRefGroup = 3AB33F5B1E1F94520039F711 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
3AB33F591E1F94520039F711 /* ASDKgram-Swift */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
3AB33F581E1F94520039F711 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3AB33F681E1F94530039F711 /* LaunchScreen.storyboard in Resources */,
3AB33F651E1F94530039F711 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
154783123A953C3AFB9805CF /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/PINCache/PINCache.framework",
"${BUILT_PRODUCTS_DIR}/PINOperation/PINOperation.framework",
"${BUILT_PRODUCTS_DIR}/PINRemoteImage/PINRemoteImage.framework",
"${BUILT_PRODUCTS_DIR}/Texture/AsyncDisplayKit.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINCache.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINOperation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PINRemoteImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AsyncDisplayKit.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3A7BEDD71E254278005769D4 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "";
};
A5A729883237749EE5D2DB1C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-ASDKgram-Swift-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
3AB33F561E1F94520039F711 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3AB33F781E1F9C400039F711 /* PhotoFeedTableNodeController.swift in Sources */,
3A2362FB1E2D33A0007E08F1 /* Date.swift in Sources */,
3AB33F7B1E1F9E630039F711 /* UIColor.swift in Sources */,
3AB33F981E22A0080039F711 /* ParseResponse.swift in Sources */,
692CD06E20E1A40D00D9B963 /* NumberFormatter.swift in Sources */,
3AB33FA41E2337850039F711 /* PhotoTableNodeCell.swift in Sources */,
3AB33FA21E230A160039F711 /* NetworkImageView.swift in Sources */,
9D4DFC6020E1DF660067C960 /* OrderedDictionary+Description.swift in Sources */,
3AB33F8C1E2106F30039F711 /* URL.swift in Sources */,
3AB33F831E20E81E0039F711 /* Constants.swift in Sources */,
9D4DFC5F20E1DF660067C960 /* OrderedDictionary.swift in Sources */,
3AB33F961E2269D40039F711 /* PopularPageModel.swift in Sources */,
3AB33F5E1E1F94530039F711 /* AppDelegate.swift in Sources */,
9D4DFC5E20E1DF660067C960 /* OrderedDictionary+Codable.swift in Sources */,
3AB33F811E1FDE100039F711 /* Webservice.swift in Sources */,
3AB33F9E1E22D9DB0039F711 /* PhotoTableViewCell.swift in Sources */,
3AB33F861E20E9B10039F711 /* PhotoFeedModel.swift in Sources */,
3AB33F881E20ED460039F711 /* PhotoModel.swift in Sources */,
3AB33F761E1F9C330039F711 /* PhotoFeedTableViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
3AB33F661E1F94530039F711 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
3AB33F671E1F94530039F711 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
3AB33F6A1E1F94530039F711 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
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 = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
3AB33F6B1E1F94530039F711 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
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 = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
3AB33F6D1E1F94530039F711 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 019E984FADA258377FC6B2D8 /* Pods-ASDKgram-Swift.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
DEVELOPMENT_TEAM = B3H446T9U7;
INFOPLIST_FILE = "ASDKgram-Swift/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.RenaldoMoon.ASDKgram-Swift";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
3AB33F6E1E1F94530039F711 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A3A86E74A8C3F06D7688AACB /* Pods-ASDKgram-Swift.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
DEVELOPMENT_TEAM = B3H446T9U7;
INFOPLIST_FILE = "ASDKgram-Swift/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.RenaldoMoon.ASDKgram-Swift";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INSTALL_OBJC_HEADER = NO;
SWIFT_VERSION = 3.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
3AB33F551E1F94520039F711 /* Build configuration list for PBXProject "ASDKgram-Swift" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3AB33F6A1E1F94530039F711 /* Debug */,
3AB33F6B1E1F94530039F711 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
3AB33F6C1E1F94530039F711 /* Build configuration list for PBXNativeTarget "ASDKgram-Swift" */ = {
isa = XCConfigurationList;
buildConfigurations = (
3AB33F6D1E1F94530039F711 /* Debug */,
3AB33F6E1E1F94530039F711 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 3AB33F521E1F94520039F711 /* Project object */;
}

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,51 @@
//
// AppDelegate.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
import AsyncDisplayKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// UIKit Home Feed viewController & navController
let UIKitNavController = UINavigationController(rootViewController: PhotoFeedTableViewController())
UIKitNavController.tabBarItem.title = "UIKit"
// ASDK Home Feed viewController & navController
let ASDKNavController = UINavigationController(rootViewController: PhotoFeedTableNodeController())
ASDKNavController.tabBarItem.title = "ASDK"
// UITabBarController
let tabBarController = UITabBarController()
tabBarController.viewControllers = [UIKitNavController, ASDKNavController]
tabBarController.selectedIndex = 1
tabBarController.tabBar.tintColor = UIColor.mainBarTintColor
// Nav Bar appearance
UINavigationBar.appearance().barTintColor = UIColor.mainBarTintColor
// UIWindow
window = UIWindow()
window?.backgroundColor = .white
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
return true
}
}

View File

@@ -0,0 +1,93 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"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" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"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"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ASDKgram-Swift" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wcs-pb-4AR">
<rect key="frame" x="43" y="312.5" width="289.5" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Wcs-pb-4AR" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="9Hq-sR-C28"/>
<constraint firstItem="Wcs-pb-4AR" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="x6Z-mF-iaI"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,34 @@
//
// Constants.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
struct Constants {
struct Unsplash {
struct URLS {
static let Host = "https://api.unsplash.com/"
static let PopularEndpoint = "photos?order_by=popular"
static let SearchEndpoint = "photos/search?geo=" //latitude,longitude,radius<units>
static let UserEndpoint = "photos?user_id="
static let ConsumerKey = "&client_id=3b99a69cee09770a4a0bbb870b437dbda53efb22f6f6de63714b71c4df7c9642"
static let ImagesPerPage = 30
}
}
struct CellLayout {
static let FontSize: CGFloat = 14
static let HeaderHeight: CGFloat = 50
static let UserImageHeight: CGFloat = 30
static let HorizontalBuffer: CGFloat = 10
static let VerticalBuffer: CGFloat = 5
static let InsetForAvatar = UIEdgeInsets(top: HorizontalBuffer, left: 0, bottom: HorizontalBuffer, right: HorizontalBuffer)
static let InsetForHeader = UIEdgeInsets(top: 0, left: HorizontalBuffer, bottom: 0, right: HorizontalBuffer)
static let InsetForFooter = UIEdgeInsets(top: VerticalBuffer, left: HorizontalBuffer, bottom: VerticalBuffer, right: HorizontalBuffer)
}
}

View File

@@ -0,0 +1,39 @@
//
// Date.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
extension Date {
static let iso8601Formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
return formatter
}()
static func timeStringSince(fromConverted date: Date) -> String {
let diffDates = NSCalendar.current.dateComponents([.day, .hour, .second], from: date, to: Date())
if let week = diffDates.day, week > 7 {
return "\(week / 7)w"
} else if let day = diffDates.day, day > 0 {
return "\(day)d"
} else if let hour = diffDates.hour, hour > 0 {
return "\(hour)h"
} else if let second = diffDates.second, second > 0 {
return "\(second)s"
} else if let zero = diffDates.second, zero == 0 {
return "1s"
} else {
return "ERROR"
}
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</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,47 @@
//
// NetworkImageView.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
let imageCache = NSCache<NSString, UIImage>()
class NetworkImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingUrlString(urlString: String) {
imageUrlString = urlString
let url = URL(string: urlString)
image = nil
if let imageFromCache = imageCache.object(forKey: urlString as NSString) {
self.image = imageFromCache
return
}
URLSession.shared.dataTask(with: url!, completionHandler: { (data, respones, error) in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async {
let imageToCache = UIImage(data: data!)
if self.imageUrlString == urlString {
self.image = imageToCache
}
imageCache.setObject(imageToCache!, forKey: urlString as NSString)
}
}).resume()
}
}

View File

@@ -0,0 +1,18 @@
//
// NumberFormatter.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
extension NumberFormatter {
static let decimalNumberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
}

View File

@@ -0,0 +1,142 @@
//
// OrderedDictionary+Codable.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#if swift(>=4.1)
extension OrderedDictionary: Encodable where Key: Encodable, Value: Encodable {
/// __inheritdoc__
public func encode(to encoder: Encoder) throws {
// Encode the ordered dictionary as an array of alternating key-value pairs.
var container = encoder.unkeyedContainer()
for (key, value) in self {
try container.encode(key)
try container.encode(value)
}
}
}
extension OrderedDictionary: Decodable where Key: Decodable, Value: Decodable {
/// __inheritdoc__
public init(from decoder: Decoder) throws {
// Decode the ordered dictionary from an array of alternating key-value pairs.
self.init()
var container = try decoder.unkeyedContainer()
while !container.isAtEnd {
let key = try container.decode(Key.self)
guard !container.isAtEnd else { throw DecodingError.unkeyedContainerReachedEndBeforeValue(decoder.codingPath) }
let value = try container.decode(Value.self)
self[key] = value
}
}
}
#else
extension OrderedDictionary: Encodable {
/// __inheritdoc__
public func encode(to encoder: Encoder) throws {
// Since Swift 4.0 lacks the protocol conditional conformance support, we have to make the
// whole OrderedDictionary type conform to Encodable and assert that the key and value
// types conform to Encodable. Furthermore, we leverage a trick of super encoders to be
// able to encode objects without knowing their exact types. This trick was used in the
// standard library for encoding/decoding Dictionary before Swift 4.1.
_assertTypeIsEncodable(Key.self, in: type(of: self))
_assertTypeIsEncodable(Value.self, in: type(of: self))
var container = encoder.unkeyedContainer()
for (key, value) in self {
let keyEncoder = container.superEncoder()
try (key as! Encodable).encode(to: keyEncoder)
let valueEncoder = container.superEncoder()
try (value as! Encodable).encode(to: valueEncoder)
}
}
private func _assertTypeIsEncodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Encodable.Type else {
if T.self == Encodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Encodable because \(T.self) does not conform to Encodable.")
}
}
}
}
extension OrderedDictionary: Decodable {
/// __inheritdoc__
public init(from decoder: Decoder) throws {
// Since Swift 4.0 lacks the protocol conditional conformance support, we have to make the
// whole OrderedDictionary type conform to Decodable and assert that the key and value
// types conform to Decodable. Furthermore, we leverage a trick of super decoders to be
// able to decode objects without knowing their exact types. This trick was used in the
// standard library for encoding/decoding Dictionary before Swift 4.1.
self.init()
_assertTypeIsDecodable(Key.self, in: type(of: self))
_assertTypeIsDecodable(Value.self, in: type(of: self))
var container = try decoder.unkeyedContainer()
let keyMetaType = (Key.self as! Decodable.Type)
let valueMetaType = (Value.self as! Decodable.Type)
while !container.isAtEnd {
let keyDecoder = try container.superDecoder()
let key = try keyMetaType.init(from: keyDecoder) as! Key
guard !container.isAtEnd else { throw DecodingError.unkeyedContainerReachedEndBeforeValue(decoder.codingPath) }
let valueDecoder = try container.superDecoder()
let value = try valueMetaType.init(from: valueDecoder) as! Value
self[key] = value
}
}
private func _assertTypeIsDecodable<T>(_ type: T.Type, in wrappingType: Any.Type) {
guard T.self is Decodable.Type else {
if T.self == Decodable.self || T.self == Codable.self {
preconditionFailure("\(wrappingType) does not conform to Decodable because Decodable does not conform to itself. You must use a concrete type to encode or decode.")
} else {
preconditionFailure("\(wrappingType) does not conform to Decodable because \(T.self) does not conform to Decodable.")
}
}
}
}
#endif
fileprivate extension DecodingError {
fileprivate static func unkeyedContainerReachedEndBeforeValue(_ codingPath: [CodingKey]) -> DecodingError {
return DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: codingPath,
debugDescription: "Unkeyed container reached end before value in key-value pair."
)
)
}
}

View File

@@ -0,0 +1,58 @@
//
// OrderedDictionary+Description.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
extension OrderedDictionary: CustomStringConvertible {
/// A textual representation of the ordered dictionary.
public var description: String {
return makeDescription(debug: false)
}
}
extension OrderedDictionary: CustomDebugStringConvertible {
/// A textual representation of the ordered dictionary, suitable for debugging.
public var debugDescription: String {
return makeDescription(debug: true)
}
}
extension OrderedDictionary {
fileprivate func makeDescription(debug: Bool) -> String {
// The implementation of the description is inspired by zwaldowski's implementation of the
// ordered dictionary. See http://bit.ly/2iqGhrb
if isEmpty { return "[:]" }
let printFunction: (Any, inout String) -> () = {
if debug {
return { debugPrint($0, separator: "", terminator: "", to: &$1) }
} else {
return { print($0, separator: "", terminator: "", to: &$1) }
}
}()
let descriptionForItem: (Any) -> String = { item in
var description = ""
printFunction(item, &description)
return description
}
let bodyComponents = map { element in
return descriptionForItem(element.key) + ": " + descriptionForItem(element.value)
}
let body = bodyComponents.joined(separator: ", ")
return "[\(body)]"
}
}

View File

@@ -0,0 +1,618 @@
//
// OrderedDictionary.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
/// A generic collection for storing key-value pairs in an ordered manner.
///
/// Same as in a dictionary all keys in the collection are unique and have an associated value.
/// Same as in an array, all key-value pairs (elements) are kept sorted and accessible by
/// a zero-based integer index.
public struct OrderedDictionary<Key: Hashable, Value>: BidirectionalCollection {
// ======================================================= //
// MARK: - Type Aliases
// ======================================================= //
/// The type of the key-value pair stored in the ordered dictionary.
public typealias Element = (key: Key, value: Value)
/// The type of the index.
public typealias Index = Int
/// The type of the indices collection.
public typealias Indices = CountableRange<Int>
/// The type of the contiguous subrange of the ordered dictionary's elements.
///
/// - SeeAlso: OrderedDictionarySlice
public typealias SubSequence = OrderedDictionarySlice<Key, Value>
// ======================================================= //
// MARK: - Initialization
// ======================================================= //
/// Creates an empty ordered dictionary.
public init() {}
/// Creates an ordered dictionary from a sequence of values keyed by a key which gets extracted
/// from the value in the provided closure.
///
/// - Parameter values: The sequence of values.
/// - Parameter getKey: The closure which provides a key for the given value from the values
/// sequence.
public init<Values: Sequence>(values: Values, keyedBy getKey: (Value) -> Key) where Values.Element == Value {
self.init(values.map { (getKey($0), $0) })
}
/// Creates an ordered dictionary from a sequence of values keyed by a key loaded from the value
/// at the given key path.
///
/// - Parameter values: The sequence of values.
/// - Parameter keyPath: The key path for the value to locate its key at.
public init(values: [Value], keyedBy keyPath: KeyPath<Value, Key>) {
self.init(values.map { ($0[keyPath: keyPath], $0) })
}
/// Creates an ordered dictionary from a regular unsorted dictionary by sorting it using the
/// the given sort function.
///
/// - Parameter unsorted: The unsorted dictionary.
/// - Parameter areInIncreasingOrder: The sort function which compares the key-value pairs.
public init(unsorted: Dictionary<Key, Value>, areInIncreasingOrder: (Element, Element) -> Bool) {
let elements = unsorted
.map { (key: $0.key, value: $0.value) }
.sorted(by: areInIncreasingOrder)
self.init(elements)
}
/// Creates an ordered dictionary from a sequence of key-value pairs.
///
/// - Parameter elements: The key-value pairs that will make up the new ordered dictionary.
/// Each key in `elements` must be unique.
public init<S: Sequence>(_ elements: S) where S.Element == Element {
for (key, value) in elements {
precondition(!containsKey(key), "Elements sequence contains duplicate keys")
self[key] = value
}
}
// ======================================================= //
// MARK: - Ordered Keys & Values
// ======================================================= //
/// A collection containing just the keys of the ordered dictionary in the correct order.
public var orderedKeys: OrderedDictionaryKeys<Key, Value> {
return self.lazy.map { $0.key }
}
/// A collection containing just the values of the ordered dictionary in the correct order.
public var orderedValues: OrderedDictionaryValues<Key, Value> {
return self.lazy.map { $0.value }
}
// ======================================================= //
// MARK: - Dictionary
// ======================================================= //
/// Converts itself to a common unsorted dictionary.
public var unorderedDictionary: Dictionary<Key, Value> {
return _keysToValues
}
// ======================================================= //
// MARK: - Key-based Access
// ======================================================= //
/// Accesses the value associated with the given key for reading and writing.
///
/// This key-based subscript returns the value for the given key if the key is found in the
/// ordered dictionary, or `nil` if the key is not found.
///
/// When you assign a value for a key and that key already exists, the ordered dictionary
/// overwrites the existing value and preservers the index of the key-value pair. If the ordered
/// dictionary does not contain the key, a new key-value pair is appended to the end of the
/// ordered dictionary.
///
/// If you assign `nil` as the value for the given key, the ordered dictionary removes that key
/// and its associated value if it exists.
///
/// - Parameter key: The key to find in the ordered dictionary.
/// - Returns: The value associated with `key` if `key` is in the ordered dictionary; otherwise,
/// `nil`.
public subscript(key: Key) -> Value? {
get {
return value(forKey: key)
}
set(newValue) {
if let newValue = newValue {
updateValue(newValue, forKey: key)
} else {
removeValue(forKey: key)
}
}
}
/// Returns a Boolean value indicating whether the ordered dictionary contains the given key.
///
/// - Parameter key: The key to be looked up.
/// - Returns: `true` if the ordered dictionary contains the given key; otherwise, `false`.
public func containsKey(_ key: Key) -> Bool {
return _keysToValues[key] != nil
}
/// Returns the value associated with the given key if the key is found in the ordered
/// dictionary, or `nil` if the key is not found.
///
/// - Parameter key: The key to find in the ordered dictionary.
/// - Returns: The value associated with `key` if `key` is in the ordered dictionary; otherwise,
/// `nil`.
public func value(forKey key: Key) -> Value? {
return _keysToValues[key]
}
/// Updates the value stored in the ordered dictionary for the given key, or appends a new
/// key-value pair if the key does not exist.
///
/// - Parameter value: The new value to add to the ordered dictionary.
/// - Parameter key: The key to associate with `value`. If `key` already exists in the ordered
/// dictionary, `value` replaces the existing associated value. If `key` is not already a key
/// of the ordered dictionary, the `(key, value)` pair is appended at the end of the ordered
/// dictionary.
@discardableResult
public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? {
if containsKey(key) {
let currentValue = _unsafeValue(forKey: key)
_keysToValues[key] = value
return currentValue
} else {
_orderedKeys.append(key)
_keysToValues[key] = value
return nil
}
}
/// Removes the given key and its associated value from the ordered dictionary.
///
/// If the key is found in the ordered dictionary, this method returns the key's associated
/// value. On removal, the indices of the ordered dictionary are invalidated. If the key is
/// not found in the ordered dictionary, this method returns `nil`.
///
/// - Parameter key: The key to remove along with its associated value.
/// - Returns: The value that was removed, or `nil` if the key was not present in the
/// ordered dictionary.
///
/// - SeeAlso: remove(at:)
@discardableResult
public mutating func removeValue(forKey key: Key) -> Value? {
guard let index = index(forKey: key) else { return nil }
let currentValue = self[index].value
_orderedKeys.remove(at: index)
_keysToValues[key] = nil
return currentValue
}
/// Removes all key-value pairs from the ordered dictionary and invalidates all indices.
///
/// - Parameter keepCapacity: Whether the ordered dictionary should keep its underlying storage.
/// If you pass `true`, the operation preserves the storage capacity that the collection has,
/// otherwise the underlying storage is released. The default is `false`.
public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
_orderedKeys.removeAll(keepingCapacity: keepCapacity)
_keysToValues.removeAll(keepingCapacity: keepCapacity)
}
private func _unsafeValue(forKey key: Key) -> Value {
let value = _keysToValues[key]
precondition(value != nil, "Inconsistency error occurred in OrderedDictionary")
return value!
}
// ======================================================= //
// MARK: - Index-based Access
// ======================================================= //
/// Accesses the key-value pair at the specified position.
///
/// The specified position has to be a valid index of the ordered dictionary. The index-base
/// subscript returns the key-value pair corresponding to the index.
///
/// - Parameter position: The position of the key-value pair to access. `position` must be
/// a valid index of the ordered dictionary and not equal to `endIndex`.
/// - Returns: A tuple containing the key-value pair corresponding to `position`.
///
/// - SeeAlso: update(:at:)
public subscript(position: Index) -> Element {
precondition(indices.contains(position), "OrderedDictionary index is out of range")
let key = _orderedKeys[position]
let value = _unsafeValue(forKey: key)
return (key, value)
}
/// Returns the index for the given key.
///
/// - Parameter key: The key to find in the ordered dictionary.
/// - Returns: The index for `key` and its associated value if `key` is in the ordered
/// dictionary; otherwise, `nil`.
public func index(forKey key: Key) -> Index? {
return _orderedKeys.index(of: key)
}
/// Returns the key-value pair at the specified index, or `nil` if there is no key-value pair
/// at that index.
///
/// - Parameter index: The index of the key-value pair to be looked up. `index` does not have to
/// be a valid index.
/// - Returns: A tuple containing the key-value pair corresponding to `index` if the index is
/// valid; otherwise, `nil`.
public func elementAt(_ index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
}
/// Checks whether the given key-value pair can be inserted into to ordered dictionary by
/// validating the presence of the key.
///
/// - Parameter newElement: The key-value pair to be inserted into the ordered dictionary.
/// - Returns: `true` if the key-value pair can be safely inserted; otherwise, `false`.
///
/// - SeeAlso: canInsert(key:)
/// - SeeAlso: canInsert(at:)
@available(*, deprecated, message: "Use canInsert(key:) with the element's key instead")
public func canInsert(_ newElement: Element) -> Bool {
return canInsert(key: newElement.key)
}
/// Checks whether a key-value pair with the given key can be inserted into the ordered
/// dictionary by validating its presence.
///
/// - Parameter key: The key to be inserted into the ordered dictionary.
/// - Returns: `true` if the key can safely be inserted; ortherwise, `false`.
///
/// - SeeAlso: canInsert(at:)
public func canInsert(key: Key) -> Bool {
return !containsKey(key)
}
/// Checks whether a new key-value pair can be inserted into the ordered dictionary at the
/// given index.
///
/// - Parameter index: The index the new key-value pair should be inserted at.
/// - Returns: `true` if a new key-value pair can be inserted at the specified index; otherwise,
/// `false`.
///
/// - SeeAlso: canInsert(key:)
public func canInsert(at index: Index) -> Bool {
return index >= startIndex && index <= endIndex
}
/// Inserts a new key-value pair at the specified position.
///
/// If the key of the inserted pair already exists in the ordered dictionary, a runtime error
/// is triggered. Use `canInsert(_:)` for performing a check first, so that this method can
/// be executed safely.
///
/// - Parameter newElement: The new key-value pair to insert into the ordered dictionary. The
/// key contained in the pair must not be already present in the ordered dictionary.
/// - Parameter index: The position at which to insert the new key-value pair. `index` must be
/// a valid index of the ordered dictionary or equal to `endIndex` property.
///
/// - SeeAlso: canInsert(key:)
/// - SeeAlso: canInsert(at:)
/// - SeeAlso: update(:at:)
public mutating func insert(_ newElement: Element, at index: Index) {
precondition(canInsert(key: newElement.key), "Cannot insert duplicate key in OrderedDictionary")
precondition(canInsert(at: index), "Cannot insert at invalid index in OrderedDictionary")
let (key, value) = newElement
_orderedKeys.insert(key, at: index)
_keysToValues[key] = value
}
/// Checks whether the key-value pair at the given index can be updated with the given key-value
/// pair. This is not the case if the key of the updated element is already present in the
/// ordered dictionary and located at another index than the updated one.
///
/// Although this is a checking method, a valid index has to be provided.
///
/// - Parameter newElement: The key-value pair to be set at the specified position.
/// - Parameter index: The position at which to set the key-value pair. `index` must be a valid
/// index of the ordered dictionary.
public func canUpdate(_ newElement: Element, at index: Index) -> Bool {
var keyPresentAtIndex = false
return _canUpdate(newElement, at: index, keyPresentAtIndex: &keyPresentAtIndex)
}
/// Updates the key-value pair located at the specified position.
///
/// If the key of the updated pair already exists in the ordered dictionary *and* is located at
/// a different position than the specified one, a runtime error is triggered. Use
/// `canUpdate(_:at:)` for performing a check first, so that this method can be executed safely.
///
/// - Parameter newElement: The key-value pair to be set at the specified position.
/// - Parameter index: The position at which to set the key-value pair. `index` must be a valid
/// index of the ordered dictionary.
///
/// - SeeAlso: canUpdate(_:at:)
/// - SeeAlso: insert(:at:)
@discardableResult
public mutating func update(_ newElement: Element, at index: Index) -> Element? {
// Store the flag indicating whether the key of the inserted element
// is present at the updated index
var keyPresentAtIndex = false
precondition(
_canUpdate(newElement, at: index, keyPresentAtIndex: &keyPresentAtIndex),
"OrderedDictionary update duplicates key"
)
// Decompose the element
let (key, value) = newElement
// Load the current element at the index
let replacedElement = self[index]
// If its key differs, remove its associated value
if (!keyPresentAtIndex) {
_keysToValues.removeValue(forKey: replacedElement.key)
}
// Store the new position of the key and the new value associated with the key
_orderedKeys[index] = key
_keysToValues[key] = value
return replacedElement
}
/// Removes and returns the key-value pair at the specified position if there is any key-value
/// pair, or `nil` if there is none.
///
/// - Parameter index: The position of the key-value pair to remove.
/// - Returns: The element at the specified index, or `nil` if the position is not taken.
///
/// - SeeAlso: removeValue(forKey:)
@discardableResult
public mutating func remove(at index: Index) -> Element? {
guard let element = elementAt(index) else { return nil }
_orderedKeys.remove(at: index)
_keysToValues.removeValue(forKey: element.key)
return element
}
private func _canUpdate(_ newElement: Element, at index: Index, keyPresentAtIndex: inout Bool) -> Bool {
precondition(indices.contains(index), "OrderedDictionary index is out of range")
let currentIndexOfKey = self.index(forKey: newElement.key)
let keyNotPresent = currentIndexOfKey == nil
keyPresentAtIndex = currentIndexOfKey == index
return keyNotPresent || keyPresentAtIndex
}
// ======================================================= //
// MARK: - Moving Elements
// ======================================================= //
/// Moves an existing key-value pair specified by the given key to the new index by removing it
/// from its original index first and inserting it at the new index. If the movement is
/// actually performed, the previous index of the key-value pair is returned. Otherwise, `nil`
/// is returned.
///
/// - Parameter key: The key specifying the key-value pair to move.
/// - Parameter newIndex: The new index the key-value pair should be moved to.
/// - Returns: The previous index of the key-value pair if it was sucessfully moved.
@discardableResult
public mutating func moveElement(forKey key: Key, to newIndex: Index) -> Index? {
// Load the previous index and return nil if the index is not found.
guard let previousIndex = index(forKey: key) else { return nil }
// If the previous and new indices match, threat it as if the movement was already
// performed.
guard previousIndex != newIndex else { return previousIndex }
// Remove the value for the key at its original index.
let value = removeValue(forKey: key)!
// Validate the new index.
precondition(canInsert(at: newIndex), "Cannot move to invalid index in OrderedDictionary")
// Insert the element at the new index.
insert((key: key, value: value), at: newIndex)
return previousIndex
}
// ======================================================= //
// MARK: - Sorting Elements
// ======================================================= //
/// Sorts the ordered dictionary in place, using the given predicate as the comparison between
/// elements.
///
/// The predicate must be a *strict weak ordering* over the elements.
///
/// - Parameter areInIncreasingOrder: A predicate that returns `true` if its first argument
/// should be ordered before its second argument; otherwise, `false`.
///
/// - SeeAlso: MutableCollection.sort(by:), sorted(by:)
public mutating func sort(by areInIncreasingOrder: (Element, Element) -> Bool) {
_orderedKeys = _sortedElements(by: areInIncreasingOrder).map { $0.key }
}
/// Returns a new ordered dictionary, sorted using the given predicate as the comparison between
/// elements.
///
/// The predicate must be a *strict weak ordering* over the elements.
///
/// - Parameter areInIncreasingOrder: A predicate that returns `true` if its first argument
/// should be ordered before its second argument; otherwise, `false`.
/// - Returns: A new ordered dictionary sorted according to the predicate.
///
/// - SeeAlso: MutableCollection.sorted(by:), sort(by:)
/// - MutatingVariant: sort
public func sorted(by areInIncreasingOrder: (Element, Element) -> Bool) -> OrderedDictionary<Key, Value> {
return OrderedDictionary(_sortedElements(by: areInIncreasingOrder))
}
private func _sortedElements(by areInIncreasingOrder: (Element, Element) -> Bool) -> [Element] {
return sorted(by: areInIncreasingOrder)
}
// ======================================================= //
// MARK: - Slices
// ======================================================= //
/// Accesses a contiguous subrange of the ordered dictionary.
///
/// - Parameter bounds: A range of the ordered dictionary's indices. The bounds of the range
/// must be valid indices of the ordered dictionary.
/// - Returns: The slice view at the ordered dictionary in the specified subrange.
public subscript(bounds: Range<Index>) -> SubSequence {
return OrderedDictionarySlice(base: self, bounds: bounds)
}
// ======================================================= //
// MARK: - Indices
// ======================================================= //
/// The indices that are valid for subscripting the ordered dictionary.
public var indices: Indices {
return _orderedKeys.indices
}
/// The position of the first key-value pair in a non-empty ordered dictionary.
public var startIndex: Index {
return _orderedKeys.startIndex
}
/// The position which is one greater than the position of the last valid key-value pair in the
/// ordered dictionary.
public var endIndex: Index {
return _orderedKeys.endIndex
}
/// Returns the position immediately after the given index.
public func index(after i: Index) -> Index {
return _orderedKeys.index(after: i)
}
/// Returns the position immediately before the given index.
public func index(before i: Index) -> Index {
return _orderedKeys.index(before: i)
}
// ======================================================= //
// MARK: - Internal Storage
// ======================================================= //
/// The backing storage for the ordered keys.
fileprivate var _orderedKeys = [Key]()
/// The backing storage for the mapping of keys to values.
fileprivate var _keysToValues = [Key: Value]()
}
// ======================================================= //
// MARK: - Subtypes
// ======================================================= //
#if swift(>=4.1)
/// A view into an ordered dictionary whose indices are a subrange of the indices of the ordered
/// dictionary.
public typealias OrderedDictionarySlice<Key: Hashable, Value> = Slice<OrderedDictionary<Key, Value>>
/// A collection containing the keys of the ordered dictionary.
///
/// Under the hood this is a lazily evaluated bidirectional collection deriving the keys from
/// the base ordered dictionary on-the-fly.
public typealias OrderedDictionaryKeys<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Key>
/// A collection containing the values of the ordered dictionary.
///
/// Under the hood this is a lazily evaluated bidirectional collection deriving the values from
/// the base ordered dictionary on-the-fly.
public typealias OrderedDictionaryValues<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Value>
#else
/// A view into an ordered dictionary whose indices are a subrange of the indices of the ordered
/// dictionary.
public typealias OrderedDictionarySlice<Key: Hashable, Value> = Slice<OrderedDictionary<Key, Value>>
/// A collection containing the keys of the ordered dictionary.
///
/// Under the hood this is a lazily evaluated bidirectional collection deriving the keys from
/// the base ordered dictionary on-the-fly.
public typealias OrderedDictionaryKeys<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Key>
/// A collection containing the values of the ordered dictionary.
///
/// Under the hood this is a lazily evaluated bidirectional collection deriving the values from
/// the base ordered dictionary on-the-fly.
public typealias OrderedDictionaryValues<Key: Hashable, Value> = LazyMapCollection<OrderedDictionary<Key, Value>, Value>
#endif
// ======================================================= //
// MARK: - Literals
// ======================================================= //
extension OrderedDictionary: ExpressibleByArrayLiteral {
/// Creates an ordered dictionary initialized from an array literal containing a list of
/// key-value pairs.
public init(arrayLiteral elements: Element...) {
self.init(elements)
}
}
extension OrderedDictionary: ExpressibleByDictionaryLiteral {
/// Creates an ordered dictionary initialized from a dictionary literal.
public init(dictionaryLiteral elements: (Key, Value)...) {
self.init(elements.map { element in
let (key, value) = element
return (key: key, value: value)
})
}
}
// ======================================================= //
// MARK: - Equatable Conformance
// ======================================================= //
#if swift(>=4.1)
extension OrderedDictionary: Equatable where Value: Equatable {}
#endif
extension OrderedDictionary where Value: Equatable {
/// Returns a Boolean value that indicates whether the two given ordered dictionaries with
/// equatable values are equal.
public static func == (lhs: OrderedDictionary, rhs: OrderedDictionary) -> Bool {
return lhs._orderedKeys == rhs._orderedKeys
&& lhs._keysToValues == rhs._keysToValues
}
}

View File

@@ -0,0 +1,24 @@
//
// PX500Convenience.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
func parsePopularPage(withURL: URL) -> Resource<PopularPageModel> {
let parse = Resource<PopularPageModel>(url: withURL, parseJSON: { jsonData in
guard let json = jsonData as? JSONDictionary, let photos = json["photos"] as? [JSONDictionary] else { return .failure(.errorParsingJSON) }
guard let model = PopularPageModel(dictionary: json, photosArray: photos.flatMap(PhotoModel.init)) else { return .failure(.errorParsingJSON) }
return .success(model)
})
return parse
}

View File

@@ -0,0 +1,23 @@
//
// ParseResponse.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
func parsePopularPage(withURL: URL, page: Int) -> Resource<PopularPageModel> {
let parse = Resource<PopularPageModel>(url: withURL, page: page) { metaData, jsonData in
do {
let photos = try JSONDecoder().decode([PhotoModel].self, from: jsonData)
return .success(PopularPageModel(metaData: metaData, photos: photos))
} catch {
return .failure(.errorParsingJSON)
}
}
return parse
}

View File

@@ -0,0 +1,119 @@
//
// PhotoFeedModel.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
final class PhotoFeedModel {
// MARK: Properties
public private(set) var photoFeedModelType: PhotoFeedModelType
private var orderedPhotos: OrderedDictionary<String, PhotoModel> = [:]
private var currentPage: Int = 0
private var totalPages: Int = 0
private var totalItems: Int = 0
private var fetchPageInProgress: Bool = false
// MARK: Lifecycle
init(photoFeedModelType: PhotoFeedModelType) {
self.photoFeedModelType = photoFeedModelType
}
// MARK: API
lazy var url: URL = {
return URL.URLForFeedModelType(feedModelType: self.photoFeedModelType)
}()
var numberOfItems: Int {
return orderedPhotos.count
}
func itemAtIndexPath(_ indexPath: IndexPath) -> PhotoModel {
return orderedPhotos[indexPath.row].value
}
// return in completion handler the number of additions and the status of internet connection
func updateNewBatchOfPopularPhotos(additionsAndConnectionStatusCompletion: @escaping (Int, InternetStatus) -> ()) {
// For this example let's use the main thread as locking queue
DispatchQueue.main.async {
guard !self.fetchPageInProgress else {
return
}
self.fetchPageInProgress = true
self.fetchNextPageOfPopularPhotos(replaceData: false) { [unowned self] additions, error in
self.fetchPageInProgress = false
if let error = error {
switch error {
case .noInternetConnection:
additionsAndConnectionStatusCompletion(0, .noConnection)
default:
additionsAndConnectionStatusCompletion(0, .connected)
}
} else {
additionsAndConnectionStatusCompletion(additions, .connected)
}
}
}
}
private func fetchNextPageOfPopularPhotos(replaceData: Bool, numberOfAdditionsCompletion: @escaping (Int, NetworkingError?) -> ()) {
if currentPage == totalPages, currentPage != 0 {
numberOfAdditionsCompletion(0, .customError("No pages left to parse"))
return
}
let pageToFetch = currentPage + 1
WebService().load(resource: parsePopularPage(withURL: url, page: pageToFetch)) { [unowned self] result in
// Callback will happen on main for now
switch result {
case .success(let itemsPage):
// Update current state
self.totalItems = itemsPage.totalNumberOfItems
self.totalPages = itemsPage.totalPages
self.currentPage = itemsPage.page
// Update photos
if replaceData {
self.orderedPhotos = []
}
var insertedItems = 0
for photo in itemsPage.photos {
if !self.orderedPhotos.containsKey(photo.photoID) {
// Append a new key-value pair by setting a value for an non-existent key
self.orderedPhotos[photo.photoID] = photo
insertedItems += 1
}
}
numberOfAdditionsCompletion(insertedItems, nil)
case .failure(let fail):
print(fail)
numberOfAdditionsCompletion(0, fail)
}
}
}
}
enum PhotoFeedModelType {
case photoFeedModelTypePopular
case photoFeedModelTypeLocation
case photoFeedModelTypeUserPhotos
}
enum InternetStatus {
case connected
case noConnection
}

View File

@@ -0,0 +1,106 @@
//
// PhotoFeedTableNodeController.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
class PhotoFeedTableNodeController: ASViewController<ASTableNode> {
// MARK: Lifecycle
private lazy var activityIndicatorView: UIActivityIndicatorView = {
return UIActivityIndicatorView(activityIndicatorStyle: .gray)
}()
var photoFeedModel = PhotoFeedModel(photoFeedModelType: .photoFeedModelTypePopular)
init() {
super.init(node: ASTableNode())
navigationItem.title = "ASDK"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MAKR: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
node.allowsSelection = false
node.dataSource = self
node.delegate = self
node.leadingScreensForBatching = 2.5
node.view.separatorStyle = .none
navigationController?.hidesBarsOnSwipe = true
node.view.addSubview(activityIndicatorView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Center the activity indicator view
let bounds = node.bounds
activityIndicatorView.frame.origin = CGPoint(
x: (bounds.width - activityIndicatorView.frame.width) / 2.0,
y: (bounds.height - activityIndicatorView.frame.height) / 2.0
)
}
func fetchNewBatchWithContext(_ context: ASBatchContext?) {
DispatchQueue.main.async {
self.activityIndicatorView.startAnimating()
}
photoFeedModel.updateNewBatchOfPopularPhotos() { additions, connectionStatus in
switch connectionStatus {
case .connected:
self.activityIndicatorView.stopAnimating()
self.addRowsIntoTableNode(newPhotoCount: additions)
context?.completeBatchFetching(true)
case .noConnection:
self.activityIndicatorView.stopAnimating()
context?.completeBatchFetching(true)
}
}
}
func addRowsIntoTableNode(newPhotoCount newPhotos: Int) {
let indexRange = (photoFeedModel.numberOfItems - newPhotos..<photoFeedModel.numberOfItems)
let indexPaths = indexRange.map { IndexPath(row: $0, section: 0) }
node.insertRows(at: indexPaths, with: .none)
}
}
// MARK: ASTableDataSource / ASTableDelegate
extension PhotoFeedTableNodeController: ASTableDataSource, ASTableDelegate {
func tableNode(_ tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int {
return photoFeedModel.numberOfItems
}
func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
let photo = photoFeedModel.itemAtIndexPath(indexPath)
let nodeBlock: ASCellNodeBlock = { _ in
return PhotoTableNodeCell(photoModel: photo)
}
return nodeBlock
}
func shouldBatchFetchForCollectionNode(collectionNode: ASCollectionNode) -> Bool {
return true
}
func tableNode(_ tableNode: ASTableNode, willBeginBatchFetchWith context: ASBatchContext) {
fetchNewBatchWithContext(context)
}
}

View File

@@ -0,0 +1,108 @@
//
// PhotoFeedTableViewController.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
class PhotoFeedTableViewController: UITableViewController {
var activityIndicator: UIActivityIndicatorView!
var photoFeed = PhotoFeedModel(photoFeedModelType: .photoFeedModelTypePopular)
init() {
super.init(nibName: nil, bundle: nil)
navigationItem.title = "UIKit"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.hidesBarsOnSwipe = true
setupActivityIndicator()
configureTableView()
fetchNewBatch()
}
func fetchNewBatch() {
activityIndicator.startAnimating()
photoFeed.updateNewBatchOfPopularPhotos() { additions, connectionStatus in
switch connectionStatus {
case .connected:
self.activityIndicator.stopAnimating()
self.addRowsIntoTableView(newPhotoCount: additions)
case .noConnection:
self.activityIndicator.stopAnimating()
break
}
}
}
// Helper functions
func setupActivityIndicator() {
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicator.translatesAutoresizingMaskIntoConstraints = false
self.activityIndicator = activityIndicator
self.tableView.addSubview(activityIndicator)
NSLayoutConstraint.activate([
activityIndicator.centerXAnchor.constraint(equalTo: self.tableView.centerXAnchor),
activityIndicator.centerYAnchor.constraint(equalTo: self.tableView.centerYAnchor)
])
}
func configureTableView() {
tableView.register(PhotoTableViewCell.self, forCellReuseIdentifier: "photoCell")
tableView.allowsSelection = false
tableView.rowHeight = UITableViewAutomaticDimension
tableView.separatorStyle = .none
}
}
extension PhotoFeedTableViewController {
func addRowsIntoTableView(newPhotoCount newPhotos: Int) {
let indexRange = (photoFeed.numberOfItems - newPhotos..<photoFeed.numberOfItems)
let indexPaths = indexRange.map { IndexPath(row: $0, section: 0) }
tableView.insertRows(at: indexPaths, with: .none)
}
// TableView Data Source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return photoFeed.numberOfItems
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "photoCell", for: indexPath) as? PhotoTableViewCell else { fatalError("Wrong cell type") }
cell.photoModel = photoFeed.itemAtIndexPath(indexPath)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return PhotoTableViewCell.height(
for: photoFeed.itemAtIndexPath(indexPath),
withWidth: self.view.frame.size.width
)
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let currentOffSetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
let screenHeight = UIScreen.main.bounds.height
let screenfullsBeforeBottom = (contentHeight - currentOffSetY) / screenHeight
if screenfullsBeforeBottom < 2.5 {
self.fetchNewBatch()
}
}
}

View File

@@ -0,0 +1,133 @@
//
// PhotoModel.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
// MARK: ProfileImage
struct ProfileImage: Codable {
let large: String
let medium: String
let small: String
}
// MARK: UserModel
struct UserModel: Codable {
let userName: String
let profileImages: ProfileImage
enum CodingKeys: String, CodingKey {
case userName = "username"
case profileImages = "profile_image"
}
}
extension UserModel {
var profileImage: String {
return profileImages.medium
}
}
// MARK: PhotoURL
struct PhotoURL: Codable {
let full: String
let raw: String
let regular: String
let small: String
let thumb: String
}
// MARK: PhotoModel
struct PhotoModel: Codable {
let urls: PhotoURL
let photoID: String
let uploadedDateString: String
let descriptionText: String?
let likesCount: Int
let width: Int
let height: Int
let user: UserModel
enum CodingKeys: String, CodingKey {
case photoID = "id"
case urls = "urls"
case uploadedDateString = "created_at"
case descriptionText = "description"
case likesCount = "likes"
case width = "width"
case height = "height"
case user = "user"
}
}
extension PhotoModel {
var url: String {
return urls.regular
}
}
extension PhotoModel {
// MARK: - Attributed Strings
func attributedStringForUserName(withSize size: CGFloat) -> NSAttributedString {
let attributes = [
NSForegroundColorAttributeName : UIColor.darkGray,
NSFontAttributeName: UIFont.boldSystemFont(ofSize: size)
]
return NSAttributedString(string: user.userName, attributes: attributes)
}
func attributedStringForDescription(withSize size: CGFloat) -> NSAttributedString {
let attributes = [
NSForegroundColorAttributeName : UIColor.darkGray,
NSFontAttributeName: UIFont.systemFont(ofSize: size)
]
return NSAttributedString(string: descriptionText ?? "", attributes: attributes)
}
func attributedStringLikes(withSize size: CGFloat) -> NSAttributedString {
guard let formattedLikesNumber = NumberFormatter.decimalNumberFormatter.string(from: NSNumber(value: likesCount)) else {
return NSAttributedString()
}
let likesAttributes = [
NSForegroundColorAttributeName : UIColor.mainBarTintColor,
NSFontAttributeName: UIFont.systemFont(ofSize: size)
]
let likesAttrString = NSAttributedString(string: "\(formattedLikesNumber) Likes", attributes: likesAttributes)
let heartAttributes = [
NSForegroundColorAttributeName : UIColor.red,
NSFontAttributeName: UIFont.systemFont(ofSize: size)
]
let heartAttrString = NSAttributedString(string: "♥︎ ", attributes: heartAttributes)
let combine = NSMutableAttributedString()
combine.append(heartAttrString)
combine.append(likesAttrString)
return combine
}
func attributedStringForTimeSinceString(withSize size: CGFloat) -> NSAttributedString {
guard let date = Date.iso8601Formatter.date(from: self.uploadedDateString) else {
return NSAttributedString();
}
let attributes = [
NSForegroundColorAttributeName : UIColor.mainBarTintColor,
NSFontAttributeName: UIFont.systemFont(ofSize: size)
]
return NSAttributedString(string: Date.timeStringSince(fromConverted: date), attributes: attributes)
}
}

View File

@@ -0,0 +1,89 @@
//
// PhotoTableNodeCell.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
import AsyncDisplayKit
class PhotoTableNodeCell: ASCellNode {
// MARK: Properties
let usernameLabel = ASTextNode()
let timeIntervalLabel = ASTextNode()
let photoLikesLabel = ASTextNode()
let photoDescriptionLabel = ASTextNode()
let avatarImageNode: ASNetworkImageNode = {
let node = ASNetworkImageNode()
node.contentMode = .scaleAspectFill
// Set the imageModificationBlock for a rounded avatar
node.imageModificationBlock = ASImageNodeRoundBorderModificationBlock(0, nil)
return node
}()
let photoImageNode: ASNetworkImageNode = {
let node = ASNetworkImageNode()
node.contentMode = .scaleAspectFill
return node
}()
// MARK: Lifecycle
init(photoModel: PhotoModel) {
super.init()
automaticallyManagesSubnodes = true
photoImageNode.url = URL(string: photoModel.url)
avatarImageNode.url = URL(string: photoModel.user.profileImage)
usernameLabel.attributedText = photoModel.attributedStringForUserName(withSize: Constants.CellLayout.FontSize)
timeIntervalLabel.attributedText = photoModel.attributedStringForTimeSinceString(withSize: Constants.CellLayout.FontSize)
photoLikesLabel.attributedText = photoModel.attributedStringLikes(withSize: Constants.CellLayout.FontSize)
photoDescriptionLabel.attributedText = photoModel.attributedStringForDescription(withSize: Constants.CellLayout.FontSize)
}
// MARK: ASDisplayNode
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
// Header Stack
var headerChildren: [ASLayoutElement] = []
let headerStack = ASStackLayoutSpec.horizontal()
headerStack.alignItems = .center
avatarImageNode.style.preferredSize = CGSize(
width: Constants.CellLayout.UserImageHeight,
height: Constants.CellLayout.UserImageHeight
)
headerChildren.append(ASInsetLayoutSpec(insets: Constants.CellLayout.InsetForAvatar, child: avatarImageNode))
usernameLabel.style.flexShrink = 1.0
headerChildren.append(usernameLabel)
let spacer = ASLayoutSpec()
spacer.style.flexGrow = 1.0
headerChildren.append(spacer)
timeIntervalLabel.style.spacingBefore = Constants.CellLayout.HorizontalBuffer
headerChildren.append(timeIntervalLabel)
let footerStack = ASStackLayoutSpec.vertical()
footerStack.spacing = Constants.CellLayout.VerticalBuffer
footerStack.children = [photoLikesLabel, photoDescriptionLabel]
headerStack.children = headerChildren
let verticalStack = ASStackLayoutSpec.vertical()
verticalStack.children = [
ASInsetLayoutSpec(insets: Constants.CellLayout.InsetForHeader, child: headerStack),
ASRatioLayoutSpec(ratio: 1.0, child: photoImageNode),
ASInsetLayoutSpec(insets: Constants.CellLayout.InsetForFooter, child: footerStack)
]
return verticalStack
}
}

View File

@@ -0,0 +1,132 @@
//
// PhotoTableViewCell.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
class PhotoTableViewCell: UITableViewCell {
var photoModel: PhotoModel? {
didSet {
if let model = photoModel {
photoImageView.loadImageUsingUrlString(urlString: model.url)
avatarImageView.loadImageUsingUrlString(urlString: model.user.profileImage)
photoLikesLabel.attributedText = model.attributedStringLikes(withSize: Constants.CellLayout.FontSize)
usernameLabel.attributedText = model.attributedStringForUserName(withSize: Constants.CellLayout.FontSize)
timeIntervalLabel.attributedText = model.attributedStringForTimeSinceString(withSize: Constants.CellLayout.FontSize)
photoDescriptionLabel.attributedText = model.attributedStringForDescription(withSize: Constants.CellLayout.FontSize)
photoDescriptionLabel.sizeToFit()
var rect = photoDescriptionLabel.frame
let availableWidth = self.bounds.size.width - Constants.CellLayout.HorizontalBuffer * 2
rect.size = model.attributedStringForDescription(withSize: Constants.CellLayout.FontSize).boundingRect(with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size
photoDescriptionLabel.frame = rect
}
}
}
let photoImageView: NetworkImageView = {
let imageView = NetworkImageView()
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
let avatarImageView: NetworkImageView = {
let imageView = NetworkImageView()
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = Constants.CellLayout.UserImageHeight / 2
imageView.clipsToBounds = true
return imageView
}()
let usernameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let timeIntervalLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let photoLikesLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let photoDescriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 3
return label
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
addSubview(photoImageView)
addSubview(avatarImageView)
addSubview(usernameLabel)
addSubview(timeIntervalLabel)
addSubview(photoLikesLabel)
addSubview(photoDescriptionLabel)
setupConstraints()
}
func setupConstraints() {
NSLayoutConstraint.activate ([
//photoImageView
photoImageView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.CellLayout.HeaderHeight),
photoImageView.widthAnchor.constraint(equalTo: widthAnchor),
photoImageView.heightAnchor.constraint(equalTo: photoImageView.widthAnchor),
// avatarImageView
avatarImageView.leftAnchor.constraint(equalTo: leftAnchor, constant: Constants.CellLayout.HorizontalBuffer),
avatarImageView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.CellLayout.HorizontalBuffer),
avatarImageView.heightAnchor.constraint(equalToConstant: Constants.CellLayout.UserImageHeight),
avatarImageView.widthAnchor.constraint(equalTo: avatarImageView.heightAnchor),
// usernameLabel
usernameLabel.leftAnchor.constraint(equalTo: avatarImageView.rightAnchor, constant: Constants.CellLayout.HorizontalBuffer),
usernameLabel.rightAnchor.constraint(equalTo: timeIntervalLabel.leftAnchor, constant: -Constants.CellLayout.HorizontalBuffer),
usernameLabel.centerYAnchor.constraint(equalTo: avatarImageView.centerYAnchor),
// timeIntervalLabel
timeIntervalLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -Constants.CellLayout.HorizontalBuffer),
timeIntervalLabel.centerYAnchor.constraint(equalTo: avatarImageView.centerYAnchor),
// photoLikesLabel
photoLikesLabel.topAnchor.constraint(equalTo: photoImageView.bottomAnchor, constant: Constants.CellLayout.VerticalBuffer),
photoLikesLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: Constants.CellLayout.HorizontalBuffer),
// photoDescriptionLabel
photoDescriptionLabel.topAnchor.constraint(equalTo: photoLikesLabel.bottomAnchor, constant: Constants.CellLayout.VerticalBuffer),
photoDescriptionLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: Constants.CellLayout.HorizontalBuffer),
photoDescriptionLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -Constants.CellLayout.HorizontalBuffer),
photoDescriptionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.CellLayout.VerticalBuffer)
])
}
class func height(for photo: PhotoModel, withWidth width: CGFloat) -> CGFloat {
let photoHeight = width
let font = UIFont.systemFont(ofSize: Constants.CellLayout.FontSize)
let likesHeight = round(font.lineHeight)
let descriptionAttrString = photo.attributedStringForDescription(withSize: Constants.CellLayout.FontSize)
let availableWidth = width - Constants.CellLayout.HorizontalBuffer * 2
let descriptionHeight = descriptionAttrString.boundingRect(with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size.height
return likesHeight + descriptionHeight + photoHeight + Constants.CellLayout.HeaderHeight + Constants.CellLayout.VerticalBuffer * 3
}
}

View File

@@ -0,0 +1,24 @@
//
// PopularPageModel.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import Foundation
struct PopularPageModel {
let page: Int
let totalPages: Int
let totalNumberOfItems: Int
let photos: [PhotoModel]
init(metaData: ResponseMetadata, photos:[PhotoModel]) {
self.page = metaData.currentPage
self.totalPages = metaData.pagesTotal
self.totalNumberOfItems = metaData.itemsTotal
self.photos = photos
}
}

View File

@@ -0,0 +1,16 @@
//
// UIColor.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
extension UIColor {
static var mainBarTintColor: UIColor {
return UIColor(red: 69/255, green: 142/255, blue: 255/255, alpha: 1)
}
}

View File

@@ -0,0 +1,29 @@
//
// URL.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
extension URL {
static func URLForFeedModelType(feedModelType: PhotoFeedModelType) -> URL {
switch feedModelType {
case .photoFeedModelTypePopular:
return URL(string: assembleUnsplashURLString(endpoint: Constants.Unsplash.URLS.PopularEndpoint))!
case .photoFeedModelTypeLocation:
return URL(string: assembleUnsplashURLString(endpoint: Constants.Unsplash.URLS.SearchEndpoint))!
case .photoFeedModelTypeUserPhotos:
return URL(string: assembleUnsplashURLString(endpoint: Constants.Unsplash.URLS.UserEndpoint))!
}
}
private static func assembleUnsplashURLString(endpoint: String) -> String {
return Constants.Unsplash.URLS.Host + endpoint + Constants.Unsplash.URLS.ConsumerKey
}
}

View File

@@ -0,0 +1,109 @@
//
// Webservice.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
final class WebService {
/// Load a new resource. Callback is called on main
func load<A>(resource: Resource<A>, completion: @escaping (Result<A>) -> ()) {
URLSession.shared.dataTask(with: resource.url) { data, response, error in
// Check for errors in responses.
let result = self.checkForNetworkErrors(data, response, error)
DispatchQueue.main.async {
// Parsing should happen off main
switch result {
case .success(let data):
completion(resource.parse(data, response))
case .failure(let error):
completion(.failure(error))
}
}
}.resume()
}
}
extension WebService {
/// // Check for errors in responses.
fileprivate func checkForNetworkErrors(_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Result<Data> {
if let error = error {
switch error {
case URLError.notConnectedToInternet, URLError.timedOut:
return .failure(.noInternetConnection)
default:
return .failure(.returnedError(error))
}
}
if let response = response as? HTTPURLResponse, response.statusCode <= 200 && response.statusCode >= 299 {
return .failure((.invalidStatusCode("Request returned status code other than 2xx \(response)")))
}
guard let data = data else {
return .failure(.dataReturnedNil)
}
return .success(data)
}
}
struct ResponseMetadata {
let currentPage: Int
let itemsTotal: Int
let itemsPerPage: Int
}
extension ResponseMetadata {
var pagesTotal: Int {
return itemsTotal / itemsPerPage
}
}
struct Resource<A> {
let url: URL
let parse: (Data, URLResponse?) -> Result<A>
}
extension Resource {
init(url: URL, page: Int, parseResponse: @escaping (ResponseMetadata, Data) -> Result<A>) {
// Append extra data to url for paging
guard let url = URL(string: url.absoluteString.appending("&page=\(page)")) else {
fatalError("Malformed URL given");
}
self.url = url
self.parse = { data, response in
// Parse out metadata from header
guard let httpUrlResponse = response as? HTTPURLResponse,
let xTotalString = httpUrlResponse.allHeaderFields["x-total"] as? String,
let xTotal = Int(xTotalString),
let xPerPageString = httpUrlResponse.allHeaderFields["x-per-page"] as? String,
let xPerPage = Int(xPerPageString)
else {
return .failure(.errorParsingResponse)
}
let metadata = ResponseMetadata(currentPage: page, itemsTotal: xTotal, itemsPerPage: xPerPage)
return parseResponse(metadata, data)
}
}
}
enum Result<T> {
case success(T)
case failure(NetworkingError)
}
enum NetworkingError: Error {
case errorParsingResponse
case errorParsingJSON
case noInternetConnection
case dataReturnedNil
case returnedError(Error)
case invalidStatusCode(String)
case customError(String)
}

View File

@@ -0,0 +1,9 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'ASDKgram-Swift' do
use_frameworks!
inhibit_all_warnings!
pod 'Texture/PINRemoteImage', :path => '../..'
end

View File

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

View File

@@ -0,0 +1,381 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
5E6D34211DB4C9D000FB9B0A /* Sample.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E6D341F1DB4C9D000FB9B0A /* Sample.h */; settings = {ATTRIBUTES = (Public, ); }; };
FF896945AEA7EF2D9CD93A65 /* Pods_Sample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC767A9EB720B4BF08C89936 /* Pods_Sample.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
088AA6578212BE9BFBB07B70 /* 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>"; };
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
5C5154389F056C672F4E9EEA /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
5E6D341D1DB4C9D000FB9B0A /* Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5E6D341F1DB4C9D000FB9B0A /* Sample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Sample.h; sourceTree = "<group>"; };
5E6D34201DB4C9D000FB9B0A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5E6D34251DB4CA8E00FB9B0A /* libPods-Sample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-Sample.a"; path = "Pods/../build/Debug-iphoneos/libPods-Sample.a"; sourceTree = "<group>"; };
5E6D34271DB4CBAA00FB9B0A /* Sample.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Sample.playground; sourceTree = "<group>"; };
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
EC767A9EB720B4BF08C89936 /* Pods_Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FDF496F367580DF9280D36EA /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
5E6D34191DB4C9D000FB9B0A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FF896945AEA7EF2D9CD93A65 /* Pods_Sample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
05E2127819D4DB510098F589 = {
isa = PBXGroup;
children = (
5E6D341E1DB4C9D000FB9B0A /* Sample */,
05E2128219D4DB510098F589 /* Products */,
1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup;
children = (
5E6D341D1DB4C9D000FB9B0A /* Sample.framework */,
);
name = Products;
sourceTree = "<group>";
};
1A943BF0259746F18D6E423F /* Frameworks */ = {
isa = PBXGroup;
children = (
5E6D34251DB4CA8E00FB9B0A /* libPods-Sample.a */,
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */,
EC767A9EB720B4BF08C89936 /* Pods_Sample.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
1AE410B73DA5C3BD087ACDD7 /* Pods */ = {
isa = PBXGroup;
children = (
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */,
088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */,
FDF496F367580DF9280D36EA /* Pods-Sample.debug.xcconfig */,
5C5154389F056C672F4E9EEA /* Pods-Sample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
5E6D341E1DB4C9D000FB9B0A /* Sample */ = {
isa = PBXGroup;
children = (
5E6D341F1DB4C9D000FB9B0A /* Sample.h */,
5E6D34201DB4C9D000FB9B0A /* Info.plist */,
5E6D34271DB4CBAA00FB9B0A /* Sample.playground */,
);
path = Sample;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
5E6D341A1DB4C9D000FB9B0A /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
5E6D34211DB4C9D000FB9B0A /* Sample.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
5E6D341C1DB4C9D000FB9B0A /* Sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5E6D34221DB4C9D000FB9B0A /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
43927A700F47FC31FA2FB429 /* [CP] Check Pods Manifest.lock */,
5E6D34181DB4C9D000FB9B0A /* Sources */,
5E6D34191DB4C9D000FB9B0A /* Frameworks */,
5E6D341A1DB4C9D000FB9B0A /* Headers */,
5E6D341B1DB4C9D000FB9B0A /* Resources */,
0FF30A537A157312FD5042F7 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Sample;
productName = Sample;
productReference = 5E6D341D1DB4C9D000FB9B0A /* Sample.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
05E2127919D4DB510098F589 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
5E6D341C1DB4C9D000FB9B0A = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 05E2127819D4DB510098F589;
productRefGroup = 05E2128219D4DB510098F589 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
5E6D341C1DB4C9D000FB9B0A /* Sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
5E6D341B1DB4C9D000FB9B0A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0FF30A537A157312FD5042F7 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
43927A700F47FC31FA2FB429 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
5E6D34181DB4C9D000FB9B0A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
05E212A219D4DB510098F589 /* 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;
ENABLE_TESTABILITY = 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 = 10.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
05E212A319D4DB510098F589 /* 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 = 10.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
5E6D34231DB4C9D000FB9B0A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FDF496F367580DF9280D36EA /* Pods-Sample.debug.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "";
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = Sample/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
5E6D34241DB4C9D000FB9B0A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5C5154389F056C672F4E9EEA /* Pods-Sample.release.xcconfig */;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CODE_SIGN_IDENTITY = "";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = Sample/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
05E212A219D4DB510098F589 /* Debug */,
05E212A319D4DB510098F589 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5E6D34221DB4C9D000FB9B0A /* Build configuration list for PBXNativeTarget "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5E6D34231DB4C9D000FB9B0A /* Debug */,
5E6D34241DB4C9D000FB9B0A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 05E2127919D4DB510098F589 /* Project object */;
}

View File

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

View File

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

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@@ -0,0 +1,19 @@
//
// Sample.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
//! Project version number for Sample.
FOUNDATION_EXPORT double SampleVersionNumber;
//! Project version string for Sample.
FOUNDATION_EXPORT const unsigned char SampleVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Sample/PublicHeader.h>

View File

@@ -0,0 +1,52 @@
//
// Contents.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
extension HorizontalStackWithSpacer {
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
usernameNode.style.flexShrink = 1.0
postLocationNode.style.flexShrink = 1.0
let verticalStackSpec = ASStackLayoutSpec.vertical()
verticalStackSpec.style.flexShrink = 1.0
// if fetching post location data from server, check if it is available yet
if postLocationNode.attributedText != nil {
verticalStackSpec.children = [usernameNode, postLocationNode]
} else {
verticalStackSpec.children = [usernameNode]
}
let spacerSpec = ASLayoutSpec()
spacerSpec.style.flexGrow = 1.0
spacerSpec.style.flexShrink = 1.0
// horizontal stack
let horizontalStack = ASStackLayoutSpec.horizontal()
horizontalStack.alignItems = .center // center items vertically in horiz stack
horizontalStack.justifyContent = .start // justify content to left
horizontalStack.style.flexShrink = 1.0
horizontalStack.style.flexGrow = 1.0
horizontalStack.children = [verticalStackSpec, spacerSpec, postTimeNode]
// inset horizontal stack
let insets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
let headerInsetSpec = ASInsetLayoutSpec(insets: insets, child: horizontalStack)
headerInsetSpec.style.flexShrink = 1.0
headerInsetSpec.style.flexGrow = 1.0
return headerInsetSpec
}
}
HorizontalStackWithSpacer().show()
//: [Index](Index)

View File

@@ -0,0 +1,7 @@
//
// Contents.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//

View File

@@ -0,0 +1,32 @@
//
// Contents.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
let userImageHeight = 60
extension PhotoWithInsetTextOverlay {
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
photoNode.style.preferredSize = CGSize(width: userImageHeight * 2, height: userImageHeight * 2)
let backgroundImageAbsoluteSpec = ASAbsoluteLayoutSpec(children: [photoNode])
let insets = UIEdgeInsets(top: CGFloat.infinity, left: 12, bottom: 12, right: 12)
let textInsetSpec = ASInsetLayoutSpec(insets: insets,
child: titleNode)
let textOverlaySpec = ASOverlayLayoutSpec(child: backgroundImageAbsoluteSpec, overlay: textInsetSpec)
return textOverlaySpec
}
}
PhotoWithInsetTextOverlay().show()
//: [Photo With Outset Icon Overlay](PhotoWithOutsetIconOverlay)

View File

@@ -0,0 +1,34 @@
//
// Contents.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
extension PhotoWithOutsetIconOverlay {
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
let iconWidth: CGFloat = 40
let iconHeight: CGFloat = 40
iconNode.style.preferredSize = CGSize(width: iconWidth, height: iconWidth)
photoNode.style.preferredSize = CGSize(width: 150, height: 150)
let x: CGFloat = 150
let y: CGFloat = 0
iconNode.style.layoutPosition = CGPoint(x: x, y: y)
photoNode.style.layoutPosition = CGPoint(x: iconWidth * 0.5, y: iconHeight * 0.5);
let absoluteLayoutSpec = ASAbsoluteLayoutSpec(children: [photoNode, iconNode])
return absoluteLayoutSpec;
}
}
PhotoWithOutsetIconOverlay().show()
//: [Horizontal Stack With Spacer](HorizontalStackWithSpacer)

View File

@@ -0,0 +1,34 @@
//
// Contents.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
extension StackLayout {
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
// Try commenting out the flexShrink to see its consequences.
subtitleNode.style.flexShrink = 1.0
let stackSpec = ASStackLayoutSpec(direction: .horizontal,
spacing: 5,
justifyContent: .start,
alignItems: .start,
children: [titleNode, subtitleNode])
let insetSpec = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 5,
left: 5,
bottom: 5,
right: 5),
child: stackSpec)
return insetSpec
}
}
StackLayout().show()
//: [Photo With Inset Text Overlay](PhotoWithInsetTextOverlay)

View File

@@ -0,0 +1,33 @@
//
// ASPlayground.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import PlaygroundSupport
import AsyncDisplayKit
public protocol ASPlayground: class {
func display(inRect: CGRect)
}
extension ASPlayground {
public func display(inRect rect: CGRect) {
var rect = rect
if rect.size == .zero {
rect.size = CGSize(width: 400, height: 400)
}
guard let nodeSelf = self as? ASDisplayNode else {
assertionFailure("Class inheriting ASPlayground must be an ASDisplayNode")
return
}
let constrainedSize = ASSizeRange(min: rect.size, max: rect.size)
_ = ASCalculateRootLayout(nodeSelf, constrainedSize)
nodeSelf.frame = rect
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = nodeSelf.view
}
}

View File

@@ -0,0 +1,45 @@
//
// HorizontalStackWithSpacer.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
fileprivate let fontSize: CGFloat = 20
public class HorizontalStackWithSpacer: ASDisplayNode, ASPlayground {
public let usernameNode = ASTextNode()
public let postLocationNode = ASTextNode()
public let postTimeNode = ASTextNode()
override public init() {
super.init()
backgroundColor = .white
automaticallyManagesSubnodes = true
setupNodes()
}
private func setupNodes() {
usernameNode.backgroundColor = .yellow
usernameNode.attributedText = NSAttributedString.attributedString(string: "hannahmbanana", fontSize: fontSize, color: .darkBlueColor(), firstWordColor: nil)
postLocationNode.backgroundColor = .lightGray
postLocationNode.maximumNumberOfLines = 1;
postLocationNode.attributedText = NSAttributedString.attributedString(string: "San Fransisco, CA", fontSize: fontSize, color: .lightBlueColor(), firstWordColor: nil)
postTimeNode.backgroundColor = .brown
postTimeNode.attributedText = NSAttributedString.attributedString(string: "30m", fontSize: fontSize, color: .lightGray, firstWordColor: nil)
}
// This is used to expose this function for overriding in extensions
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
return ASLayoutSpec()
}
public func show() {
display(inRect: CGRect(x: 0, y: 0, width: 450, height: 100))
}
}

View File

@@ -0,0 +1,40 @@
//
// PhotoWithInsetTextOverlay.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
public class PhotoWithInsetTextOverlay: ASDisplayNode, ASPlayground {
public let photoNode = ASNetworkImageNode()
public let titleNode = ASTextNode()
override public init() {
super.init()
backgroundColor = .white
automaticallyManagesSubnodes = true
setupNodes()
}
private func setupNodes() {
photoNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png")
photoNode.backgroundColor = .black
titleNode.backgroundColor = .blue
titleNode.maximumNumberOfLines = 2
titleNode.truncationAttributedText = NSAttributedString.attributedString(string: "...", fontSize: 16, color: .white, firstWordColor: nil)
titleNode.attributedText = NSAttributedString.attributedString(string: "family fall hikes", fontSize: 16, color: .white, firstWordColor: nil)
}
// This is used to expose this function for overriding in extensions
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
return ASLayoutSpec()
}
public func show() {
display(inRect: CGRect(x: 0, y: 0, width: 120, height: 120))
}
}

View File

@@ -0,0 +1,40 @@
//
// PhotoWithOutsetIconOverlay.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
fileprivate let userImageHeight = 60
public class PhotoWithOutsetIconOverlay: ASDisplayNode, ASPlayground {
public let photoNode = ASNetworkImageNode()
public let iconNode = ASNetworkImageNode()
override public init() {
super.init()
backgroundColor = .white
automaticallyManagesSubnodes = true
setupNodes()
}
private func setupNodes() {
photoNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png")
photoNode.backgroundColor = .black
iconNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png")
iconNode.imageModificationBlock = ASImageNodeRoundBorderModificationBlock(10, .white)
}
// This is used to expose this function for overriding in extensions
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
return ASLayoutSpec()
}
public func show() {
display(inRect: CGRect(x: 0, y: 0, width: 190, height: 190))
}
}

View File

@@ -0,0 +1,38 @@
//
// StackLayout.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import AsyncDisplayKit
public class StackLayout: ASDisplayNode, ASPlayground {
public let titleNode = ASTextNode()
public let subtitleNode = ASTextNode()
override public init() {
super.init()
backgroundColor = .white
automaticallyManagesSubnodes = true
setupNodes()
}
private func setupNodes() {
titleNode.backgroundColor = .blue
titleNode.attributedText = NSAttributedString.attributedString(string: "Headline!", fontSize: 14, color: .white, firstWordColor: nil)
subtitleNode.backgroundColor = .yellow
subtitleNode.attributedText = NSAttributedString(string: "Lorem ipsum dolor sit amet, sed ex laudem utroque meliore, at cum lucilius vituperata. Ludus mollis consulatu mei eu, esse vocent epicurei sed at. Ut cum recusabo prodesset. Ut cetero periculis sed, mundi senserit est ut. Nam ut sonet mandamus intellegebat, summo voluptaria vim ad.")
}
// This is used to expose this function for overriding in extensions
override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
return ASLayoutSpec()
}
public func show() {
display(inRect: .zero)
}
}

View File

@@ -0,0 +1,50 @@
//
// Utilities.swift
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
import Foundation
extension UIColor {
static func darkBlueColor() -> UIColor {
return UIColor(red: 18.0/255.0, green: 86.0/255.0, blue: 136.0/255.0, alpha: 1.0)
}
static func lightBlueColor() -> UIColor {
return UIColor(red: 0.0, green: 122.0/255.0, blue: 1.0, alpha: 1.0)
}
static func duskColor() -> UIColor {
return UIColor(red: 255/255.0, green: 181/255.0, blue: 68/255.0, alpha: 1.0)
}
static func customOrangeColor() -> UIColor {
return UIColor(red: 40/255.0, green: 43/255.0, blue: 53/255.0, alpha: 1.0)
}
}
extension NSAttributedString {
static func attributedString(string: String, fontSize size: CGFloat, color: UIColor?, firstWordColor: UIColor?) -> NSAttributedString {
let attributes = [NSForegroundColorAttributeName: color ?? UIColor.black,
NSFontAttributeName: UIFont.boldSystemFont(ofSize: size)]
let attributedString = NSMutableAttributedString(string: string, attributes: attributes)
if let firstWordColor = firstWordColor {
let nsString = string as NSString
let firstSpaceRange = nsString.rangeOfCharacter(from: NSCharacterSet.whitespaces)
let firstWordRange = NSMakeRange(0, firstSpaceRange.location)
attributedString.addAttribute(NSForegroundColorAttributeName, value: firstWordColor, range: firstWordRange)
}
return attributedString
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='ios' display-mode='rendered' timelineScrubberEnabled='true'>
<pages>
<page name='Index'/>
<page name='StackLayout'/>
<page name='PhotoWithInsetTextOverlay'/>
<page name='PhotoWithOutsetIconOverlay'/>
<page name='HorizontalStackWithSpacer'/>
</pages>
</playground>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

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

View File

@@ -0,0 +1,364 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */; };
05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; };
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; };
05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; };
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; };
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; };
881AF5D3D4458C15BACC8930 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D65D3016E9D596BDDD17FA44 /* libPods-Sample.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0431779F19E096F3CEC4D269 /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = "<group>"; };
05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; };
6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; };
D65D3016E9D596BDDD17FA44 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
DBA49A0CCF4CA8FC1F96CB6D /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
05E2127E19D4DB510098F589 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
881AF5D3D4458C15BACC8930 /* libPods-Sample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
05E2127819D4DB510098F589 = {
isa = PBXGroup;
children = (
05E2128319D4DB510098F589 /* Sample */,
05E2128219D4DB510098F589 /* Products */,
1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup;
children = (
05E2128119D4DB510098F589 /* Sample.app */,
);
name = Products;
sourceTree = "<group>";
};
05E2128319D4DB510098F589 /* Sample */ = {
isa = PBXGroup;
children = (
05E2128819D4DB510098F589 /* AppDelegate.h */,
05E2128919D4DB510098F589 /* AppDelegate.m */,
05E2128B19D4DB510098F589 /* ViewController.h */,
05E2128C19D4DB510098F589 /* ViewController.m */,
05E2128419D4DB510098F589 /* Supporting Files */,
);
path = Sample;
sourceTree = "<group>";
};
05E2128419D4DB510098F589 /* Supporting Files */ = {
isa = PBXGroup;
children = (
0585427F19D4DBE100606EA6 /* Default-568h@2x.png */,
6C2C82AA19EE274300767484 /* Default-667h@2x.png */,
6C2C82AB19EE274300767484 /* Default-736h@3x.png */,
05E2128519D4DB510098F589 /* Info.plist */,
05E2128619D4DB510098F589 /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
1A943BF0259746F18D6E423F /* Frameworks */ = {
isa = PBXGroup;
children = (
D65D3016E9D596BDDD17FA44 /* libPods-Sample.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
1AE410B73DA5C3BD087ACDD7 /* Pods */ = {
isa = PBXGroup;
children = (
DBA49A0CCF4CA8FC1F96CB6D /* Pods-Sample.debug.xcconfig */,
0431779F19E096F3CEC4D269 /* Pods-Sample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
05E2128019D4DB510098F589 /* Sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
E080B80F89C34A25B3488E26 /* [CP] Check Pods Manifest.lock */,
05E2127D19D4DB510098F589 /* Sources */,
05E2127E19D4DB510098F589 /* Frameworks */,
05E2127F19D4DB510098F589 /* Resources */,
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */,
75CADB9ECE58AB74892E1D67 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Sample;
productName = Sample;
productReference = 05E2128119D4DB510098F589 /* Sample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
05E2127919D4DB510098F589 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
05E2128019D4DB510098F589 = {
CreatedOnToolsVersion = 6.0.1;
};
};
};
buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 05E2127819D4DB510098F589;
productRefGroup = 05E2128219D4DB510098F589 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
05E2128019D4DB510098F589 /* Sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
05E2127F19D4DB510098F589 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */,
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */,
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
75CADB9ECE58AB74892E1D67 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
E080B80F89C34A25B3488E26 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Sample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
05E2127D19D4DB510098F589 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
05E2128719D4DB510098F589 /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
05E212A219D4DB510098F589 /* 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 = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
05E212A319D4DB510098F589 /* 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 = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
05E212A519D4DB510098F589 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DBA49A0CCF4CA8FC1F96CB6D /* Pods-Sample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Sample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
05E212A619D4DB510098F589 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0431779F19E096F3CEC4D269 /* Pods-Sample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = Sample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
05E212A219D4DB510098F589 /* Debug */,
05E212A319D4DB510098F589 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
05E212A519D4DB510098F589 /* Debug */,
05E212A619D4DB510098F589 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 05E2127919D4DB510098F589 /* 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 = "0630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "05E2128019D4DB510098F589"
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 = "05E2128019D4DB510098F589"
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 = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

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

View File

@@ -0,0 +1,16 @@
//
// AppDelegate.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,47 @@
//
// AppDelegate.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "AppDelegate.h"
#import "ViewController.h"
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.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;
UIViewController *viewController = [[ViewController alloc] init];
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Another Copy" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)];
[navController pushViewController:viewController animated:animated];
}
- (void)pushNewViewController
{
[self pushNewViewControllerAnimated:YES];
}
@end

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.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>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,14 @@
//
// ViewController.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

View File

@@ -0,0 +1,192 @@
//
// ViewController.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "ViewController.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASAssert.h>
#define NumberOfSections 10
#define NumberOfRowsPerSection 20
#define NumberOfReloadIterations 50
typedef enum : NSUInteger {
ReloadData,
ReloadRows,
ReloadSections,
ReloadTypeMax
} ReloadType;
@interface ViewController () <ASTableViewDataSource, ASTableViewDelegate>
{
ASTableView *_tableView;
NSMutableArray *_sections; // Contains arrays of indexPaths representing rows
}
@end
@implementation ViewController
- (instancetype)init
{
if (!(self = [super init]))
return nil;
_tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView.asyncDataSource = self;
_tableView.asyncDelegate = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_sections = [NSMutableArray arrayWithCapacity:NumberOfSections];
for (int i = 0; i < NumberOfSections; i++) {
NSMutableArray *rowsArray = [NSMutableArray arrayWithCapacity:NumberOfRowsPerSection];
for (int j = 0; j < NumberOfRowsPerSection; j++) {
[rowsArray addObject:[NSIndexPath indexPathForRow:j inSection:i]];
}
[_sections addObject:rowsArray];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view addSubview:_tableView];
}
- (void)viewWillLayoutSubviews
{
_tableView.frame = self.view.bounds;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self thrashTableView];
}
- (NSIndexSet *)randomIndexSet
{
u_int32_t upperBound = (u_int32_t)_sections.count - 1;
u_int32_t randA = arc4random_uniform(upperBound);
u_int32_t randB = arc4random_uniform(upperBound);
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(MIN(randA, randB), MAX(randA, randB) - MIN(randA, randB))];
}
- (NSArray *)randomIndexPathsExisting:(BOOL)existing
{
NSMutableArray *indexPaths = [NSMutableArray array];
[[self randomIndexSet] enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger rowNum = [self tableView:_tableView numberOfRowsInSection:idx];
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
for (NSUInteger i = (existing ? 0 : rowNum); i < (existing ? rowNum : rowNum * 2); i++) {
// Maximize evility by sporadically skipping indicies 1/3rd of the time, but only if reloading existing rows
if (existing && arc4random_uniform(2) == 0) {
continue;
}
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
[indexPaths addObject:indexPath];
}
}];
return indexPaths;
}
- (void)thrashTableView
{
[_tableView reloadData];
NSArray *indexPathsAddedAndRemoved = nil;
for (int i = 0; i < NumberOfReloadIterations; ++i) {
UITableViewRowAnimation rowAnimation = (arc4random_uniform(1) == 0 ? UITableViewRowAnimationMiddle : UITableViewRowAnimationNone);
BOOL animatedScroll = (arc4random_uniform(2) == 0 ? YES : NO);
ReloadType reloadType = (arc4random_uniform(ReloadTypeMax));
BOOL letRunloopProceed = (arc4random_uniform(2) == 0 ? YES : NO);
BOOL useBeginEndUpdates = (arc4random_uniform(3) == 0 ? YES : NO);
// FIXME: Need to revise the logic to support mutating the data source rather than just reload thrashing.
// UITableView itself does not support deleting a row in the same edit transaction as reloading it, for example.
BOOL addIndexPaths = NO; //(arc4random_uniform(2) == 0 ? YES : NO);
if (useBeginEndUpdates) {
[_tableView beginUpdates];
}
switch (reloadType) {
case ReloadData:
[_tableView reloadData];
break;
case ReloadRows:
[_tableView reloadRowsAtIndexPaths:[self randomIndexPathsExisting:YES] withRowAnimation:rowAnimation];
break;
case ReloadSections:
[_tableView reloadSections:[self randomIndexSet] withRowAnimation:rowAnimation];
break;
default:
break;
}
if (addIndexPaths && !indexPathsAddedAndRemoved) {
indexPathsAddedAndRemoved = [self randomIndexPathsExisting:NO];
for (NSIndexPath *indexPath in indexPathsAddedAndRemoved) {
[_sections[indexPath.section] addObject:indexPath];
}
[_tableView insertRowsAtIndexPaths:indexPathsAddedAndRemoved withRowAnimation:rowAnimation];
}
[_tableView setContentOffset:CGPointMake(0, arc4random_uniform(_tableView.contentSize.height - _tableView.bounds.size.height)) animated:animatedScroll];
if (letRunloopProceed) {
// Run other stuff on the main queue for between 2ms and 1000ms.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:(1 / (1 + arc4random_uniform(500)))]];
if (indexPathsAddedAndRemoved) {
for (NSIndexPath *indexPath in indexPathsAddedAndRemoved) {
[_sections[indexPath.section] removeObjectIdenticalTo:indexPath];
}
[_tableView deleteRowsAtIndexPaths:indexPathsAddedAndRemoved withRowAnimation:rowAnimation];
indexPathsAddedAndRemoved = nil;
}
}
if (useBeginEndUpdates) {
[_tableView endUpdates];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [(NSArray *)[_sections objectAtIndex:section] count];
}
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
ASTextCellNode *textCellNode = [ASTextCellNode new];
textCellNode.text = indexPath.description;
return textCellNode;
}
@end

View File

@@ -0,0 +1,18 @@
//
// main.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#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,5 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
target 'Sample' do
pod 'Texture', :path => '../..'
end

View File

@@ -0,0 +1,383 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; };
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; };
05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; };
1BEECAB53F4B61DCB949ED44 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EEDFC574739077BA65E0CF5 /* libPods-Sample.a */; };
9C37D01E1CC94BC9004C8BC1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C37D01D1CC94BC9004C8BC1 /* Launch Screen.storyboard */; };
9CACC7811CCEAF9E009A1613 /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CACC7801CCEAF9E009A1613 /* TableViewController.m */; };
9CACC7841CCEAFAE009A1613 /* CollectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CACC7831CCEAFAE009A1613 /* CollectionViewController.m */; };
9CACC7871CCEBD3B009A1613 /* KittenNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CACC7861CCEBD3B009A1613 /* KittenNode.m */; };
9CACC78A1CCEC82C009A1613 /* OverrideViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CACC7891CCEC82C009A1613 /* OverrideViewController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
056298286C03B7760575CC56 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
088AA6578212BE9BFBB07B70 /* 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>"; };
1EEDFC574739077BA65E0CF5 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
9C37D01D1CC94BC9004C8BC1 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
9CACC77F1CCEAF9E009A1613 /* TableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableViewController.h; sourceTree = "<group>"; };
9CACC7801CCEAF9E009A1613 /* TableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableViewController.m; sourceTree = "<group>"; };
9CACC7821CCEAFAE009A1613 /* CollectionViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionViewController.h; sourceTree = "<group>"; };
9CACC7831CCEAFAE009A1613 /* CollectionViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionViewController.m; sourceTree = "<group>"; };
9CACC7851CCEBD3B009A1613 /* KittenNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KittenNode.h; sourceTree = "<group>"; };
9CACC7861CCEBD3B009A1613 /* KittenNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KittenNode.m; sourceTree = "<group>"; };
9CACC7881CCEC82C009A1613 /* OverrideViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OverrideViewController.h; sourceTree = "<group>"; };
9CACC7891CCEC82C009A1613 /* OverrideViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OverrideViewController.m; sourceTree = "<group>"; };
A7F0013FBBCBEA0C9FB68986 /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
05E2127E19D4DB510098F589 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1BEECAB53F4B61DCB949ED44 /* libPods-Sample.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
05E2127819D4DB510098F589 = {
isa = PBXGroup;
children = (
05E2128319D4DB510098F589 /* Sample */,
05E2128219D4DB510098F589 /* Products */,
1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
usesTabs = 0;
};
05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup;
children = (
05E2128119D4DB510098F589 /* Sample.app */,
);
name = Products;
sourceTree = "<group>";
};
05E2128319D4DB510098F589 /* Sample */ = {
isa = PBXGroup;
children = (
05E2128819D4DB510098F589 /* AppDelegate.h */,
05E2128919D4DB510098F589 /* AppDelegate.m */,
05E2128B19D4DB510098F589 /* ViewController.h */,
05E2128C19D4DB510098F589 /* ViewController.m */,
05E2128419D4DB510098F589 /* Supporting Files */,
9CACC77F1CCEAF9E009A1613 /* TableViewController.h */,
9CACC7801CCEAF9E009A1613 /* TableViewController.m */,
9CACC7821CCEAFAE009A1613 /* CollectionViewController.h */,
9CACC7831CCEAFAE009A1613 /* CollectionViewController.m */,
9CACC7851CCEBD3B009A1613 /* KittenNode.h */,
9CACC7861CCEBD3B009A1613 /* KittenNode.m */,
9CACC7881CCEC82C009A1613 /* OverrideViewController.h */,
9CACC7891CCEC82C009A1613 /* OverrideViewController.m */,
);
path = Sample;
sourceTree = "<group>";
};
05E2128419D4DB510098F589 /* Supporting Files */ = {
isa = PBXGroup;
children = (
05E2128519D4DB510098F589 /* Info.plist */,
05E2128619D4DB510098F589 /* main.m */,
9C37D01D1CC94BC9004C8BC1 /* Launch Screen.storyboard */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
1A943BF0259746F18D6E423F /* Frameworks */ = {
isa = PBXGroup;
children = (
3D24B17D1E4A4E7A9566C5E9 /* libPods.a */,
1EEDFC574739077BA65E0CF5 /* libPods-Sample.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
1AE410B73DA5C3BD087ACDD7 /* Pods */ = {
isa = PBXGroup;
children = (
C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */,
088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */,
056298286C03B7760575CC56 /* Pods-Sample.debug.xcconfig */,
A7F0013FBBCBEA0C9FB68986 /* Pods-Sample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
05E2128019D4DB510098F589 /* Sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
E080B80F89C34A25B3488E26 /* 📦 Check Pods Manifest.lock */,
05E2127D19D4DB510098F589 /* Sources */,
05E2127E19D4DB510098F589 /* Frameworks */,
05E2127F19D4DB510098F589 /* Resources */,
F012A6F39E0149F18F564F50 /* 📦 Copy Pods Resources */,
FFF65E837E66ADA71296F0FF /* 📦 Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Sample;
productName = Sample;
productReference = 05E2128119D4DB510098F589 /* Sample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
05E2127919D4DB510098F589 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
05E2128019D4DB510098F589 = {
CreatedOnToolsVersion = 6.0.1;
};
};
};
buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 05E2127819D4DB510098F589;
productRefGroup = 05E2128219D4DB510098F589 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
05E2128019D4DB510098F589 /* Sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
05E2127F19D4DB510098F589 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9C37D01E1CC94BC9004C8BC1 /* Launch Screen.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
E080B80F89C34A25B3488E26 /* 📦 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;
};
F012A6F39E0149F18F564F50 /* 📦 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-Sample/Pods-Sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
FFF65E837E66ADA71296F0FF /* 📦 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-Sample/Pods-Sample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
05E2127D19D4DB510098F589 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
9CACC78A1CCEC82C009A1613 /* OverrideViewController.m in Sources */,
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
05E2128719D4DB510098F589 /* main.m in Sources */,
9CACC7841CCEAFAE009A1613 /* CollectionViewController.m in Sources */,
9CACC7871CCEBD3B009A1613 /* KittenNode.m in Sources */,
9CACC7811CCEAF9E009A1613 /* TableViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
05E212A219D4DB510098F589 /* 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 = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
05E212A319D4DB510098F589 /* 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 = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
05E212A519D4DB510098F589 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 056298286C03B7760575CC56 /* Pods-Sample.debug.xcconfig */;
buildSettings = {
INFOPLIST_FILE = Sample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
05E212A619D4DB510098F589 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = A7F0013FBBCBEA0C9FB68986 /* Pods-Sample.release.xcconfig */;
buildSettings = {
INFOPLIST_FILE = Sample/Info.plist;
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 */
05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
05E212A219D4DB510098F589 /* Debug */,
05E212A319D4DB510098F589 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
05E212A519D4DB510098F589 /* Debug */,
05E212A619D4DB510098F589 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 05E2127919D4DB510098F589 /* 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 = "05E2128019D4DB510098F589"
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 = "05E2128019D4DB510098F589"
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 = "05E2128019D4DB510098F589"
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 = "05E2128019D4DB510098F589"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

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

View File

@@ -0,0 +1,18 @@
//
// AppDelegate.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
#define UseAutomaticLayout 1
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,29 @@
//
// AppDelegate.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "AppDelegate.h"
#import "ViewController.h"
#import "TableViewController.h"
#import "CollectionViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
UITabBarController *tabController = [[UITabBarController alloc] init];
[tabController setViewControllers:@[[[ViewController alloc] init], [[TableViewController alloc] init], [[CollectionViewController alloc] init]]];
self.window.rootViewController = tabController;
[self.window makeKeyAndVisible];
return YES;
}
@end

View File

@@ -0,0 +1,13 @@
//
// CollectionViewController.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface CollectionViewController : ASViewController<ASCollectionNode *>
@end

View File

@@ -0,0 +1,71 @@
//
// CollectionViewController.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "CollectionViewController.h"
#import "KittenNode.h"
#import <AsyncDisplayKit/ASTraitCollection.h>
@interface CollectionViewController () <ASCollectionDelegate, ASCollectionDataSource>
@property (nonatomic, strong) ASCollectionNode *collectionNode;
@end
@implementation CollectionViewController
- (instancetype)init
{
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.minimumLineSpacing = 10;
layout.minimumInteritemSpacing = 10;
ASCollectionNode *collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout];
if (!(self = [super initWithNode:collectionNode]))
return nil;
self.title = @"Collection Node";
_collectionNode = collectionNode;
collectionNode.dataSource = self;
collectionNode.delegate = self;
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.collectionNode.view.contentInset = UIEdgeInsetsMake(20, 10, CGRectGetHeight(self.tabBarController.tabBar.frame), 10);
}
#pragma mark - ASCollectionDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return 50;
}
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath
{
KittenNode *cell = [[KittenNode alloc] init];
cell.textNode.maximumNumberOfLines = 3;
cell.imageTappedBlock = ^{
[KittenNode defaultImageTappedAction:self];
};
return cell;
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
ASTraitCollection *traitCollection = [self.collectionNode asyncTraitCollection];
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
return ASSizeRangeMake(CGSizeMake(200, 120), CGSizeMake(200, 120));
}
return ASSizeRangeMake(CGSizeMake(132, 180), CGSizeMake(132, 180));
}
@end

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>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>Launch Screen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,21 @@
//
// KittenNode.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface KittenNode : ASCellNode
@property (nonatomic, strong, readonly) ASNetworkImageNode *imageNode;
@property (nonatomic, strong, readonly) ASTextNode *textNode;
@property (nonatomic, copy) dispatch_block_t imageTappedBlock;
// The default action when an image node is tapped. This action will create an
// OverrideVC and override its display traits to always be compact.
+ (void)defaultImageTappedAction:(ASViewController *)sourceViewController;
@end

View File

@@ -0,0 +1,167 @@
//
// KittenNode.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "KittenNode.h"
#import "OverrideViewController.h"
#import <AsyncDisplayKit/ASTraitCollection.h>
static const CGFloat kOuterPadding = 16.0f;
static const CGFloat kInnerPadding = 10.0f;
@interface KittenNode ()
{
CGSize _kittenSize;
}
@end
@implementation KittenNode
// lorem ipsum text courtesy https://kittyipsum.com/ <3
+ (NSArray *)placeholders
{
static NSArray *placeholders = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
placeholders = @[
@"Kitty ipsum dolor sit amet, purr sleep on your face lay down in your way biting, sniff tincidunt a etiam fluffy fur judging you stuck in a tree kittens.",
@"Lick tincidunt a biting eat the grass, egestas enim ut lick leap puking climb the curtains lick.",
@"Lick quis nunc toss the mousie vel, tortor pellentesque sunbathe orci turpis non tail flick suscipit sleep in the sink.",
@"Orci turpis litter box et stuck in a tree, egestas ac tempus et aliquam elit.",
@"Hairball iaculis dolor dolor neque, nibh adipiscing vehicula egestas dolor aliquam.",
@"Sunbathe fluffy fur tortor faucibus pharetra jump, enim jump on the table I don't like that food catnip toss the mousie scratched.",
@"Quis nunc nam sleep in the sink quis nunc purr faucibus, chase the red dot consectetur bat sagittis.",
@"Lick tail flick jump on the table stretching purr amet, rhoncus scratched jump on the table run.",
@"Suspendisse aliquam vulputate feed me sleep on your keyboard, rip the couch faucibus sleep on your keyboard tristique give me fish dolor.",
@"Rip the couch hiss attack your ankles biting pellentesque puking, enim suspendisse enim mauris a.",
@"Sollicitudin iaculis vestibulum toss the mousie biting attack your ankles, puking nunc jump adipiscing in viverra.",
@"Nam zzz amet neque, bat tincidunt a iaculis sniff hiss bibendum leap nibh.",
@"Chase the red dot enim puking chuf, tristique et egestas sniff sollicitudin pharetra enim ut mauris a.",
@"Sagittis scratched et lick, hairball leap attack adipiscing catnip tail flick iaculis lick.",
@"Neque neque sleep in the sink neque sleep on your face, climb the curtains chuf tail flick sniff tortor non.",
@"Ac etiam kittens claw toss the mousie jump, pellentesque rhoncus litter box give me fish adipiscing mauris a.",
@"Pharetra egestas sunbathe faucibus ac fluffy fur, hiss feed me give me fish accumsan.",
@"Tortor leap tristique accumsan rutrum sleep in the sink, amet sollicitudin adipiscing dolor chase the red dot.",
@"Knock over the lamp pharetra vehicula sleep on your face rhoncus, jump elit cras nec quis quis nunc nam.",
@"Sollicitudin feed me et ac in viverra catnip, nunc eat I don't like that food iaculis give me fish.",
];
});
return placeholders;
}
- (instancetype)init
{
if (!(self = [super init]))
return nil;
_kittenSize = CGSizeMake(100,100);
// kitten image, with a solid background colour serving as placeholder
_imageNode = [[ASNetworkImageNode alloc] init];
_imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
_imageNode.style.preferredSize = _kittenSize;
[_imageNode addTarget:self action:@selector(imageTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
CGFloat scale = [UIScreen mainScreen].scale;
_imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"https://placekitten.com/%zd/%zd?image=%zd",
(NSInteger)roundl(_kittenSize.width * scale),
(NSInteger)roundl(_kittenSize.height * scale),
(NSInteger)arc4random_uniform(20)]];
[self addSubnode:_imageNode];
// lorem ipsum text, plus some nice styling
_textNode = [[ASTextNode alloc] init];
_textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum]
attributes:[self textStyle]];
_textNode.style.flexShrink = 1.0;
_textNode.style.flexGrow = 1.0;
[self addSubnode:_textNode];
return self;
}
- (void)imageTapped:(id)sender
{
if (self.imageTappedBlock) {
self.imageTappedBlock();
}
}
- (NSString *)kittyIpsum
{
NSArray *placeholders = [KittenNode placeholders];
u_int32_t ipsumCount = (u_int32_t)[placeholders count];
u_int32_t location = arc4random_uniform(ipsumCount);
u_int32_t length = arc4random_uniform(ipsumCount - location);
NSMutableString *string = [placeholders[location] mutableCopy];
for (u_int32_t i = location + 1; i < location + length; i++) {
[string appendString:(i % 2 == 0) ? @"\n" : @" "];
[string appendString:placeholders[i]];
}
return string;
}
- (NSDictionary *)textStyle
{
UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:12.0f];
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
style.paragraphSpacing = 0.5 * font.lineHeight;
style.hyphenationFactor = 1.0;
return @{ NSFontAttributeName: font,
NSParagraphStyleAttributeName: style,
ASTextNodeWordKerningAttributeName : @.5};
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init];
stackSpec.spacing = kInnerPadding;
[stackSpec setChildren:@[_imageNode, _textNode]];
if (self.asyncTraitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
_imageNode.style.alignSelf = ASStackLayoutAlignSelfStart;
stackSpec.direction = ASStackLayoutDirectionHorizontal;
} else {
_imageNode.style.alignSelf = ASStackLayoutAlignSelfCenter;
stackSpec.direction = ASStackLayoutDirectionVertical;
}
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding) child:stackSpec];
}
+ (void)defaultImageTappedAction:(ASViewController *)sourceViewController
{
OverrideViewController *overrideVC = [[OverrideViewController alloc] init];
__weak OverrideViewController *weakOverrideVC = overrideVC;
overrideVC.overrideDisplayTraitsWithTraitCollection = ^(UITraitCollection *traitCollection) {
ASTraitCollection *asyncTraitCollection = [ASTraitCollection traitCollectionWithDisplayScale:traitCollection.displayScale
userInterfaceIdiom:traitCollection.userInterfaceIdiom
horizontalSizeClass:UIUserInterfaceSizeClassCompact
verticalSizeClass:UIUserInterfaceSizeClassCompact
forceTouchCapability:traitCollection.forceTouchCapability
containerSize:weakOverrideVC.view.bounds.size];
return asyncTraitCollection;
};
[sourceViewController presentViewController:overrideVC animated:YES completion:nil];
overrideVC.closeBlock = ^{
[sourceViewController dismissViewControllerAnimated:YES completion:nil];
};
}
@end

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright © 2016 Facebook. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
<rect key="frame" x="20" y="559" width="560" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display Traits" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
<rect key="frame" x="20" y="180" width="560" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
<constraint firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" constant="20" symbolic="YES" id="SfN-ll-jLj"/>
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" constant="20" symbolic="YES" id="x7j-FC-K8j"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="625" y="488"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,27 @@
//
// OverrideViewController.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
/*
* A simple node that displays the attribution for the kitties in the app. Note that
* for a regular horizontal size class it does something stupid and sets the font size to 100.
* It's VC, OverrideViewController, will have its display traits overridden such that
* it will always have a compact horizontal size class.
*/
@interface OverrideNode : ASDisplayNode
@end
/*
* This is a fairly stupid VC that's main purpose is to show how to override ASDisplayTraits.
* Take a look at `defaultImageTappedAction` in KittenNode to see how this is accomplished.
*/
@interface OverrideViewController : ASViewController<OverrideNode *>
@property (nonatomic, copy) dispatch_block_t closeBlock;
@end

View File

@@ -0,0 +1,95 @@
//
// OverrideViewController.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "OverrideViewController.h"
#import <AsyncDisplayKit/ASTraitCollection.h>
static NSString *kLinkAttributeName = @"PlaceKittenNodeLinkAttributeName";
@interface OverrideNode()
@property (nonatomic, strong) ASTextNode *textNode;
@property (nonatomic, strong) ASButtonNode *buttonNode;
@end
@implementation OverrideNode
- (instancetype)init
{
if (!(self = [super init]))
return nil;
_textNode = [[ASTextNode alloc] init];
_textNode.style.flexGrow = 1.0;
_textNode.style.flexShrink = 1.0;
_textNode.maximumNumberOfLines = 3;
[self addSubnode:_textNode];
_buttonNode = [[ASButtonNode alloc] init];
[_buttonNode setAttributedTitle:[[NSAttributedString alloc] initWithString:@"Close"] forState:UIControlStateNormal];
[self addSubnode:_buttonNode];
self.backgroundColor = [UIColor lightGrayColor];
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
CGFloat pointSize = 16.f;
ASTraitCollection *traitCollection = [self asyncTraitCollection];
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
// This should never happen because we override the VC's display traits to always be compact.
pointSize = 100;
}
NSString *blurb = @"kittens courtesy placekitten.com";
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb];
[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue" size:pointSize] range:NSMakeRange(0, blurb.length)];
[string addAttributes:@{
kLinkAttributeName: [NSURL URLWithString:@"http://placekitten.com/"],
NSForegroundColorAttributeName: [UIColor grayColor],
NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
}
range:[blurb rangeOfString:@"placekitten.com"]];
_textNode.attributedText = string;
ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
stackSpec.children = @[_textNode, _buttonNode];
stackSpec.spacing = 10;
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(40, 20, 20, 20) child:stackSpec];
}
@end
@interface OverrideViewController ()
@end
@implementation OverrideViewController
- (instancetype)init
{
OverrideNode *overrideNode = [[OverrideNode alloc] init];
if (!(self = [super initWithNode:overrideNode]))
return nil;
[overrideNode.buttonNode addTarget:self action:@selector(closeTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
return self;
}
- (void)closeTapped:(id)sender
{
if (self.closeBlock) {
self.closeBlock();
}
}
@end

View File

@@ -0,0 +1,14 @@
//
// TableViewController.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface TableViewController : ASViewController<ASTableNode *>
@end

View File

@@ -0,0 +1,60 @@
//
// TableViewController.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "TableViewController.h"
#import "KittenNode.h"
@interface TableViewController () <ASTableViewDataSource, ASTableViewDelegate>
@property (nonatomic, strong) ASTableNode *tableNode;
@end
@implementation TableViewController
- (instancetype)init
{
ASTableNode *tableNode = [[ASTableNode alloc] init];
if (!(self = [super initWithNode:tableNode]))
return nil;
_tableNode = tableNode;
tableNode.delegate = self;
tableNode.dataSource = self;
self.title = @"Table Node";
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableNode.view.contentInset = UIEdgeInsetsMake(CGRectGetHeight([[UIApplication sharedApplication] statusBarFrame]), 0, CGRectGetHeight(self.tabBarController.tabBar.frame), 0);
}
#pragma mark -
#pragma mark ASTableView.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
KittenNode *cell = [[KittenNode alloc] init];
cell.imageTappedBlock = ^{
[KittenNode defaultImageTappedAction:self];
};
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 15;
}
@end

View File

@@ -0,0 +1,14 @@
//
// ViewController.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface ViewController : ASViewController<ASDisplayNode *>
@end

View File

@@ -0,0 +1,43 @@
//
// ViewController.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import "ViewController.h"
#import "KittenNode.h"
#import "OverrideViewController.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASAssert.h>
@interface ViewController ()
@end
@implementation ViewController
#pragma mark -
#pragma mark UIViewController.
- (instancetype)init
{
KittenNode *displayNode = [[KittenNode alloc] init];
if (!(self = [super initWithNode:displayNode]))
return nil;
self.title = @"Display Node";
displayNode.imageTappedBlock = ^{
[KittenNode defaultImageTappedAction:self];
};
return self;
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
}
@end

View File

@@ -0,0 +1,18 @@
//
// main.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#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,6 @@
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!
target 'Sample' do
pod 'Texture', :path => '../..'
end

View File

@@ -0,0 +1,370 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
BCDB7EDE9701EB3DD88BCDA0 /* Pods_Sample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9DD2D199AD8BD92717ED9783 /* Pods_Sample.framework */; };
CCB8301E1C7688B500847D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CCB8301D1C7688B500847D42 /* Assets.xcassets */; };
CCB830211C7688B500847D42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CCB8301F1C7688B500847D42 /* LaunchScreen.storyboard */; };
CCB8302C1C7688EC00847D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB830281C7688EC00847D42 /* AppDelegate.swift */; };
CCB8302D1C7688EC00847D42 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB830291C7688EC00847D42 /* ViewController.swift */; };
CCB8302E1C7688EC00847D42 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB8302A1C7688EC00847D42 /* Utilities.swift */; };
CCB8302F1C7688EC00847D42 /* DemoCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB8302B1C7688EC00847D42 /* DemoCellNode.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0C1B25A26B6D6815A16D0911 /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
46B216EB47D6586F63D99B86 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
9DD2D199AD8BD92717ED9783 /* Pods_Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CCB830131C7688B500847D42 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
CCB8301D1C7688B500847D42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
CCB830201C7688B500847D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
CCB830221C7688B500847D42 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CCB830281C7688EC00847D42 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
CCB830291C7688EC00847D42 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
CCB8302A1C7688EC00847D42 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
CCB8302B1C7688EC00847D42 /* DemoCellNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoCellNode.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
CCB830101C7688B500847D42 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BCDB7EDE9701EB3DD88BCDA0 /* Pods_Sample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2D17B5A68E217F63F504A0AB /* Pods */ = {
isa = PBXGroup;
children = (
46B216EB47D6586F63D99B86 /* Pods-Sample.debug.xcconfig */,
0C1B25A26B6D6815A16D0911 /* Pods-Sample.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
4C1A8C0BFF16C5457DF86019 /* Frameworks */ = {
isa = PBXGroup;
children = (
9DD2D199AD8BD92717ED9783 /* Pods_Sample.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
CCB8300A1C7688B500847D42 = {
isa = PBXGroup;
children = (
CCB830151C7688B500847D42 /* Sample */,
CCB830141C7688B500847D42 /* Products */,
2D17B5A68E217F63F504A0AB /* Pods */,
4C1A8C0BFF16C5457DF86019 /* Frameworks */,
);
sourceTree = "<group>";
};
CCB830141C7688B500847D42 /* Products */ = {
isa = PBXGroup;
children = (
CCB830131C7688B500847D42 /* Sample.app */,
);
name = Products;
sourceTree = "<group>";
};
CCB830151C7688B500847D42 /* Sample */ = {
isa = PBXGroup;
children = (
CCB830281C7688EC00847D42 /* AppDelegate.swift */,
CCB830291C7688EC00847D42 /* ViewController.swift */,
CCB8302A1C7688EC00847D42 /* Utilities.swift */,
CCB8302B1C7688EC00847D42 /* DemoCellNode.swift */,
CCB8301D1C7688B500847D42 /* Assets.xcassets */,
CCB8301F1C7688B500847D42 /* LaunchScreen.storyboard */,
CCB830221C7688B500847D42 /* Info.plist */,
);
path = Sample;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
CCB830121C7688B500847D42 /* Sample */ = {
isa = PBXNativeTarget;
buildConfigurationList = CCB830251C7688B500847D42 /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
95C31681ADDC41EDF46E7EDD /* Check Pods Manifest.lock */,
CCB8300F1C7688B500847D42 /* Sources */,
CCB830101C7688B500847D42 /* Frameworks */,
CCB830111C7688B500847D42 /* Resources */,
D7905F8C75C02D4B15A13055 /* Embed Pods Frameworks */,
FC69305B914BC44642AD442E /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Sample;
productName = Sample;
productReference = CCB830131C7688B500847D42 /* Sample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
CCB8300B1C7688B500847D42 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = "Adlai Holler";
TargetAttributes = {
CCB830121C7688B500847D42 = {
CreatedOnToolsVersion = 7.2.1;
};
};
};
buildConfigurationList = CCB8300E1C7688B500847D42 /* Build configuration list for PBXProject "Sample" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = CCB8300A1C7688B500847D42;
productRefGroup = CCB830141C7688B500847D42 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
CCB830121C7688B500847D42 /* Sample */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
CCB830111C7688B500847D42 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CCB830211C7688B500847D42 /* LaunchScreen.storyboard in Resources */,
CCB8301E1C7688B500847D42 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
95C31681ADDC41EDF46E7EDD /* 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;
};
D7905F8C75C02D4B15A13055 /* 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-Sample/Pods-Sample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
FC69305B914BC44642AD442E /* 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-Sample/Pods-Sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
CCB8300F1C7688B500847D42 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CCB8302F1C7688EC00847D42 /* DemoCellNode.swift in Sources */,
CCB8302E1C7688EC00847D42 /* Utilities.swift in Sources */,
CCB8302D1C7688EC00847D42 /* ViewController.swift in Sources */,
CCB8302C1C7688EC00847D42 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
CCB8301F1C7688B500847D42 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
CCB830201C7688B500847D42 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
CCB830231C7688B500847D42 /* 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;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
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 = 9.2;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
CCB830241C7688B500847D42 /* 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 = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
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 = 9.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_VERSION = 2.3;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
CCB830261C7688B500847D42 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 46B216EB47D6586F63D99B86 /* Pods-Sample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = Sample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = adlai.Sample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
CCB830271C7688B500847D42 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0C1B25A26B6D6815A16D0911 /* Pods-Sample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
INFOPLIST_FILE = Sample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = adlai.Sample;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
CCB8300E1C7688B500847D42 /* Build configuration list for PBXProject "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CCB830231C7688B500847D42 /* Debug */,
CCB830241C7688B500847D42 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CCB830251C7688B500847D42 /* Build configuration list for PBXNativeTarget "Sample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CCB830261C7688B500847D42 /* Debug */,
CCB830271C7688B500847D42 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = CCB8300B1C7688B500847D42 /* 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,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CCB830121C7688B500847D42"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CCB830121C7688B500847D42"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CCB830121C7688B500847D42"
BuildableName = "Sample.app"
BlueprintName = "Sample"
ReferencedContainer = "container:Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CCB830121C7688B500847D42"
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,51 @@
//
// AppDelegate.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window = window
let vc = ViewController()
window.rootViewController = UINavigationController(rootViewController: vc)
window.makeKeyAndVisible()
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}

View File

@@ -0,0 +1,38 @@
{
"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"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,88 @@
//
// DemoCellNode.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
import AsyncDisplayKit
final class DemoCellNode: ASCellNode {
let childA = ASDisplayNode()
let childB = ASDisplayNode()
var state = State.Right
override init() {
super.init()
automaticallyManagesSubnodes = true
}
override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
let specA = ASRatioLayoutSpec(ratio: 1, child: childA)
specA.style.flexBasis = ASDimensionMakeWithPoints(1)
specA.style.flexGrow = 1.0
let specB = ASRatioLayoutSpec(ratio: 1, child: childB)
specB.style.flexBasis = ASDimensionMakeWithPoints(1)
specB.style.flexGrow = 1.0
let children = state.isReverse ? [ specB, specA ] : [ specA, specB ]
let direction: ASStackLayoutDirection = state.isVertical ? .Vertical : .Horizontal
return ASStackLayoutSpec(direction: direction,
spacing: 20,
justifyContent: .SpaceAround,
alignItems: .Center,
children: children)
}
override func animateLayoutTransition(context: ASContextTransitioning) {
childA.frame = context.initialFrameForNode(childA)
childB.frame = context.initialFrameForNode(childB)
let tinyDelay = drand48() / 10
UIView.animateWithDuration(0.5, delay: tinyDelay, usingSpringWithDamping: 0.9, initialSpringVelocity: 1.5, options: .BeginFromCurrentState, animations: { () -> Void in
self.childA.frame = context.finalFrameForNode(self.childA)
self.childB.frame = context.finalFrameForNode(self.childB)
}, completion: {
context.completeTransition($0)
})
}
enum State {
case Right
case Up
case Left
case Down
var isVertical: Bool {
switch self {
case .Up, .Down:
return true
default:
return false
}
}
var isReverse: Bool {
switch self {
case .Left, .Up:
return true
default:
return false
}
}
mutating func advance() {
switch self {
case .Right:
self = .Up
case .Up:
self = .Left
case .Left:
self = .Down
case .Down:
self = .Right
}
}
}
}

View File

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

View File

@@ -0,0 +1,16 @@
//
// Utilities.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
extension UIColor {
static func random() -> UIColor {
return UIColor(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0)
}
}

View File

@@ -0,0 +1,96 @@
//
// ViewController.swift
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
import UIKit
import AsyncDisplayKit
final class ViewController: ASViewController, ASCollectionDelegate, ASCollectionDataSource {
let itemCount = 1000
let itemSize: CGSize
let padding: CGFloat
var collectionNode: ASCollectionNode {
return node as! ASCollectionNode
}
init() {
let layout = UICollectionViewFlowLayout()
(padding, itemSize) = ViewController.computeLayoutSizesForMainScreen()
layout.minimumInteritemSpacing = padding
layout.minimumLineSpacing = padding
super.init(node: ASCollectionNode(collectionViewLayout: layout))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Color", style: .Plain, target: self, action: #selector(didTapColorsButton))
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Layout", style: .Plain, target: self, action: #selector(didTapLayoutButton))
collectionNode.delegate = self
collectionNode.dataSource = self
title = "Background Updating"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: ASCollectionDataSource
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount
}
func collectionView(collectionView: ASCollectionView, nodeBlockForItemAtIndexPath indexPath: NSIndexPath) -> ASCellNodeBlock {
return {
let node = DemoCellNode()
node.backgroundColor = UIColor.random()
node.childA.backgroundColor = UIColor.random()
node.childB.backgroundColor = UIColor.random()
return node
}
}
func collectionView(collectionView: ASCollectionView, constrainedSizeForNodeAtIndexPath indexPath: NSIndexPath) -> ASSizeRange {
return ASSizeRangeMake(itemSize, itemSize)
}
// MARK: Action Handling
@objc private func didTapColorsButton() {
let currentlyVisibleNodes = collectionNode.view.visibleNodes()
let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)
dispatch_async(queue) {
for case let node as DemoCellNode in currentlyVisibleNodes {
node.backgroundColor = UIColor.random()
}
}
}
@objc private func didTapLayoutButton() {
let currentlyVisibleNodes = collectionNode.view.visibleNodes()
let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)
dispatch_async(queue) {
for case let node as DemoCellNode in currentlyVisibleNodes {
node.state.advance()
node.setNeedsLayout()
}
}
}
// MARK: Static
static func computeLayoutSizesForMainScreen() -> (padding: CGFloat, itemSize: CGSize) {
let numberOfColumns = 4
let screen = UIScreen.mainScreen()
let scale = screen.scale
let screenWidth = Int(screen.bounds.width * screen.scale)
let itemWidthPx = (screenWidth - (numberOfColumns - 1)) / numberOfColumns
let leftover = screenWidth - itemWidthPx * numberOfColumns
let paddingPx = leftover / (numberOfColumns - 1)
let itemDimension = CGFloat(itemWidthPx) / scale
let padding = CGFloat(paddingPx) / scale
return (padding: padding, itemSize: CGSize(width: itemDimension, height: itemDimension))
}
}

View File

@@ -0,0 +1 @@
github "facebook/AsyncDisplayKit" "master"

View File

@@ -0,0 +1,18 @@
//
// AppDelegate.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end

View File

@@ -0,0 +1,48 @@
//
// AppDelegate.m
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
@import AsyncDisplayKit;
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
@end

View File

@@ -0,0 +1,38 @@
{
"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"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

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

View File

@@ -0,0 +1,16 @@
//
// ViewController.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

Some files were not shown because too many files have changed in this diff Show More