diff --git a/docs/static/images/example-app-screenshots/TextureHeroExample.gif b/docs/static/images/example-app-screenshots/TextureHeroExample.gif new file mode 100644 index 000000000..d996d83b1 Binary files /dev/null and b/docs/static/images/example-app-screenshots/TextureHeroExample.gif differ diff --git a/examples/README.md b/examples/README.md index 76c71a75d..96b65a619 100644 --- a/examples/README.md +++ b/examples/README.md @@ -217,6 +217,9 @@ Featuring: There is an associated swift version app: LayoutSpecExamples-Swift with same logic implementation. +### TextureHeroExample [Swift] +![Hero Example App Screenshot](https://github.com/TextureGroup/Texture/tree/master/docs/static/images/example-app-screenshots/TextureHeroExample.gif) + ## License This file provided by Facebook is for non-commercial testing and evaluation diff --git a/examples/TextureHeroExample/Podfile b/examples/TextureHeroExample/Podfile new file mode 100644 index 000000000..cf4665c01 --- /dev/null +++ b/examples/TextureHeroExample/Podfile @@ -0,0 +1,9 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '9.0' + +use_frameworks! + +target 'Sample' do + pod 'Texture', :path => '../..' + pod 'Hero' +end diff --git a/examples/TextureHeroExample/Podfile.lock b/examples/TextureHeroExample/Podfile.lock new file mode 100644 index 000000000..93a01aa42 --- /dev/null +++ b/examples/TextureHeroExample/Podfile.lock @@ -0,0 +1,59 @@ +PODS: + - Hero (1.3.0) + - PINCache (3.0.1-beta.6): + - PINCache/Arc-exception-safe (= 3.0.1-beta.6) + - PINCache/Core (= 3.0.1-beta.6) + - PINCache/Arc-exception-safe (3.0.1-beta.6): + - PINCache/Core + - PINCache/Core (3.0.1-beta.6): + - PINOperation (~> 1.1.0) + - PINOperation (1.1.1) + - PINRemoteImage/Core (3.0.0-beta.13): + - PINOperation + - PINRemoteImage/iOS (3.0.0-beta.13): + - PINRemoteImage/Core + - PINRemoteImage/PINCache (3.0.0-beta.13): + - PINCache (= 3.0.1-beta.6) + - PINRemoteImage/Core + - Texture (2.7): + - Texture/AssetsLibrary (= 2.7) + - Texture/MapKit (= 2.7) + - Texture/Photos (= 2.7) + - Texture/PINRemoteImage (= 2.7) + - Texture/AssetsLibrary (2.7): + - Texture/Core + - Texture/Core (2.7) + - Texture/MapKit (2.7): + - Texture/Core + - Texture/Photos (2.7): + - Texture/Core + - Texture/PINRemoteImage (2.7): + - PINRemoteImage/iOS (= 3.0.0-beta.13) + - PINRemoteImage/PINCache + - Texture/Core + +DEPENDENCIES: + - Hero + - Texture (from `../..`) + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - Hero + - PINCache + - PINOperation + - PINRemoteImage + +EXTERNAL SOURCES: + Texture: + :path: "../.." + +SPEC CHECKSUMS: + Hero: 3eecd3fd62dd2425cbc7e35505c557b86d5a379e + PINCache: d195fdba255283f7e9900a55e3cced377f431f9b + PINOperation: a6219e6fc9db9c269eb7a7b871ac193bcf400aac + PINRemoteImage: d6d51c5d2adda55f1ce30c96e850b6c4ebd2856a + Texture: c9bdf779b599c6e1eef33e67a4185f27a1befff8 + +PODFILE CHECKSUM: 076f074407115d702d18706547b4af7109020292 + +COCOAPODS: 1.5.3 diff --git a/examples/TextureHeroExample/Sample.xcodeproj/project.pbxproj b/examples/TextureHeroExample/Sample.xcodeproj/project.pbxproj new file mode 100644 index 000000000..e5b5121fe --- /dev/null +++ b/examples/TextureHeroExample/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,440 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 050E7C7419D22E19004363C2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 050E7C7319D22E19004363C2 /* AppDelegate.swift */; }; + 161FE6897BB33A570A663F90 /* Pods_Sample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27E6141C83F216FCA6D7EAEE /* Pods_Sample.framework */; }; + 5E2932011DD1E07900026492 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E2931FF1DD1E07900026492 /* LaunchScreen.storyboard */; }; + 9BFF6A08211FF7D50097F60C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9BFF6A07211FF7D50097F60C /* Assets.xcassets */; }; + 9BFF6A0A211FF8650097F60C /* UserListNodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFF6A09211FF8650097F60C /* UserListNodeController.swift */; }; + 9BFF6A0C211FF8720097F60C /* UserPreviewNodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFF6A0B211FF8720097F60C /* UserPreviewNodeController.swift */; }; + 9BFF6A0E211FF8780097F60C /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFF6A0D211FF8780097F60C /* User.swift */; }; + 9BFF6A10211FF8950097F60C /* UserListHeadLineCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFF6A0F211FF8950097F60C /* UserListHeadLineCellNode.swift */; }; + 9BFF6A12211FF89D0097F60C /* UserCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFF6A11211FF89D0097F60C /* UserCellNode.swift */; }; + 9BFF6A152120065A0097F60C /* Extension+Hero.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BFF6A142120065A0097F60C /* Extension+Hero.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 050E7C6E19D22E19004363C2 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 050E7C7219D22E19004363C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 050E7C7319D22E19004363C2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 08B9AAEC0A03243C3516AA96 /* 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 = ""; }; + 27E6141C83F216FCA6D7EAEE /* Pods_Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E2932001DD1E07900026492 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 9B402DAAE8C6A8B9BE1A506B /* 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 = ""; }; + 9BFF6A07211FF7D50097F60C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 9BFF6A09211FF8650097F60C /* UserListNodeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListNodeController.swift; sourceTree = ""; }; + 9BFF6A0B211FF8720097F60C /* UserPreviewNodeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreviewNodeController.swift; sourceTree = ""; }; + 9BFF6A0D211FF8780097F60C /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; + 9BFF6A0F211FF8950097F60C /* UserListHeadLineCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserListHeadLineCellNode.swift; sourceTree = ""; }; + 9BFF6A11211FF89D0097F60C /* UserCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCellNode.swift; sourceTree = ""; }; + 9BFF6A142120065A0097F60C /* Extension+Hero.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Hero.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 050E7C6B19D22E19004363C2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 161FE6897BB33A570A663F90 /* Pods_Sample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 050E7C6519D22E19004363C2 = { + isa = PBXGroup; + children = ( + 050E7C7019D22E19004363C2 /* Sample */, + 050E7C6F19D22E19004363C2 /* Products */, + 092C2001FE124604891D6E90 /* Frameworks */, + 655F2ABBD991CBDE7140FACE /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 050E7C6F19D22E19004363C2 /* Products */ = { + isa = PBXGroup; + children = ( + 050E7C6E19D22E19004363C2 /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 050E7C7019D22E19004363C2 /* Sample */ = { + isa = PBXGroup; + children = ( + 9BFF6A13212006490097F60C /* Extensions */, + 9BFF6A07211FF7D50097F60C /* Assets.xcassets */, + 9BFF6A04211FF76E0097F60C /* Models */, + 9BFF6A03211FF7660097F60C /* Controllers */, + 9BFF6A02211FF7620097F60C /* Nodes */, + 050E7C7319D22E19004363C2 /* AppDelegate.swift */, + 050E7C7119D22E19004363C2 /* Supporting Files */, + ); + path = Sample; + sourceTree = ""; + }; + 050E7C7119D22E19004363C2 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5E2931FF1DD1E07900026492 /* LaunchScreen.storyboard */, + 050E7C7219D22E19004363C2 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 092C2001FE124604891D6E90 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 27E6141C83F216FCA6D7EAEE /* Pods_Sample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 655F2ABBD991CBDE7140FACE /* Pods */ = { + isa = PBXGroup; + children = ( + 9B402DAAE8C6A8B9BE1A506B /* Pods-Sample.debug.xcconfig */, + 08B9AAEC0A03243C3516AA96 /* Pods-Sample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9BFF6A02211FF7620097F60C /* Nodes */ = { + isa = PBXGroup; + children = ( + 9BFF6A0F211FF8950097F60C /* UserListHeadLineCellNode.swift */, + 9BFF6A11211FF89D0097F60C /* UserCellNode.swift */, + ); + name = Nodes; + sourceTree = ""; + }; + 9BFF6A03211FF7660097F60C /* Controllers */ = { + isa = PBXGroup; + children = ( + 9BFF6A09211FF8650097F60C /* UserListNodeController.swift */, + 9BFF6A0B211FF8720097F60C /* UserPreviewNodeController.swift */, + ); + name = Controllers; + sourceTree = ""; + }; + 9BFF6A04211FF76E0097F60C /* Models */ = { + isa = PBXGroup; + children = ( + 9BFF6A0D211FF8780097F60C /* User.swift */, + ); + name = Models; + sourceTree = ""; + }; + 9BFF6A13212006490097F60C /* Extensions */ = { + isa = PBXGroup; + children = ( + 9BFF6A142120065A0097F60C /* Extension+Hero.swift */, + ); + name = Extensions; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 050E7C6D19D22E19004363C2 /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 050E7C8D19D22E1A004363C2 /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + B8824BD0ED824BAD8268EC35 /* [CP] Check Pods Manifest.lock */, + 050E7C6A19D22E19004363C2 /* Sources */, + 050E7C6B19D22E19004363C2 /* Frameworks */, + 050E7C6C19D22E19004363C2 /* Resources */, + 1F5A9F09F5875F61862D0783 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 050E7C6E19D22E19004363C2 /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 050E7C6619D22E19004363C2 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftMigration = 0700; + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 050E7C6D19D22E19004363C2 = { + CreatedOnToolsVersion = 6.0.1; + LastSwiftMigration = 0810; + }; + }; + }; + buildConfigurationList = 050E7C6919D22E19004363C2 /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 050E7C6519D22E19004363C2; + productRefGroup = 050E7C6F19D22E19004363C2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 050E7C6D19D22E19004363C2 /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 050E7C6C19D22E19004363C2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BFF6A08211FF7D50097F60C /* Assets.xcassets in Resources */, + 5E2932011DD1E07900026492 /* LaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1F5A9F09F5875F61862D0783 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Hero/Hero.framework", + "${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}/Hero.framework", + "${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-Sample/Pods-Sample-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B8824BD0ED824BAD8268EC35 /* [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 */ + 050E7C6A19D22E19004363C2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BFF6A10211FF8950097F60C /* UserListHeadLineCellNode.swift in Sources */, + 9BFF6A0E211FF8780097F60C /* User.swift in Sources */, + 9BFF6A152120065A0097F60C /* Extension+Hero.swift in Sources */, + 9BFF6A12211FF89D0097F60C /* UserCellNode.swift in Sources */, + 9BFF6A0A211FF8650097F60C /* UserListNodeController.swift in Sources */, + 9BFF6A0C211FF8720097F60C /* UserPreviewNodeController.swift in Sources */, + 050E7C7419D22E19004363C2 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5E2931FF1DD1E07900026492 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5E2932001DD1E07900026492 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 050E7C8B19D22E1A004363C2 /* 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_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_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; + 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_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; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 050E7C8C19D22E1A004363C2 /* 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_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_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 = YES; + 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"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 050E7C8E19D22E1A004363C2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9B402DAAE8C6A8B9BE1A506B /* Pods-Sample.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + 050E7C8F19D22E1A004363C2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 08B9AAEC0A03243C3516AA96 /* Pods-Sample.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = Sample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 050E7C6919D22E19004363C2 /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 050E7C8B19D22E1A004363C2 /* Debug */, + 050E7C8C19D22E1A004363C2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 050E7C8D19D22E1A004363C2 /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 050E7C8E19D22E1A004363C2 /* Debug */, + 050E7C8F19D22E1A004363C2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 050E7C6619D22E19004363C2 /* Project object */; +} diff --git a/examples/TextureHeroExample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/TextureHeroExample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..a80c03824 --- /dev/null +++ b/examples/TextureHeroExample/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/TextureHeroExample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples/TextureHeroExample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme new file mode 100644 index 000000000..05f94265c --- /dev/null +++ b/examples/TextureHeroExample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/TextureHeroExample/Sample.xcworkspace/contents.xcworkspacedata b/examples/TextureHeroExample/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..7b5a2f305 --- /dev/null +++ b/examples/TextureHeroExample/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples/TextureHeroExample/Sample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/TextureHeroExample/Sample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/examples/TextureHeroExample/Sample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/examples/TextureHeroExample/Sample/AppDelegate.swift b/examples/TextureHeroExample/Sample/AppDelegate.swift new file mode 100644 index 000000000..a68e04870 --- /dev/null +++ b/examples/TextureHeroExample/Sample/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import UIKit +import Hero + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) + let navigationController = UINavigationController(rootViewController: UserListNodeController()) + navigationController.hero.isEnabled = true + navigationController.hero.modalAnimationType = .selectBy(presenting: .slide(direction: .left), + dismissing: .slide(direction: .right)) + + window?.rootViewController = navigationController + window?.makeKeyAndVisible() + return true + } + +} diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d8db8d65f --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "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" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Contents.json new file mode 100644 index 000000000..da4a164c9 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Eunha.imageset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Eunha.imageset/Contents.json new file mode 100644 index 000000000..105d2ec7e --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Eunha.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "eunha.jpeg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Eunha.imageset/eunha.jpeg b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Eunha.imageset/eunha.jpeg new file mode 100644 index 000000000..37b1dda75 Binary files /dev/null and b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Eunha.imageset/eunha.jpeg differ diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/SinB.imageset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/SinB.imageset/Contents.json new file mode 100644 index 000000000..835b39a22 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/SinB.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "shinB.jpeg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/SinB.imageset/shinB.jpeg b/examples/TextureHeroExample/Sample/Assets.xcassets/User/SinB.imageset/shinB.jpeg new file mode 100644 index 000000000..221a0a4ce Binary files /dev/null and b/examples/TextureHeroExample/Sample/Assets.xcassets/User/SinB.imageset/shinB.jpeg differ diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Sowon.imageset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Sowon.imageset/Contents.json new file mode 100644 index 000000000..53e6398ab --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Sowon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "sowon.jpeg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Sowon.imageset/sowon.jpeg b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Sowon.imageset/sowon.jpeg new file mode 100644 index 000000000..b4b02bffd Binary files /dev/null and b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Sowon.imageset/sowon.jpeg differ diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Umji.imageset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Umji.imageset/Contents.json new file mode 100644 index 000000000..0ee76f685 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Umji.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "EJ.jpeg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Umji.imageset/EJ.jpeg b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Umji.imageset/EJ.jpeg new file mode 100644 index 000000000..f739340f3 Binary files /dev/null and b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Umji.imageset/EJ.jpeg differ diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yerin.imageset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yerin.imageset/Contents.json new file mode 100644 index 000000000..1357a1533 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yerin.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "yerin.jpeg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yerin.imageset/yerin.jpeg b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yerin.imageset/yerin.jpeg new file mode 100644 index 000000000..5f68183ab Binary files /dev/null and b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yerin.imageset/yerin.jpeg differ diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yuju.imageset/Contents.json b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yuju.imageset/Contents.json new file mode 100644 index 000000000..9c5226f54 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yuju.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "yuju.jpeg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yuju.imageset/yuju.jpeg b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yuju.imageset/yuju.jpeg new file mode 100644 index 000000000..c8e553900 Binary files /dev/null and b/examples/TextureHeroExample/Sample/Assets.xcassets/User/Yuju.imageset/yuju.jpeg differ diff --git a/examples/TextureHeroExample/Sample/Base.lproj/LaunchScreen.storyboard b/examples/TextureHeroExample/Sample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..7c257a142 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/TextureHeroExample/Sample/Extension+Hero.swift b/examples/TextureHeroExample/Sample/Extension+Hero.swift new file mode 100644 index 000000000..752de5e79 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Extension+Hero.swift @@ -0,0 +1,53 @@ +// +// Extension+Hero.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation +import Hero +import AsyncDisplayKit + +protocol HeroExampleProtocol { + func setupHero() +} + +extension ASDisplayNode { + + enum HeroIdentifier { + case profile(String) + case username(String) + case bio(String) + + var identifier: String { + switch self { + case .profile(let id): + return "profile-hero-identifer-\(id)" + case .username(let id): + return "username-hero-identifer-\(id)" + case .bio(let id): + return "bio-hero-identifer-\(id)" + } + } + } + + func applyHero(id: HeroIdentifier, modifier: [HeroModifier]?) { + guard ASDisplayNodeThreadIsMain() else { + fatalError("This method must be called on the main thread") + } + self.view.hero.id = id.identifier + self.view.hero.modifiers = modifier + } +} + diff --git a/examples/TextureHeroExample/Sample/Info.plist b/examples/TextureHeroExample/Sample/Info.plist new file mode 100644 index 000000000..8eaf01793 --- /dev/null +++ b/examples/TextureHeroExample/Sample/Info.plist @@ -0,0 +1,41 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + + diff --git a/examples/TextureHeroExample/Sample/User.swift b/examples/TextureHeroExample/Sample/User.swift new file mode 100644 index 000000000..a9d55151e --- /dev/null +++ b/examples/TextureHeroExample/Sample/User.swift @@ -0,0 +1,51 @@ +// +// User.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation +import UIKit + +struct User { + let profileImage: UIImage? + let name: String + let bio: String +} + +extension User { + + static func generateMockUsers() -> [User] { + let usernames: [String] = ["Umji", + "Eunha", + "SinB", + "Sowon", + "Yerin", + "Yuju"] + let userBio: [String] = + ["Umji is a South Korean singer. She is a vocalist and the maknae of the girl group GFRIEND.", + "Eunha is a South Korean singer and actress. She is known as the lead vocalist of the girl group GFriend.", + "SinB is a South Korean singer. She is the main dancer, face and center of GFriend, which is under Source Music.", + "Sowon is a South Korean singer rapper. She is the leader and rapper of GFriend, which is under Source Music.", + "Yerin is a South Korean singer. She is the lead dancer and second center of GFriend, which is under Source Music.", + "Yuju is a South Korean singer. She is best known as the main vocalist of the South Korean girl group GFriend." + ] + + return (0 ..< usernames.count).map { index -> User in + return User(profileImage: UIImage(named: usernames[index]), + name: usernames[index], + bio: userBio[index]) + } + } +} diff --git a/examples/TextureHeroExample/Sample/UserCellNode.swift b/examples/TextureHeroExample/Sample/UserCellNode.swift new file mode 100644 index 000000000..fca0893ea --- /dev/null +++ b/examples/TextureHeroExample/Sample/UserCellNode.swift @@ -0,0 +1,115 @@ +// +// UserCellNode.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation +import AsyncDisplayKit +import Hero + +class UserCellNode: ASCellNode { + typealias Node = UserCellNode + + struct Const { + static let cellInsets: UIEdgeInsets = + .init(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0) + } + + let profileNode: ASImageNode = { + let node = ASImageNode() + node.style.preferredSize = .init(width: 80.0, height: 80.0) + node.cornerRadius = node.style.preferredSize.height / 2.0 + node.clipsToBounds = true + node.borderColor = UIColor.lightGray.cgColor + node.borderWidth = 1.0 + return node + }() + + let usernameNode = ASTextNode() + let bioNode = ASTextNode() + let id: String + + init(_ model: User) { + self.id = model.name + super.init() + self.selectionStyle = .none + self.automaticallyManagesSubnodes = true + self.backgroundColor = .white + self.isOpaque = true + + profileNode.image = model.profileImage + usernameNode.attributedText = + NSAttributedString(string: model.name, attributes: Node.usernameAttr) + bioNode.attributedText = + NSAttributedString(string: model.bio, attributes: Node.bioAttr) + } + + override func didLoad() { + super.didLoad() + self.setupHero() + } +} + +// MARK: Hero Example +extension UserCellNode: HeroExampleProtocol { + func setupHero() { + self.profileNode.applyHero(id: .profile(id), modifier: nil) + self.usernameNode.applyHero(id: .username(id), modifier: nil) + self.bioNode.applyHero(id: .bio(id), modifier: nil) + } +} + +// MARK: LayoutSpec +extension UserCellNode { + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + let profileAreaLayout = profileAreaLayoutSpec() + profileAreaLayout.style.flexGrow = 1.0 + profileAreaLayout.style.flexShrink = 1.0 + bioNode.style.flexGrow = 1.0 + bioNode.style.flexShrink = 1.0 + + let stackLayout = ASStackLayoutSpec(direction: .vertical, + spacing: 10.0, + justifyContent: .start, + alignItems: .stretch, + children: [profileAreaLayout, bioNode]) + return ASInsetLayoutSpec(insets: Const.cellInsets, child: stackLayout) + } + + func profileAreaLayoutSpec() -> ASLayoutSpec { + profileNode.style.flexShrink = 1.0 + profileNode.style.flexGrow = 0.0 + usernameNode.style.flexShrink = 1.0 + usernameNode.style.flexGrow = 1.0 + + return ASStackLayoutSpec(direction: .horizontal, + spacing: 10.0, + justifyContent: .start, + alignItems: .center, + children: [profileNode, usernameNode]) + } +} + +// Mark: Attribute +extension UserCellNode { + static var usernameAttr: [NSAttributedStringKey: Any] { + return [.font: UIFont.systemFont(ofSize: 20.0, weight: .bold), + .foregroundColor: UIColor.darkGray] + } + static var bioAttr: [NSAttributedStringKey: Any] { + return [.font: UIFont.systemFont(ofSize: 12.0), + .foregroundColor: UIColor.gray] + } +} diff --git a/examples/TextureHeroExample/Sample/UserListHeadLineCellNode.swift b/examples/TextureHeroExample/Sample/UserListHeadLineCellNode.swift new file mode 100644 index 000000000..0fec67fe8 --- /dev/null +++ b/examples/TextureHeroExample/Sample/UserListHeadLineCellNode.swift @@ -0,0 +1,56 @@ +// +// UserListHeadLineCellNode.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation +import AsyncDisplayKit + +class UserListHeadLineCellNode: ASCellNode { + typealias Node = UserListHeadLineCellNode + + struct Const { + static let cellInsets: UIEdgeInsets = .init(top: 15.0, left: 15.0, bottom: 15.0, right: 15.0) + } + + let headLineNode: ASTextNode = { + let node = ASTextNode() + node.attributedText = + NSAttributedString(string: "Texture Hero Example", + attributes: Node.headlineAttr) + return node + }() + + override init() { + super.init() + self.automaticallyManagesSubnodes = true + self.selectionStyle = .none + self.backgroundColor = .white + self.isOpaque = true + } +} + +extension UserListHeadLineCellNode { + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASInsetLayoutSpec(insets: Const.cellInsets, child: headLineNode) + } +} + +extension UserListHeadLineCellNode { + static var headlineAttr: [NSAttributedStringKey: Any] { + return [.font: UIFont.systemFont(ofSize: 50.0, weight: .bold), + .foregroundColor: UIColor.darkGray] + } +} diff --git a/examples/TextureHeroExample/Sample/UserListNodeController.swift b/examples/TextureHeroExample/Sample/UserListNodeController.swift new file mode 100644 index 000000000..9cb81c81d --- /dev/null +++ b/examples/TextureHeroExample/Sample/UserListNodeController.swift @@ -0,0 +1,103 @@ +// +// UserListNodeController.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation +import AsyncDisplayKit +import Hero + +class UserListNodeController: ASViewController { + + var userList: [User] = User.generateMockUsers() + + enum Section: Int { + case headline + case user + + static var numberOfSections: Int { + return 2 + } + } + + init() { + super.init(node: ASTableNode(style: .plain)) + self.title = "User List" + self.node.backgroundColor = .white + self.node.isOpaque = true + self.node.delegate = self + self.node.dataSource = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + self.node.view.tableFooterView = UIView(frame: .zero) + self.node.view.separatorStyle = .none + } +} + +extension UserListNodeController: ASTableDelegate { + func tableNode(_ tableNode: ASTableNode, didSelectRowAt indexPath: IndexPath) { + guard let section = Section(rawValue: indexPath.section) else { return } + switch section { + case .headline: + break + case .user: + guard indexPath.row < userList.count else { return } + self.openUserPreview(userList[indexPath.row]) + } + } + + private func openUserPreview(_ model: User) { + let viewController = UserPreviewNodeController(model) + self.navigationController?.pushViewController(viewController, animated: true) + } +} + +extension UserListNodeController: ASTableDataSource { + func numberOfSections(in tableNode: ASTableNode) -> Int { + return Section.numberOfSections + } + + func tableNode(_ tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int { + guard let `section` = Section(rawValue: section) else { return 0 } + switch section { + case .headline: + return 1 + case .user: + return userList.count + } + } + + func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { + return { + guard let section = Section(rawValue: indexPath.section) else { + return ASCellNode() + } + + switch section { + case .headline: + return UserListHeadLineCellNode() + case .user: + guard indexPath.row < self.userList.count else { return ASCellNode() } + return UserCellNode(self.userList[indexPath.row]) + } + } + } +} diff --git a/examples/TextureHeroExample/Sample/UserPreviewNodeController.swift b/examples/TextureHeroExample/Sample/UserPreviewNodeController.swift new file mode 100644 index 000000000..ec8bd703a --- /dev/null +++ b/examples/TextureHeroExample/Sample/UserPreviewNodeController.swift @@ -0,0 +1,143 @@ +// +// UserPreviewNodeController.swift +// Sample +// +// Copyright (c) 2014-present, Facebook, Inc. All rights reserved. +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. An additional grant +// of patent rights can be found in the PATENTS file in the same directory. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import Foundation +import AsyncDisplayKit +import Hero + +class UserPreviewNodeController: ASViewController { + typealias PreviewController = UserPreviewNodeController + + struct Const { + static let profileAreaInsets: UIEdgeInsets = + .init(top: 100.0, left: 15.0, bottom: .infinity, right: 15.0) + } + + let profileNode: ASImageNode = { + let node = ASImageNode() + node.style.preferredSize = .init(width: 80.0, height: 80.0) + node.cornerRadius = node.style.preferredSize.height / 2.0 + node.clipsToBounds = true + node.borderColor = UIColor.lightGray.cgColor + node.borderWidth = 1.0 + return node + }() + + let usernameNode = ASTextNode() + let bioNode = ASTextNode() + let id: String + + let edgeScreenGesture: UIScreenEdgePanGestureRecognizer = { + let gesture = UIScreenEdgePanGestureRecognizer() + gesture.edges = [.left] + return gesture + }() + + init(_ model: User) { + self.id = model.name + super.init(node: ASDisplayNode()) + self.title = "User Preview" + self.node.backgroundColor = .white + self.node.isOpaque = true + self.node.automaticallyManagesSubnodes = true + self.node.layoutSpecBlock = { [weak self] (_, _) -> ASLayoutSpec in + return self?.userPreivewLayoutSpec() ?? ASLayoutSpec() + } + self.applySubnodeAttribute(model) + } + + func applySubnodeAttribute(_ model: User) { + profileNode.image = model.profileImage + usernameNode.attributedText = + NSAttributedString(string: model.name, attributes: PreviewController.usernameAttr) + bioNode.attributedText = + NSAttributedString(string: model.bio, attributes: PreviewController.bioAttr) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + self.setupHero() + self.view.addGestureRecognizer(edgeScreenGesture) + self.view.isUserInteractionEnabled = true + edgeScreenGesture.addTarget(self, action: #selector(dismissPreview(gestureRecognizer:))) + } +} + +// Mark: HERO Example +extension UserPreviewNodeController: HeroExampleProtocol { + func setupHero() { + self.hero.isEnabled = true + self.profileNode.applyHero(id: .profile(id), modifier: [.fade]) + self.usernameNode.applyHero(id: .username(id), modifier: [.fade]) + self.bioNode.applyHero(id: .bio(id), modifier: [.fade]) + } + + @objc func dismissPreview(gestureRecognizer: UIScreenEdgePanGestureRecognizer) { + switch gestureRecognizer.state { + case .began: + self.navigationController?.popViewController(animated: true) + case .changed: + let translation = gestureRecognizer.translation(in: nil) + let progress = translation.x / view.bounds.width + Hero.shared.update(progress) + default: + Hero.shared.finish() + } + } +} + +// Mark: LayoutSpec +extension UserPreviewNodeController { + func userPreivewLayoutSpec() -> ASLayoutSpec { + profileNode.style.flexShrink = 1.0 + profileNode.style.flexGrow = 0.0 + usernameNode.style.flexShrink = 1.0 + usernameNode.style.flexGrow = 1.0 + bioNode.style.flexShrink = 1.0 + bioNode.style.flexGrow = 1.0 + + let stackLayout = ASStackLayoutSpec(direction: .vertical, + spacing: 10.0, + justifyContent: .start, + alignItems: .center, + children: [profileNode, + usernameNode, + bioNode]) + + return ASInsetLayoutSpec(insets: Const.profileAreaInsets, + child: stackLayout) + } +} + +// Mark: Attribute +extension UserPreviewNodeController { + static var usernameAttr: [NSAttributedStringKey: Any] { + return [.font: UIFont.systemFont(ofSize: 20.0, weight: .bold), + .foregroundColor: UIColor.darkGray] + } + static var bioAttr: [NSAttributedStringKey: Any] { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.alignment = .center + return [.font: UIFont.systemFont(ofSize: 12.0), + .foregroundColor: UIColor.gray, + .paragraphStyle: paragraphStyle as Any] + } +}