Merge pull request #1671 from Eke/ASVideoPlayerNode
ASVideoPlayerNode initial version
@ -311,6 +311,14 @@
|
|||||||
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */; };
|
7AB338691C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */; };
|
||||||
81EE384F1C8E94F000456208 /* ASRunLoopQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
81EE384F1C8E94F000456208 /* ASRunLoopQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
81EE38501C8E94F000456208 /* ASRunLoopQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */; };
|
81EE38501C8E94F000456208 /* ASRunLoopQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */; };
|
||||||
|
8B0768B31CE752EC002E1453 /* ASDefaultPlaybackButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */; };
|
||||||
|
8B0768B41CE752EC002E1453 /* ASDefaultPlaybackButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */; };
|
||||||
|
8BBBAB8C1CEBAF1700107FC6 /* ASDefaultPlaybackButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */; };
|
||||||
|
8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */; };
|
||||||
|
8BDA5FC51CDBDDE1007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; };
|
||||||
|
8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */; };
|
||||||
|
8BDA5FC71CDBDF91007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; };
|
||||||
|
8BDA5FC81CDBDF95007D13B2 /* ASVideoPlayerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */; };
|
||||||
92074A611CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */; };
|
92074A611CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */; };
|
||||||
92074A621CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */; };
|
92074A621CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */; };
|
||||||
92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */; };
|
92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */; };
|
||||||
@ -820,6 +828,10 @@
|
|||||||
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
|
7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
|
||||||
81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; };
|
81EE384D1C8E94F000456208 /* ASRunLoopQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRunLoopQueue.h; path = ../ASRunLoopQueue.h; sourceTree = "<group>"; };
|
||||||
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; };
|
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; sourceTree = "<group>"; };
|
||||||
|
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDefaultPlaybackButton.h; sourceTree = "<group>"; };
|
||||||
|
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDefaultPlaybackButton.m; sourceTree = "<group>"; };
|
||||||
|
8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASVideoPlayerNode.h; sourceTree = "<group>"; };
|
||||||
|
8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASVideoPlayerNode.mm; sourceTree = "<group>"; };
|
||||||
92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASImageNode+tvOS.h"; sourceTree = "<group>"; };
|
92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASImageNode+tvOS.h"; sourceTree = "<group>"; };
|
||||||
92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASImageNode+tvOS.m"; sourceTree = "<group>"; };
|
92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASImageNode+tvOS.m"; sourceTree = "<group>"; };
|
||||||
92074A651CC8BADA00918F75 /* ASControlNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+tvOS.h"; sourceTree = "<group>"; };
|
92074A651CC8BADA00918F75 /* ASControlNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+tvOS.h"; sourceTree = "<group>"; };
|
||||||
@ -908,6 +920,7 @@
|
|||||||
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = "<group>"; };
|
B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = "<group>"; };
|
||||||
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = "<group>"; };
|
B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = "<group>"; };
|
||||||
|
BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.profile.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = "<group>"; };
|
CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = "<group>"; };
|
||||||
CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = "<group>"; };
|
CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = "<group>"; };
|
||||||
CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = "<group>"; };
|
CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = "<group>"; };
|
||||||
@ -1064,6 +1077,8 @@
|
|||||||
92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */,
|
92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */,
|
||||||
AEEC47DF1C20C2DD00EC1693 /* ASVideoNode.h */,
|
AEEC47DF1C20C2DD00EC1693 /* ASVideoNode.h */,
|
||||||
AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */,
|
AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */,
|
||||||
|
8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */,
|
||||||
|
8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */,
|
||||||
055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */,
|
055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */,
|
||||||
AC6456071B0A335000CF11B8 /* ASCellNode.mm */,
|
AC6456071B0A335000CF11B8 /* ASCellNode.mm */,
|
||||||
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */,
|
18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */,
|
||||||
@ -1332,6 +1347,8 @@
|
|||||||
CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
|
CC3B20881C3F7A5400798563 /* ASWeakSet.m */,
|
||||||
DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */,
|
DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */,
|
||||||
DBC452DA1C5BF64600B16017 /* NSArray+Diffing.m */,
|
DBC452DA1C5BF64600B16017 /* NSArray+Diffing.m */,
|
||||||
|
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
|
||||||
|
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
|
||||||
);
|
);
|
||||||
path = Private;
|
path = Private;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1476,6 +1493,7 @@
|
|||||||
children = (
|
children = (
|
||||||
FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */,
|
FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */,
|
||||||
D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */,
|
D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */,
|
||||||
|
BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */,
|
||||||
);
|
);
|
||||||
name = Pods;
|
name = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1570,6 +1588,7 @@
|
|||||||
DBDB83941C6E879900D0098C /* ASPagerFlowLayout.h in Headers */,
|
DBDB83941C6E879900D0098C /* ASPagerFlowLayout.h in Headers */,
|
||||||
058D0A4F195D05CB00B7D73C /* ASImageNode.h in Headers */,
|
058D0A4F195D05CB00B7D73C /* ASImageNode.h in Headers */,
|
||||||
05F20AA41A15733C00DCA68A /* ASImageProtocols.h in Headers */,
|
05F20AA41A15733C00DCA68A /* ASImageProtocols.h in Headers */,
|
||||||
|
8BDA5FC51CDBDDE1007D13B2 /* ASVideoPlayerNode.h in Headers */,
|
||||||
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */,
|
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */,
|
||||||
ACF6ED221B17843500DA7C62 /* ASInsetLayoutSpec.h in Headers */,
|
ACF6ED221B17843500DA7C62 /* ASInsetLayoutSpec.h in Headers */,
|
||||||
ACF6ED4B1B17847A00DA7C62 /* ASInternalHelpers.h in Headers */,
|
ACF6ED4B1B17847A00DA7C62 /* ASInternalHelpers.h in Headers */,
|
||||||
@ -1591,6 +1610,7 @@
|
|||||||
B13CA1001C52004900E031AB /* ASCollectionNode+Beta.h in Headers */,
|
B13CA1001C52004900E031AB /* ASCollectionNode+Beta.h in Headers */,
|
||||||
0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
||||||
0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */,
|
0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */,
|
||||||
|
8B0768B31CE752EC002E1453 /* ASDefaultPlaybackButton.h in Headers */,
|
||||||
058D0A59195D05DC00B7D73C /* ASMutableAttributedStringBuilder.h in Headers */,
|
058D0A59195D05DC00B7D73C /* ASMutableAttributedStringBuilder.h in Headers */,
|
||||||
055B9FA81A1C154B00035D6D /* ASNetworkImageNode.h in Headers */,
|
055B9FA81A1C154B00035D6D /* ASNetworkImageNode.h in Headers */,
|
||||||
ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */,
|
ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */,
|
||||||
@ -1747,6 +1767,7 @@
|
|||||||
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
||||||
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
|
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
|
||||||
B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */,
|
B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */,
|
||||||
|
8BBBAB8C1CEBAF1700107FC6 /* ASDefaultPlaybackButton.h in Headers */,
|
||||||
B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */,
|
B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */,
|
||||||
34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */,
|
34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */,
|
||||||
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */,
|
B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */,
|
||||||
@ -1790,6 +1811,7 @@
|
|||||||
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
|
B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
|
||||||
044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */,
|
044284FF1BAA3BD600D16268 /* UICollectionViewLayout+ASConvenience.h in Headers */,
|
||||||
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */,
|
B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */,
|
||||||
|
8BDA5FC71CDBDF91007D13B2 /* ASVideoPlayerNode.h in Headers */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1836,12 +1858,12 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 058D09D2195D04C000B7D73C /* Build configuration list for PBXNativeTarget "AsyncDisplayKitTests" */;
|
buildConfigurationList = 058D09D2195D04C000B7D73C /* Build configuration list for PBXNativeTarget "AsyncDisplayKitTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
2E61B6A0DB0F436A9DDBE86F /* Check Pods Manifest.lock */,
|
2E61B6A0DB0F436A9DDBE86F /* 📦 Check Pods Manifest.lock */,
|
||||||
058D09B8195D04C000B7D73C /* Sources */,
|
058D09B8195D04C000B7D73C /* Sources */,
|
||||||
058D09B9195D04C000B7D73C /* Frameworks */,
|
058D09B9195D04C000B7D73C /* Frameworks */,
|
||||||
058D09BA195D04C000B7D73C /* Resources */,
|
058D09BA195D04C000B7D73C /* Resources */,
|
||||||
3B9D88CDF51B429C8409E4B6 /* Copy Pods Resources */,
|
3B9D88CDF51B429C8409E4B6 /* 📦 Copy Pods Resources */,
|
||||||
B130AB1AC0A1E5162E211C19 /* Embed Pods Frameworks */,
|
B130AB1AC0A1E5162E211C19 /* 📦 Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -1942,14 +1964,14 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
2E61B6A0DB0F436A9DDBE86F /* Check Pods Manifest.lock */ = {
|
2E61B6A0DB0F436A9DDBE86F /* 📦 Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
);
|
);
|
||||||
name = "Check Pods Manifest.lock";
|
name = "📦 Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -1957,14 +1979,14 @@
|
|||||||
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";
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
3B9D88CDF51B429C8409E4B6 /* Copy Pods Resources */ = {
|
3B9D88CDF51B429C8409E4B6 /* 📦 Copy Pods Resources */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
);
|
);
|
||||||
name = "Copy Pods Resources";
|
name = "📦 Copy Pods Resources";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -1972,14 +1994,14 @@
|
|||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-resources.sh\"\n";
|
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
B130AB1AC0A1E5162E211C19 /* Embed Pods Frameworks */ = {
|
B130AB1AC0A1E5162E211C19 /* 📦 Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
);
|
);
|
||||||
name = "Embed Pods Frameworks";
|
name = "📦 Embed Pods Frameworks";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -2004,6 +2026,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
058D0A22195D050800B7D73C /* _ASAsyncTransaction.mm in Sources */,
|
058D0A22195D050800B7D73C /* _ASAsyncTransaction.mm in Sources */,
|
||||||
|
8B0768B41CE752EC002E1453 /* ASDefaultPlaybackButton.m in Sources */,
|
||||||
E55D86321CA8A14000A0C26F /* ASLayoutable.mm in Sources */,
|
E55D86321CA8A14000A0C26F /* ASLayoutable.mm in Sources */,
|
||||||
68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
|
68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
|
||||||
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
|
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
|
||||||
@ -2108,6 +2131,7 @@
|
|||||||
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
|
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
|
||||||
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
|
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
|
||||||
257754AC1BEE44CD00737CA5 /* ASTextKitRenderer.mm in Sources */,
|
257754AC1BEE44CD00737CA5 /* ASTextKitRenderer.mm in Sources */,
|
||||||
|
8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */,
|
||||||
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */,
|
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */,
|
||||||
257754B21BEE44CD00737CA5 /* ASTextKitShadower.mm in Sources */,
|
257754B21BEE44CD00737CA5 /* ASTextKitShadower.mm in Sources */,
|
||||||
9CFFC6BE1CCAC52B006A6476 /* ASEnvironment.mm in Sources */,
|
9CFFC6BE1CCAC52B006A6476 /* ASEnvironment.mm in Sources */,
|
||||||
@ -2164,6 +2188,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */,
|
9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */,
|
||||||
|
8BBBAB8D1CEBAF1E00107FC6 /* ASDefaultPlaybackButton.m in Sources */,
|
||||||
DE4843DB1C93EAB100A1F33B /* ASLayoutTransition.mm in Sources */,
|
DE4843DB1C93EAB100A1F33B /* ASLayoutTransition.mm in Sources */,
|
||||||
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */,
|
B30BF6541C59D889004FCD53 /* ASLayoutManager.m in Sources */,
|
||||||
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
|
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
|
||||||
@ -2252,6 +2277,7 @@
|
|||||||
B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */,
|
B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */,
|
||||||
B35062561B010EFD0018CF92 /* ASSentinel.m in Sources */,
|
B35062561B010EFD0018CF92 /* ASSentinel.m in Sources */,
|
||||||
9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
|
9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
|
||||||
|
8BDA5FC81CDBDF95007D13B2 /* ASVideoPlayerNode.mm in Sources */,
|
||||||
34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */,
|
34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */,
|
||||||
34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */,
|
34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */,
|
||||||
7AB338661C55B3420055FDE8 /* ASRelativeLayoutSpec.mm in Sources */,
|
7AB338661C55B3420055FDE8 /* ASRelativeLayoutSpec.mm in Sources */,
|
||||||
@ -2648,7 +2674,7 @@
|
|||||||
};
|
};
|
||||||
DB1020821CBCA2AD00FA6FE1 /* Profile */ = {
|
DB1020821CBCA2AD00FA6FE1 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */;
|
baseConfigurationReference = BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CLANG_ENABLE_CODE_COVERAGE = YES;
|
CLANG_ENABLE_CODE_COVERAGE = YES;
|
||||||
|
|||||||
@ -15,8 +15,10 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
ASVideoNodePlayerStateUnknown,
|
ASVideoNodePlayerStateUnknown,
|
||||||
ASVideoNodePlayerStateInitialLoading,
|
ASVideoNodePlayerStateInitialLoading,
|
||||||
ASVideoNodePlayerStateLoading,
|
ASVideoNodePlayerStateReadyToPlay,
|
||||||
|
ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying,
|
||||||
ASVideoNodePlayerStatePlaying,
|
ASVideoNodePlayerStatePlaying,
|
||||||
|
ASVideoNodePlayerStateLoading,
|
||||||
ASVideoNodePlayerStatePaused,
|
ASVideoNodePlayerStatePaused,
|
||||||
ASVideoNodePlayerStateFinished
|
ASVideoNodePlayerStateFinished
|
||||||
} ASVideoNodePlayerState;
|
} ASVideoNodePlayerState;
|
||||||
@ -40,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@property (nullable, atomic, strong, readonly) AVPlayer *player;
|
@property (nullable, atomic, strong, readonly) AVPlayer *player;
|
||||||
@property (nullable, atomic, strong, readonly) AVPlayerItem *currentItem;
|
@property (nullable, atomic, strong, readonly) AVPlayerItem *currentItem;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState.
|
* When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState.
|
||||||
* If it leaves the visible interfaceState it will pause but will resume once it has returned.
|
* If it leaves the visible interfaceState it will pause but will resume once it has returned.
|
||||||
@ -57,9 +60,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
//! Defaults to AVLayerVideoGravityResizeAspect
|
//! Defaults to AVLayerVideoGravityResizeAspect
|
||||||
@property (atomic) NSString *gravity;
|
@property (atomic) NSString *gravity;
|
||||||
|
|
||||||
//! Defaults to an ASDefaultPlayButton instance.
|
|
||||||
@property (nullable, atomic) ASButtonNode *playButton;
|
|
||||||
|
|
||||||
@property (nullable, atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
|
@property (nullable, atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -73,9 +73,7 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
ASImageNode *_placeholderImageNode; // TODO: Make ASVideoNode an ASImageNode subclass; remove this.
|
ASImageNode *_placeholderImageNode; // TODO: Make ASVideoNode an ASImageNode subclass; remove this.
|
||||||
|
|
||||||
ASButtonNode *_playButtonNode;
|
|
||||||
ASDisplayNode *_playerNode;
|
ASDisplayNode *_playerNode;
|
||||||
ASDisplayNode *_spinnerNode;
|
|
||||||
NSString *_gravity;
|
NSString *_gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +91,6 @@ static NSString * const kStatus = @"status";
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.playButton = [[ASDefaultPlayButton alloc] init];
|
|
||||||
self.gravity = AVLayerVideoGravityResizeAspect;
|
self.gravity = AVLayerVideoGravityResizeAspect;
|
||||||
_periodicTimeObserverTimescale = 10000;
|
_periodicTimeObserverTimescale = 10000;
|
||||||
[self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
[self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||||
@ -212,10 +209,7 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
// Stretch out play button, placeholder image player node to the max size
|
// Stretch out play button, placeholder image player node to the max size
|
||||||
NSMutableArray *children = [NSMutableArray array];
|
NSMutableArray *children = [NSMutableArray array];
|
||||||
if (_playButtonNode) {
|
|
||||||
_playButtonNode.preferredFrameSize = maxSize;
|
|
||||||
[children addObject:_playButtonNode];
|
|
||||||
}
|
|
||||||
if (_placeholderImageNode) {
|
if (_placeholderImageNode) {
|
||||||
_placeholderImageNode.preferredFrameSize = maxSize;
|
_placeholderImageNode.preferredFrameSize = maxSize;
|
||||||
[children addObject:_placeholderImageNode];
|
[children addObject:_placeholderImageNode];
|
||||||
@ -225,28 +219,9 @@ static NSString * const kStatus = @"status";
|
|||||||
[children addObject:_playerNode];
|
[children addObject:_playerNode];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center spinner node
|
|
||||||
if (_spinnerNode) {
|
|
||||||
ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode];
|
|
||||||
centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
|
|
||||||
[children addObject:centerLayoutSpec];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layout
|
|
||||||
{
|
|
||||||
[super layout];
|
|
||||||
|
|
||||||
CGRect bounds = self.bounds;
|
|
||||||
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
CGFloat horizontalDiff = (CGRectGetWidth(bounds) - CGRectGetWidth(_playButtonNode.bounds))/2;
|
|
||||||
CGFloat verticalDiff = (CGRectGetHeight(bounds) - CGRectGetHeight(_playButtonNode.bounds))/2;
|
|
||||||
_playButtonNode.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)generatePlaceholderImage
|
- (void)generatePlaceholderImage
|
||||||
{
|
{
|
||||||
ASVideoNode * __weak weakSelf = self;
|
ASVideoNode * __weak weakSelf = self;
|
||||||
@ -319,13 +294,14 @@ static NSString * const kStatus = @"status";
|
|||||||
|
|
||||||
if ([keyPath isEqualToString:kStatus]) {
|
if ([keyPath isEqualToString:kStatus]) {
|
||||||
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
if ([change[NSKeyValueChangeNewKey] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
||||||
[self removeSpinner];
|
self.playerState = ASVideoNodePlayerStateReadyToPlay;
|
||||||
// If we don't yet have a placeholder image update it now that we should have data available for it
|
// If we don't yet have a placeholder image update it now that we should have data available for it
|
||||||
if (_placeholderImageNode.image == nil) {
|
if (_placeholderImageNode.image == nil) {
|
||||||
[self generatePlaceholderImage];
|
[self generatePlaceholderImage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) {
|
} else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) {
|
||||||
|
self.playerState = ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying;
|
||||||
if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || [change[NSKeyValueChangeNewKey] boolValue]) && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || [change[NSKeyValueChangeNewKey] boolValue]) && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||||
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) {
|
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) {
|
||||||
[_delegate videoNodeDidRecoverFromStall:self];
|
[_delegate videoNodeDidRecoverFromStall:self];
|
||||||
@ -335,7 +311,6 @@ static NSString * const kStatus = @"status";
|
|||||||
} else if ([keyPath isEqualToString:kplaybackBufferEmpty]) {
|
} else if ([keyPath isEqualToString:kplaybackBufferEmpty]) {
|
||||||
if (_shouldBePlaying && [change[NSKeyValueChangeNewKey] boolValue] == true && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
if (_shouldBePlaying && [change[NSKeyValueChangeNewKey] boolValue] == true && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||||
self.playerState = ASVideoNodePlayerStateLoading;
|
self.playerState = ASVideoNodePlayerStateLoading;
|
||||||
[self showSpinner];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,26 +430,6 @@ static NSString * const kStatus = @"status";
|
|||||||
_playerState = playerState;
|
_playerState = playerState;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPlayButton:(ASButtonNode *)playButton
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
|
|
||||||
[_playButtonNode removeTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
|
||||||
[_playButtonNode removeFromSupernode];
|
|
||||||
|
|
||||||
_playButtonNode = playButton;
|
|
||||||
[_playButtonNode addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
|
||||||
|
|
||||||
[self addSubnode:playButton];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (ASButtonNode *)playButton
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
return _playButtonNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setAsset:(AVAsset *)asset
|
- (void)setAsset:(AVAsset *)asset
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
@ -488,10 +443,6 @@ static NSString * const kStatus = @"status";
|
|||||||
_asset = asset;
|
_asset = asset;
|
||||||
|
|
||||||
[self setNeedsDataFetch];
|
[self setNeedsDataFetch];
|
||||||
|
|
||||||
if (_shouldAutoplay) {
|
|
||||||
[self play];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (AVAsset *)asset
|
- (AVAsset *)asset
|
||||||
@ -583,11 +534,8 @@ static NSString * const kStatus = @"status";
|
|||||||
if (_playerNode == nil) {
|
if (_playerNode == nil) {
|
||||||
_playerNode = [self constructPlayerNode];
|
_playerNode = [self constructPlayerNode];
|
||||||
|
|
||||||
if (_playButtonNode.supernode == self) {
|
|
||||||
[self insertSubnode:_playerNode belowSubnode:_playButtonNode];
|
|
||||||
} else {
|
|
||||||
[self addSubnode:_playerNode];
|
[self addSubnode:_playerNode];
|
||||||
}
|
|
||||||
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
@ -596,13 +544,9 @@ static NSString * const kStatus = @"status";
|
|||||||
[_player play];
|
[_player play];
|
||||||
_shouldBePlaying = YES;
|
_shouldBePlaying = YES;
|
||||||
|
|
||||||
[UIView animateWithDuration:0.15 animations:^{
|
|
||||||
_playButtonNode.alpha = 0.0;
|
|
||||||
}];
|
|
||||||
if (![self ready]) {
|
if (![self ready]) {
|
||||||
[self showSpinner];
|
self.playerState = ASVideoNodePlayerStateLoading;
|
||||||
} else {
|
} else {
|
||||||
[self removeSpinner];
|
|
||||||
self.playerState = ASVideoNodePlayerStatePlaying;
|
self.playerState = ASVideoNodePlayerStatePlaying;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,35 +556,6 @@ static NSString * const kStatus = @"status";
|
|||||||
return _currentPlayerItem.status == AVPlayerItemStatusReadyToPlay;
|
return _currentPlayerItem.status == AVPlayerItemStatusReadyToPlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showSpinner
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
|
|
||||||
if (!_spinnerNode) {
|
|
||||||
_spinnerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{
|
|
||||||
UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init];
|
|
||||||
spinnnerView.color = [UIColor whiteColor];
|
|
||||||
return spinnnerView;
|
|
||||||
}];
|
|
||||||
_spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0);
|
|
||||||
|
|
||||||
[self addSubnode:_spinnerNode];
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
[(UIActivityIndicatorView *)_spinnerNode.view startAnimating];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeSpinner
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
|
|
||||||
if (!_spinnerNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[_spinnerNode removeFromSupernode];
|
|
||||||
_spinnerNode = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)pause
|
- (void)pause
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
@ -649,11 +564,7 @@ static NSString * const kStatus = @"status";
|
|||||||
}
|
}
|
||||||
self.playerState = ASVideoNodePlayerStatePaused;
|
self.playerState = ASVideoNodePlayerStatePaused;
|
||||||
[_player pause];
|
[_player pause];
|
||||||
[self removeSpinner];
|
|
||||||
_shouldBePlaying = NO;
|
_shouldBePlaying = NO;
|
||||||
[UIView animateWithDuration:0.15 animations:^{
|
|
||||||
_playButtonNode.alpha = 1.0;
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isPlaying
|
- (BOOL)isPlaying
|
||||||
@ -700,7 +611,6 @@ static NSString * const kStatus = @"status";
|
|||||||
- (void)videoNodeDidStall:(NSNotification *)notification
|
- (void)videoNodeDidStall:(NSNotification *)notification
|
||||||
{
|
{
|
||||||
self.playerState = ASVideoNodePlayerStateLoading;
|
self.playerState = ASVideoNodePlayerStateLoading;
|
||||||
[self showSpinner];
|
|
||||||
if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) {
|
if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) {
|
||||||
[_delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
[_delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)];
|
||||||
}
|
}
|
||||||
@ -724,13 +634,6 @@ static NSString * const kStatus = @"status";
|
|||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Internal Properties
|
#pragma mark - Internal Properties
|
||||||
|
|
||||||
- (ASDisplayNode *)spinner
|
|
||||||
{
|
|
||||||
ASDN::MutexLocker l(_videoLock);
|
|
||||||
return _spinnerNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (ASImageNode *)placeholderImageNode
|
- (ASImageNode *)placeholderImageNode
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
@ -794,7 +697,6 @@ static NSString * const kStatus = @"status";
|
|||||||
{
|
{
|
||||||
[_player removeTimeObserver:_timeObserver];
|
[_player removeTimeObserver:_timeObserver];
|
||||||
_timeObserver = nil;
|
_timeObserver = nil;
|
||||||
[_playButtonNode removeTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
|
||||||
[self removePlayerItemObservers:_currentPlayerItem];
|
[self removePlayerItemObservers:_currentPlayerItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
160
AsyncDisplayKit/ASVideoPlayerNode.h
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//
|
||||||
|
// ASVideoPlayerNode.h
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/6/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if TARGET_OS_IOS
|
||||||
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
//#import <AsyncDisplayKit/ASThread.h>
|
||||||
|
//#import <AsyncDisplayKit/ASVideoNode.h>
|
||||||
|
//#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||||
|
|
||||||
|
@class AVAsset;
|
||||||
|
@protocol ASVideoPlayerNodeDelegate;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ASVideoPlayerNodeControlTypePlaybackButton,
|
||||||
|
ASVideoPlayerNodeControlTypeElapsedText,
|
||||||
|
ASVideoPlayerNodeControlTypeDurationText,
|
||||||
|
ASVideoPlayerNodeControlTypeScrubber,
|
||||||
|
ASVideoPlayerNodeControlTypeFlexGrowSpacer,
|
||||||
|
} ASVideoPlayerNodeControlType;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ASVideoPlayerNode : ASDisplayNode
|
||||||
|
|
||||||
|
@property (nullable, atomic, weak) id<ASVideoPlayerNodeDelegate> delegate;
|
||||||
|
|
||||||
|
@property (nonatomic, assign, readonly) CMTime duration;
|
||||||
|
|
||||||
|
@property (nonatomic, assign) BOOL controlsDisabled;
|
||||||
|
|
||||||
|
@property (nonatomic, assign, readonly) BOOL loadAssetWhenNodeBecomesVisible;
|
||||||
|
|
||||||
|
#pragma mark - ASVideoNode property proxy
|
||||||
|
/**
|
||||||
|
* When shouldAutoplay is set to true, a video node will play when it has both loaded and entered the "visible" interfaceState.
|
||||||
|
* If it leaves the visible interfaceState it will pause but will resume once it has returned.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign, readwrite) BOOL shouldAutoPlay;
|
||||||
|
@property (nonatomic, assign, readwrite) BOOL shouldAutoRepeat;
|
||||||
|
@property (nonatomic, assign, readwrite) BOOL muted;
|
||||||
|
@property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState;
|
||||||
|
@property (nonatomic, assign, readwrite) BOOL shouldAggressivelyRecoverFromStall;
|
||||||
|
|
||||||
|
//! Defaults to 100
|
||||||
|
@property (nonatomic, assign) int32_t periodicTimeObserverTimescale;
|
||||||
|
//! Defaults to AVLayerVideoGravityResizeAspect
|
||||||
|
@property (atomic) NSString *gravity;
|
||||||
|
|
||||||
|
- (instancetype)initWithUrl:(NSURL*)url;
|
||||||
|
- (instancetype)initWithAsset:(AVAsset*)asset;
|
||||||
|
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
|
||||||
|
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
|
||||||
|
|
||||||
|
#pragma mark - Public API
|
||||||
|
- (void)seekToTime:(CGFloat)percentComplete;
|
||||||
|
- (void)play;
|
||||||
|
- (void)pause;
|
||||||
|
- (BOOL)isPlaying;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#pragma mark - ASVideoPlayerNodeDelegate -
|
||||||
|
@protocol ASVideoPlayerNodeDelegate <NSObject>
|
||||||
|
@optional
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked before creating controlbar controls
|
||||||
|
* @param videoPlayer
|
||||||
|
*/
|
||||||
|
- (NSArray *)videoPlayerNodeNeededDefaultControls:(ASVideoPlayerNode*)videoPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked before creating default controls, asks delegate for custom controls dictionary.
|
||||||
|
* This dictionary must constain only ASDisplayNode subclass objects.
|
||||||
|
* @param videoPlayer
|
||||||
|
* @discussion - This method is invoked only when developer implements videoPlayerNodeLayoutSpec:forControls:forMaximumSize:
|
||||||
|
* and gives ability to add custom constrols to ASVideoPlayerNode, for example mute button.
|
||||||
|
*/
|
||||||
|
- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode*)videoPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked in layoutSpecThatFits:
|
||||||
|
* @param videoPlayer
|
||||||
|
* @param controls - Dictionary of controls which are used in videoPlayer; Dictionary keys are ASVideoPlayerNodeControlType
|
||||||
|
* @param maxSize - Maximum size for ASVideoPlayerNode
|
||||||
|
* @discussion - Developer can layout whole ASVideoPlayerNode as he wants. ASVideoNode is locked and it can't be changed
|
||||||
|
*/
|
||||||
|
- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
forControls:(NSDictionary *)controls
|
||||||
|
forMaximumSize:(CGSize)maxSize;
|
||||||
|
|
||||||
|
#pragma mark Text delegate methods
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked before creating ASVideoPlayerNodeControlTypeElapsedText and ASVideoPlayerNodeControlTypeDurationText
|
||||||
|
* @param videoPlayer
|
||||||
|
* @param timeLabelType
|
||||||
|
*/
|
||||||
|
- (NSDictionary *)videoPlayerNodeTimeLabelAttributes:(ASVideoPlayerNode *)videoPlayerNode timeLabelType:(ASVideoPlayerNodeControlType)timeLabelType;
|
||||||
|
- (NSString *)videoPlayerNode:(ASVideoPlayerNode *)videoPlayerNode
|
||||||
|
timeStringForTimeLabelType:(ASVideoPlayerNodeControlType)timeLabelType
|
||||||
|
forTime:(CMTime)time;
|
||||||
|
|
||||||
|
#pragma mark Scrubber delegate methods
|
||||||
|
- (UIColor *)videoPlayerNodeScrubberMaximumTrackTint:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
- (UIColor *)videoPlayerNodeScrubberMinimumTrackTint:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
- (UIColor *)videoPlayerNodeScrubberThumbTint:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
- (UIImage *)videoPlayerNodeScrubberThumbImage:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
|
||||||
|
#pragma mark - Spinner delegate methods
|
||||||
|
- (UIColor *)videoPlayerNodeSpinnerTint:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
|
||||||
|
#pragma mark - Playback button delegate methods
|
||||||
|
- (UIColor *)videoPlayerNodePlaybackButtonTint:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark ASVideoNodeDelegate proxy methods
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked when ASVideoPlayerNode is taped.
|
||||||
|
* @param videoPlayerNode The ASVideoPlayerNode that was tapped.
|
||||||
|
*/
|
||||||
|
- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked when ASVideoNode playback time is updated.
|
||||||
|
* @param videoPlayerNode The video player node
|
||||||
|
* @param second current playback time.
|
||||||
|
*/
|
||||||
|
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didPlayToTime:(CMTime)time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked when ASVideoNode changes state.
|
||||||
|
* @param videoPlayerNode The ASVideoPlayerNode whose ASVideoNode is changing state.
|
||||||
|
* @param state ASVideoNode state before this change.
|
||||||
|
* @param toSate ASVideoNode new state.
|
||||||
|
* @discussion This method is called after each state change
|
||||||
|
*/
|
||||||
|
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer willChangeVideoNodeState:(ASVideoNodePlayerState)state toVideoNodeState:(ASVideoNodePlayerState)toState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method is invoked when ASVideoNode decides to change state.
|
||||||
|
* @param videoPlayerNode The ASVideoPlayerNode whose ASVideoNode is changing state.
|
||||||
|
* @param state ASVideoNode that is going to be set.
|
||||||
|
* @discussion Delegate method invoked when player changes it's state to
|
||||||
|
* ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused
|
||||||
|
* and asks delegate if state change is valid
|
||||||
|
*/
|
||||||
|
- (BOOL)videoPlayerNode:(ASVideoPlayerNode*)videoPlayer shouldChangeVideoNodeStateTo:(ASVideoNodePlayerState)state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @abstract Delegate method invoked when the ASVideoNode has played to its end time.
|
||||||
|
* @param videoPlayerNode The video node has played to its end time.
|
||||||
|
*/
|
||||||
|
- (void)videoPlayerNodeDidPlayToEnd:(ASVideoPlayerNode *)videoPlayer;
|
||||||
|
|
||||||
|
@end
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
#endif
|
||||||
801
AsyncDisplayKit/ASVideoPlayerNode.mm
Normal file
@ -0,0 +1,801 @@
|
|||||||
|
//
|
||||||
|
// ASVideoPlayerNode.m
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/6/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ASVideoPlayerNode.h"
|
||||||
|
#import "ASDefaultPlaybackButton.h"
|
||||||
|
|
||||||
|
static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||||
|
|
||||||
|
@interface ASVideoPlayerNode() <ASVideoNodeDelegate>
|
||||||
|
{
|
||||||
|
ASDN::RecursiveMutex _videoPlayerLock;
|
||||||
|
|
||||||
|
__weak id<ASVideoPlayerNodeDelegate> _delegate;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
unsigned int delegateNeededDefaultControls:1;
|
||||||
|
unsigned int delegateCustomControls:1;
|
||||||
|
unsigned int delegateSpinnerTintColor:1;
|
||||||
|
unsigned int delegatePlaybackButtonTint:1;
|
||||||
|
unsigned int delegateScrubberMaximumTrackTintColor:1;
|
||||||
|
unsigned int delegateScrubberMinimumTrackTintColor:1;
|
||||||
|
unsigned int delegateScrubberThumbTintColor:1;
|
||||||
|
unsigned int delegateScrubberThumbImage:1;
|
||||||
|
unsigned int delegateTimeLabelAttributes:1;
|
||||||
|
unsigned int delegateTimeLabelAttributedString:1;
|
||||||
|
unsigned int delegateLayoutSpecForControls:1;
|
||||||
|
unsigned int delegateVideoNodeDidPlayToTime:1;
|
||||||
|
unsigned int delegateVideoNodeWillChangeState:1;
|
||||||
|
unsigned int delegateVideoNodeShouldChangeState:1;
|
||||||
|
unsigned int delegateVideoNodePlaybackDidFinish:1;
|
||||||
|
unsigned int delegateDidTapVideoPlayerNode:1;
|
||||||
|
} _delegateFlags;
|
||||||
|
|
||||||
|
NSURL *_url;
|
||||||
|
AVAsset *_asset;
|
||||||
|
|
||||||
|
ASVideoNode *_videoNode;
|
||||||
|
|
||||||
|
NSArray *_neededDefaultControls;
|
||||||
|
|
||||||
|
NSMutableDictionary *_cachedControls;
|
||||||
|
|
||||||
|
ASDefaultPlaybackButton *_playbackButtonNode;
|
||||||
|
ASTextNode *_elapsedTextNode;
|
||||||
|
ASTextNode *_durationTextNode;
|
||||||
|
ASDisplayNode *_scrubberNode;
|
||||||
|
ASStackLayoutSpec *_controlFlexGrowSpacerSpec;
|
||||||
|
ASDisplayNode *_spinnerNode;
|
||||||
|
|
||||||
|
BOOL _loadAssetWhenNodeBecomesVisible;
|
||||||
|
BOOL _isSeeking;
|
||||||
|
CMTime _duration;
|
||||||
|
|
||||||
|
BOOL _controlsDisabled;
|
||||||
|
|
||||||
|
BOOL _shouldAutoPlay;
|
||||||
|
BOOL _shouldAutoRepeat;
|
||||||
|
BOOL _muted;
|
||||||
|
int32_t _periodicTimeObserverTimescale;
|
||||||
|
NSString *_gravity;
|
||||||
|
|
||||||
|
BOOL _shouldAggressivelyRecoverFromStall;
|
||||||
|
|
||||||
|
UIColor *_defaultControlsColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ASVideoPlayerNode
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self _init];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUrl:(NSURL*)url
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
_url = url;
|
||||||
|
_asset = [AVAsset assetWithURL:_url];
|
||||||
|
_loadAssetWhenNodeBecomesVisible = YES;
|
||||||
|
|
||||||
|
[self _init];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithAsset:(AVAsset *)asset
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
_asset = asset;
|
||||||
|
_loadAssetWhenNodeBecomesVisible = YES;
|
||||||
|
|
||||||
|
[self _init];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
_url = url;
|
||||||
|
_asset = [AVAsset assetWithURL:_url];
|
||||||
|
_loadAssetWhenNodeBecomesVisible = loadAssetWhenNodeBecomesVisible;
|
||||||
|
|
||||||
|
[self _init];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
_asset = asset;
|
||||||
|
_loadAssetWhenNodeBecomesVisible = loadAssetWhenNodeBecomesVisible;
|
||||||
|
|
||||||
|
[self _init];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_init
|
||||||
|
{
|
||||||
|
_defaultControlsColor = [UIColor whiteColor];
|
||||||
|
_cachedControls = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
|
_videoNode = [[ASVideoNode alloc] init];
|
||||||
|
_videoNode.delegate = self;
|
||||||
|
if (_loadAssetWhenNodeBecomesVisible == NO) {
|
||||||
|
_videoNode.asset = _asset;
|
||||||
|
}
|
||||||
|
[self addSubnode:_videoNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didLoad
|
||||||
|
{
|
||||||
|
[super didLoad];
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_videoPlayerLock);
|
||||||
|
[self createControls];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)visibilityDidChange:(BOOL)isVisible
|
||||||
|
{
|
||||||
|
[super visibilityDidChange:isVisible];
|
||||||
|
|
||||||
|
ASDN::MutexLocker l(_videoPlayerLock);
|
||||||
|
|
||||||
|
if (isVisible && _loadAssetWhenNodeBecomesVisible && _asset != _videoNode.asset) {
|
||||||
|
_videoNode.asset = _asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)createDefaultControlElementArray
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateNeededDefaultControls) {
|
||||||
|
return [_delegate videoPlayerNodeNeededDefaultControls:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
return @[ @(ASVideoPlayerNodeControlTypePlaybackButton),
|
||||||
|
@(ASVideoPlayerNodeControlTypeElapsedText),
|
||||||
|
@(ASVideoPlayerNodeControlTypeScrubber),
|
||||||
|
@(ASVideoPlayerNodeControlTypeDurationText) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - UI
|
||||||
|
- (void)createControls
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_videoPlayerLock);
|
||||||
|
|
||||||
|
if (_controlsDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_neededDefaultControls == nil) {
|
||||||
|
_neededDefaultControls = [self createDefaultControlElementArray];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedControls == nil) {
|
||||||
|
_cachedControls = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id object in _neededDefaultControls) {
|
||||||
|
ASVideoPlayerNodeControlType type = (ASVideoPlayerNodeControlType)[object integerValue];
|
||||||
|
switch (type) {
|
||||||
|
case ASVideoPlayerNodeControlTypePlaybackButton:
|
||||||
|
[self createPlaybackButton];
|
||||||
|
break;
|
||||||
|
case ASVideoPlayerNodeControlTypeElapsedText:
|
||||||
|
[self createElapsedTextField];
|
||||||
|
break;
|
||||||
|
case ASVideoPlayerNodeControlTypeDurationText:
|
||||||
|
[self createDurationTextField];
|
||||||
|
break;
|
||||||
|
case ASVideoPlayerNodeControlTypeScrubber:
|
||||||
|
[self createScrubber];
|
||||||
|
break;
|
||||||
|
case ASVideoPlayerNodeControlTypeFlexGrowSpacer:
|
||||||
|
[self createControlFlexGrowSpacer];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_delegateFlags.delegateCustomControls && _delegateFlags.delegateLayoutSpecForControls) {
|
||||||
|
NSDictionary *customControls = [_delegate videoPlayerNodeCustomControls:self];
|
||||||
|
for (id key in customControls) {
|
||||||
|
id node = customControls[key];
|
||||||
|
if (![node isKindOfClass:[ASDisplayNode class]]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self addSubnode:node];
|
||||||
|
[_cachedControls setObject:node forKey:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASPerformBlockOnMainThread(^{
|
||||||
|
ASDN::MutexLocker l(_videoPlayerLock);
|
||||||
|
[self setNeedsLayout];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeControls
|
||||||
|
{
|
||||||
|
NSArray *controls = [_cachedControls allValues];
|
||||||
|
[controls enumerateObjectsUsingBlock:^(ASDisplayNode *_Nonnull node, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||||
|
[node removeFromSupernode];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self cleanCachedControls];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cleanCachedControls
|
||||||
|
{
|
||||||
|
[_cachedControls removeAllObjects];
|
||||||
|
|
||||||
|
_playbackButtonNode = nil;
|
||||||
|
_elapsedTextNode = nil;
|
||||||
|
_durationTextNode = nil;
|
||||||
|
_scrubberNode = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)createPlaybackButton
|
||||||
|
{
|
||||||
|
if (_playbackButtonNode == nil) {
|
||||||
|
_playbackButtonNode = [[ASDefaultPlaybackButton alloc] init];
|
||||||
|
_playbackButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
|
||||||
|
if (_delegateFlags.delegatePlaybackButtonTint) {
|
||||||
|
_playbackButtonNode.tintColor = [_delegate videoPlayerNodePlaybackButtonTint:self];
|
||||||
|
} else {
|
||||||
|
_playbackButtonNode.tintColor = _defaultControlsColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) {
|
||||||
|
_playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePause;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_playbackButtonNode addTarget:self action:@selector(didTapPlaybackButton:) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||||
|
[_cachedControls setObject:_playbackButtonNode forKey:@(ASVideoPlayerNodeControlTypePlaybackButton)];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self addSubnode:_playbackButtonNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)createElapsedTextField
|
||||||
|
{
|
||||||
|
if (_elapsedTextNode == nil) {
|
||||||
|
_elapsedTextNode = [[ASTextNode alloc] init];
|
||||||
|
_elapsedTextNode.attributedString = [self timeLabelAttributedStringForString:@"00:00"
|
||||||
|
forControlType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||||
|
_elapsedTextNode.truncationMode = NSLineBreakByClipping;
|
||||||
|
|
||||||
|
[_cachedControls setObject:_elapsedTextNode forKey:@(ASVideoPlayerNodeControlTypeElapsedText)];
|
||||||
|
}
|
||||||
|
[self addSubnode:_elapsedTextNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)createDurationTextField
|
||||||
|
{
|
||||||
|
if (_durationTextNode == nil) {
|
||||||
|
_durationTextNode = [[ASTextNode alloc] init];
|
||||||
|
_durationTextNode.attributedString = [self timeLabelAttributedStringForString:@"00:00"
|
||||||
|
forControlType:ASVideoPlayerNodeControlTypeDurationText];
|
||||||
|
_durationTextNode.truncationMode = NSLineBreakByClipping;
|
||||||
|
|
||||||
|
[_cachedControls setObject:_durationTextNode forKey:@(ASVideoPlayerNodeControlTypeDurationText)];
|
||||||
|
}
|
||||||
|
[self addSubnode:_durationTextNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)createScrubber
|
||||||
|
{
|
||||||
|
if (_scrubberNode == nil) {
|
||||||
|
_scrubberNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
|
||||||
|
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectZero];
|
||||||
|
slider.minimumValue = 0.0;
|
||||||
|
slider.maximumValue = 1.0;
|
||||||
|
|
||||||
|
if (_delegateFlags.delegateScrubberMinimumTrackTintColor) {
|
||||||
|
slider.minimumTrackTintColor = [_delegate videoPlayerNodeScrubberMinimumTrackTint:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_delegateFlags.delegateScrubberMaximumTrackTintColor) {
|
||||||
|
slider.maximumTrackTintColor = [_delegate videoPlayerNodeScrubberMaximumTrackTint:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_delegateFlags.delegateScrubberThumbTintColor) {
|
||||||
|
slider.thumbTintColor = [_delegate videoPlayerNodeScrubberThumbTint:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_delegateFlags.delegateScrubberThumbImage) {
|
||||||
|
UIImage *thumbImage = [_delegate videoPlayerNodeScrubberThumbImage:self];
|
||||||
|
[slider setThumbImage:thumbImage forState:UIControlStateNormal];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[slider addTarget:self action:@selector(beginSeek) forControlEvents:UIControlEventTouchDown];
|
||||||
|
[slider addTarget:self action:@selector(endSeek) forControlEvents:UIControlEventTouchUpInside|UIControlEventTouchUpOutside|UIControlEventTouchCancel];
|
||||||
|
[slider addTarget:self action:@selector(seekTimeDidChange:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
|
return slider;
|
||||||
|
}];
|
||||||
|
|
||||||
|
_scrubberNode.flexShrink = YES;
|
||||||
|
|
||||||
|
[_cachedControls setObject:_scrubberNode forKey:@(ASVideoPlayerNodeControlTypeScrubber)];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self addSubnode:_scrubberNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)createControlFlexGrowSpacer
|
||||||
|
{
|
||||||
|
if (_controlFlexGrowSpacerSpec == nil) {
|
||||||
|
_controlFlexGrowSpacerSpec = [[ASStackLayoutSpec alloc] init];
|
||||||
|
_controlFlexGrowSpacerSpec.flexGrow = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_cachedControls setObject:_controlFlexGrowSpacerSpec forKey:@(ASVideoPlayerNodeControlTypeFlexGrowSpacer)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateDurationTimeLabel
|
||||||
|
{
|
||||||
|
if (!_durationTextNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSString *formattedDuration = [self timeStringForCMTime:_duration forTimeLabelType:ASVideoPlayerNodeControlTypeDurationText];
|
||||||
|
_durationTextNode.attributedString = [self timeLabelAttributedStringForString:formattedDuration forControlType:ASVideoPlayerNodeControlTypeDurationText];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateElapsedTimeLabel:(NSTimeInterval)seconds
|
||||||
|
{
|
||||||
|
if (!_elapsedTextNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSString *formatteElapsed = [self timeStringForCMTime:CMTimeMakeWithSeconds( seconds, _videoNode.periodicTimeObserverTimescale ) forTimeLabelType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||||
|
_elapsedTextNode.attributedString = [self timeLabelAttributedStringForString:formatteElapsed forControlType:ASVideoPlayerNodeControlTypeElapsedText];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSAttributedString*)timeLabelAttributedStringForString:(NSString*)string forControlType:(ASVideoPlayerNodeControlType)controlType
|
||||||
|
{
|
||||||
|
NSDictionary *options;
|
||||||
|
if (_delegateFlags.delegateTimeLabelAttributes) {
|
||||||
|
options = [_delegate videoPlayerNodeTimeLabelAttributes:self timeLabelType:controlType];
|
||||||
|
} else {
|
||||||
|
options = @{
|
||||||
|
NSFontAttributeName : [UIFont systemFontOfSize:12.0],
|
||||||
|
NSForegroundColorAttributeName: _defaultControlsColor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:options];
|
||||||
|
|
||||||
|
return attributedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - ASVideoNodeDelegate
|
||||||
|
- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateVideoNodeWillChangeState) {
|
||||||
|
[_delegate videoPlayerNode:self willChangeVideoNodeState:state toVideoNodeState:toState];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toState == ASVideoNodePlayerStateReadyToPlay) {
|
||||||
|
_duration = _videoNode.currentItem.duration;
|
||||||
|
[self updateDurationTimeLabel];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toState == ASVideoNodePlayerStatePlaying) {
|
||||||
|
_playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePause;
|
||||||
|
[self removeSpinner];
|
||||||
|
} else if (toState != ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying && toState != ASVideoNodePlayerStateReadyToPlay) {
|
||||||
|
_playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toState == ASVideoNodePlayerStateLoading || toState == ASVideoNodePlayerStateInitialLoading) {
|
||||||
|
[self showSpinner];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toState == ASVideoNodePlayerStateReadyToPlay || toState == ASVideoNodePlayerStatePaused || toState == ASVideoNodePlayerStatePlaybackLikelyToKeepUpButNotPlaying) {
|
||||||
|
[self removeSpinner];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)videoNode:(ASVideoNode *)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateVideoNodeShouldChangeState) {
|
||||||
|
return [_delegate videoPlayerNode:self shouldChangeVideoNodeStateTo:state];
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateVideoNodeDidPlayToTime) {
|
||||||
|
[_delegate videoPlayerNode:self didPlayToTime:_videoNode.player.currentTime];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isSeeking) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_elapsedTextNode) {
|
||||||
|
[self updateElapsedTimeLabel:timeInterval];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_scrubberNode) {
|
||||||
|
[(UISlider*)_scrubberNode.view setValue:( timeInterval / CMTimeGetSeconds(_duration) ) animated:NO];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)videoDidPlayToEnd:(ASVideoNode *)videoNode
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateVideoNodePlaybackDidFinish) {
|
||||||
|
[_delegate videoPlayerNodeDidPlayToEnd:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didTapVideoNode:(ASVideoNode *)videoNode
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateDidTapVideoPlayerNode) {
|
||||||
|
[_delegate didTapVideoPlayerNode:self];
|
||||||
|
} else {
|
||||||
|
[self togglePlayPause];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Actions
|
||||||
|
- (void)togglePlayPause
|
||||||
|
{
|
||||||
|
if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) {
|
||||||
|
[_videoNode pause];
|
||||||
|
} else {
|
||||||
|
[_videoNode play];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showSpinner
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_videoPlayerLock);
|
||||||
|
|
||||||
|
if (!_spinnerNode) {
|
||||||
|
_spinnerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{
|
||||||
|
UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init];
|
||||||
|
spinnnerView.color = _defaultControlsColor;
|
||||||
|
if (_delegateFlags.delegateSpinnerTintColor) {
|
||||||
|
spinnnerView.color = [_delegate videoPlayerNodeSpinnerTint:self];
|
||||||
|
}
|
||||||
|
return spinnnerView;
|
||||||
|
}];
|
||||||
|
_spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0);
|
||||||
|
|
||||||
|
[self addSubnode:_spinnerNode];
|
||||||
|
[self setNeedsLayout];
|
||||||
|
}
|
||||||
|
[(UIActivityIndicatorView *)_spinnerNode.view startAnimating];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeSpinner
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_videoPlayerLock);
|
||||||
|
|
||||||
|
if (!_spinnerNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[_spinnerNode removeFromSupernode];
|
||||||
|
_spinnerNode = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didTapPlaybackButton:(ASControlNode*)node
|
||||||
|
{
|
||||||
|
[self togglePlayPause];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)beginSeek
|
||||||
|
{
|
||||||
|
_isSeeking = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)endSeek
|
||||||
|
{
|
||||||
|
_isSeeking = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)seekTimeDidChange:(UISlider*)slider
|
||||||
|
{
|
||||||
|
CGFloat percentage = slider.value * 100;
|
||||||
|
[self seekToTime:percentage];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public API
|
||||||
|
- (void)seekToTime:(CGFloat)percentComplete
|
||||||
|
{
|
||||||
|
CGFloat seconds = ( CMTimeGetSeconds(_duration) * percentComplete ) / 100;
|
||||||
|
|
||||||
|
[self updateElapsedTimeLabel:seconds];
|
||||||
|
[_videoNode.player seekToTime:CMTimeMakeWithSeconds(seconds, _videoNode.periodicTimeObserverTimescale)];
|
||||||
|
|
||||||
|
if (_videoNode.playerState != ASVideoNodePlayerStatePlaying) {
|
||||||
|
[self togglePlayPause];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)play
|
||||||
|
{
|
||||||
|
[_videoNode play];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pause
|
||||||
|
{
|
||||||
|
[_videoNode pause];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isPlaying
|
||||||
|
{
|
||||||
|
return [_videoNode isPlaying];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)controlsForLayoutSpec
|
||||||
|
{
|
||||||
|
NSMutableArray *controls = [[NSMutableArray alloc] initWithCapacity:_cachedControls.count];
|
||||||
|
|
||||||
|
if (_cachedControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]) {
|
||||||
|
[controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]) {
|
||||||
|
[controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
|
||||||
|
[controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cachedControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]) {
|
||||||
|
[controls addObject:_cachedControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Layout
|
||||||
|
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||||
|
{
|
||||||
|
CGSize maxSize = constrainedSize.max;
|
||||||
|
if (!CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero)) {
|
||||||
|
maxSize = self.preferredFrameSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent crashes through if infinite width or height
|
||||||
|
if (isinf(maxSize.width) || isinf(maxSize.height)) {
|
||||||
|
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoPlayerNode");
|
||||||
|
maxSize = CGSizeZero;
|
||||||
|
}
|
||||||
|
_videoNode.preferredFrameSize = maxSize;
|
||||||
|
|
||||||
|
ASLayoutSpec *layoutSpec;
|
||||||
|
|
||||||
|
if (_delegateFlags.delegateLayoutSpecForControls) {
|
||||||
|
layoutSpec = [_delegate videoPlayerNodeLayoutSpec:self forControls:_cachedControls forMaximumSize:maxSize];
|
||||||
|
} else {
|
||||||
|
layoutSpec = [self defaultLayoutSpecThatFits:maxSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableArray *children = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
if (_spinnerNode) {
|
||||||
|
ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode];
|
||||||
|
centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
|
||||||
|
[children addObject:centerLayoutSpec];
|
||||||
|
}
|
||||||
|
|
||||||
|
ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec];
|
||||||
|
overlaySpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
|
||||||
|
|
||||||
|
[children addObject:overlaySpec];
|
||||||
|
|
||||||
|
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASLayoutSpec*)defaultLayoutSpecThatFits:(CGSize)maxSize
|
||||||
|
{
|
||||||
|
_scrubberNode.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
|
||||||
|
|
||||||
|
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||||
|
spacer.flexGrow = YES;
|
||||||
|
|
||||||
|
ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||||
|
spacing:10.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsCenter
|
||||||
|
children: [self controlsForLayoutSpec] ];
|
||||||
|
controlbarSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
|
||||||
|
|
||||||
|
ASInsetLayoutSpec *controlbarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:controlbarSpec];
|
||||||
|
|
||||||
|
controlbarInsetSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
ASStackLayoutSpec *mainVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||||
|
spacing:0.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsStart
|
||||||
|
children:@[spacer,controlbarInsetSpec]];
|
||||||
|
|
||||||
|
return mainVerticalStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Properties
|
||||||
|
- (id<ASVideoPlayerNodeDelegate>)delegate
|
||||||
|
{
|
||||||
|
return _delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDelegate:(id<ASVideoPlayerNodeDelegate>)delegate
|
||||||
|
{
|
||||||
|
if (delegate == _delegate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_delegate = delegate;
|
||||||
|
|
||||||
|
if (_delegate == nil) {
|
||||||
|
memset(&_delegateFlags, 0, sizeof(_delegateFlags));
|
||||||
|
} else {
|
||||||
|
_delegateFlags.delegateNeededDefaultControls = [_delegate respondsToSelector:@selector(videoPlayerNodeNeededDefaultControls:)];
|
||||||
|
_delegateFlags.delegateCustomControls = [_delegate respondsToSelector:@selector(videoPlayerNodeCustomControls:)];
|
||||||
|
_delegateFlags.delegateSpinnerTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeSpinnerTint:)];
|
||||||
|
_delegateFlags.delegateScrubberMaximumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMaximumTrackTint:)];
|
||||||
|
_delegateFlags.delegateScrubberMinimumTrackTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberMinimumTrackTint:)];
|
||||||
|
_delegateFlags.delegateScrubberThumbTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbTint:)];
|
||||||
|
_delegateFlags.delegateScrubberThumbImage = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbImage:)];
|
||||||
|
_delegateFlags.delegateTimeLabelAttributes = [_delegate respondsToSelector:@selector(videoPlayerNodeTimeLabelAttributes:timeLabelType:)];
|
||||||
|
_delegateFlags.delegateLayoutSpecForControls = [_delegate respondsToSelector:@selector(videoPlayerNodeLayoutSpec:forControls:forMaximumSize:)];
|
||||||
|
_delegateFlags.delegateVideoNodeDidPlayToTime = [_delegate respondsToSelector:@selector(videoPlayerNode:didPlayToTime:)];
|
||||||
|
_delegateFlags.delegateVideoNodeWillChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:willChangeVideoNodeState:toVideoNodeState:)];
|
||||||
|
_delegateFlags.delegateVideoNodePlaybackDidFinish = [_delegate respondsToSelector:@selector(videoPlayerNodeDidPlayToEnd:)];
|
||||||
|
_delegateFlags.delegateVideoNodeShouldChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:shouldChangeVideoNodeStateTo:)];
|
||||||
|
_delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)];
|
||||||
|
_delegateFlags.delegatePlaybackButtonTint = [_delegate respondsToSelector:@selector(videoPlayerNodePlaybackButtonTint:)];
|
||||||
|
_delegateFlags.delegateDidTapVideoPlayerNode = [_delegate respondsToSelector:@selector(didTapVideoPlayerNode:)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setControlsDisabled:(BOOL)controlsDisabled
|
||||||
|
{
|
||||||
|
if (_controlsDisabled == controlsDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_controlsDisabled = controlsDisabled;
|
||||||
|
|
||||||
|
if (_controlsDisabled && _cachedControls.count > 0) {
|
||||||
|
[self removeControls];
|
||||||
|
} else if (!_controlsDisabled) {
|
||||||
|
[self createControls];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setShouldAutoPlay:(BOOL)shouldAutoPlay
|
||||||
|
{
|
||||||
|
if (_shouldAutoPlay == shouldAutoPlay) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_shouldAutoPlay = shouldAutoPlay;
|
||||||
|
_videoNode.shouldAutoplay = _shouldAutoPlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setShouldAutoRepeat:(BOOL)shouldAutoRepeat
|
||||||
|
{
|
||||||
|
if (_shouldAutoRepeat == shouldAutoRepeat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_shouldAutoRepeat = shouldAutoRepeat;
|
||||||
|
_videoNode.shouldAutorepeat = _shouldAutoRepeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMuted:(BOOL)muted
|
||||||
|
{
|
||||||
|
if (_muted == muted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_muted = muted;
|
||||||
|
_videoNode.muted = _muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPeriodicTimeObserverTimescale:(int32_t)periodicTimeObserverTimescale
|
||||||
|
{
|
||||||
|
if (_periodicTimeObserverTimescale == periodicTimeObserverTimescale) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_periodicTimeObserverTimescale = periodicTimeObserverTimescale;
|
||||||
|
_videoNode.periodicTimeObserverTimescale = _periodicTimeObserverTimescale;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)gravity
|
||||||
|
{
|
||||||
|
if (_gravity == nil) {
|
||||||
|
_gravity = _videoNode.gravity;
|
||||||
|
}
|
||||||
|
return _gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setGravity:(NSString *)gravity
|
||||||
|
{
|
||||||
|
if (_gravity == gravity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_gravity = gravity;
|
||||||
|
_videoNode.gravity = _gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASVideoNodePlayerState)playerState
|
||||||
|
{
|
||||||
|
return _videoNode.playerState;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)shouldAggressivelyRecoverFromStall
|
||||||
|
{
|
||||||
|
return _videoNode.shouldAggressivelyRecoverFromStall;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setShouldAggressivelyRecoverFromStall:(BOOL)shouldAggressivelyRecoverFromStall
|
||||||
|
{
|
||||||
|
if (_shouldAggressivelyRecoverFromStall == shouldAggressivelyRecoverFromStall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_shouldAggressivelyRecoverFromStall = shouldAggressivelyRecoverFromStall;
|
||||||
|
_videoNode.shouldAggressivelyRecoverFromStall = _shouldAggressivelyRecoverFromStall;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Helpers
|
||||||
|
- (NSString *)timeStringForCMTime:(CMTime)time forTimeLabelType:(ASVideoPlayerNodeControlType)type
|
||||||
|
{
|
||||||
|
if (_delegateFlags.delegateTimeLabelAttributedString) {
|
||||||
|
return [_delegate videoPlayerNode:self timeStringForTimeLabelType:type forTime:time];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSUInteger dTotalSeconds = CMTimeGetSeconds(time);
|
||||||
|
|
||||||
|
NSUInteger dHours = floor(dTotalSeconds / 3600);
|
||||||
|
NSUInteger dMinutes = floor(dTotalSeconds % 3600 / 60);
|
||||||
|
NSUInteger dSeconds = floor(dTotalSeconds % 3600 % 60);
|
||||||
|
|
||||||
|
NSString *videoDurationText;
|
||||||
|
if (dHours > 0) {
|
||||||
|
videoDurationText = [NSString stringWithFormat:@"%i:%02i:%02i", (int)dHours, (int)dMinutes, (int)dSeconds];
|
||||||
|
} else {
|
||||||
|
videoDurationText = [NSString stringWithFormat:@"%02i:%02i", (int)dMinutes, (int)dSeconds];
|
||||||
|
}
|
||||||
|
return videoDurationText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
16
AsyncDisplayKit/Private/ASDefaultPlaybackButton.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// ASDefaultPlaybackButton.h
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/14/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
typedef enum {
|
||||||
|
ASDefaultPlaybackButtonTypePlay,
|
||||||
|
ASDefaultPlaybackButtonTypePause
|
||||||
|
} ASDefaultPlaybackButtonType;
|
||||||
|
@interface ASDefaultPlaybackButton : ASControlNode
|
||||||
|
@property (nonatomic, assign) ASDefaultPlaybackButtonType buttonType;
|
||||||
|
@end
|
||||||
81
AsyncDisplayKit/Private/ASDefaultPlaybackButton.m
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// ASDefaultPlaybackButton.m
|
||||||
|
// AsyncDisplayKit
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/14/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ASDefaultPlaybackButton.h"
|
||||||
|
@interface ASDefaultPlaybackButton()
|
||||||
|
{
|
||||||
|
ASDefaultPlaybackButtonType _buttonType;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ASDefaultPlaybackButton
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if (!(self = [super init])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.opaque = NO;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setButtonType:(ASDefaultPlaybackButtonType)buttonType
|
||||||
|
{
|
||||||
|
ASDefaultPlaybackButtonType oldType = _buttonType;
|
||||||
|
_buttonType = buttonType;
|
||||||
|
|
||||||
|
if (oldType != _buttonType) {
|
||||||
|
[self setNeedsDisplay];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable id<NSObject>)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
@"buttonType" : [NSNumber numberWithInt:_buttonType],
|
||||||
|
@"color" : self.tintColor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)drawRect:(CGRect)bounds withParameters:(id<NSObject>)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing
|
||||||
|
{
|
||||||
|
ASDefaultPlaybackButtonType buttonType = [parameters[@"buttonType"] intValue];
|
||||||
|
UIColor *color = parameters[@"color"];
|
||||||
|
|
||||||
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
|
||||||
|
if (buttonType == ASDefaultPlaybackButtonTypePlay) {
|
||||||
|
[bezierPath moveToPoint: CGPointMake(0, 0)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(0, bounds.size.height)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(bounds.size.width, bounds.size.height/2)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(0, 0)];
|
||||||
|
[bezierPath closePath];
|
||||||
|
} else if (buttonType == ASDefaultPlaybackButtonTypePause) {
|
||||||
|
CGFloat pauseSingleLineWidth = bounds.size.width / 3.0;
|
||||||
|
[bezierPath moveToPoint: CGPointMake(0, bounds.size.height)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth, bounds.size.height)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth, 0)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(0, 0)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(0, bounds.size.height)];
|
||||||
|
[bezierPath closePath];
|
||||||
|
[bezierPath moveToPoint: CGPointMake(pauseSingleLineWidth * 2, 0)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth * 2, bounds.size.height)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(bounds.size.width, bounds.size.height)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(bounds.size.width, 0)];
|
||||||
|
[bezierPath addLineToPoint: CGPointMake(pauseSingleLineWidth * 2, 0)];
|
||||||
|
[bezierPath closePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
[color setFill];
|
||||||
|
[bezierPath fill];
|
||||||
|
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
}
|
||||||
|
@end
|
||||||
@ -50,13 +50,6 @@
|
|||||||
_requestedKeys = @[ @"playable" ];
|
_requestedKeys = @[ @"playable" ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testSpinnerDefaultsToNil
|
|
||||||
{
|
|
||||||
XCTAssertNil(_videoNode.spinner);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)testOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnode
|
- (void)testOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnode
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
@ -73,8 +66,6 @@
|
|||||||
{
|
{
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
|
|
||||||
XCTAssertNotNil(_videoNode.spinner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -97,7 +88,6 @@
|
|||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
[_videoNode pause];
|
[_videoNode pause];
|
||||||
|
|
||||||
XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -119,8 +109,6 @@
|
|||||||
|
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
[_videoNode observeValueForKeyPath:@"status" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @(AVPlayerItemStatusReadyToPlay)} context:NULL];
|
[_videoNode observeValueForKeyPath:@"status" ofObject:[_videoNode currentItem] change:@{NSKeyValueChangeNewKey : @(AVPlayerItemStatusReadyToPlay)} context:NULL];
|
||||||
|
|
||||||
XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -379,21 +367,6 @@
|
|||||||
XCTAssertEqual(UIViewContentModeScaleAspectFill, _videoNode.placeholderImageNode.contentMode);
|
XCTAssertEqual(UIViewContentModeScaleAspectFill, _videoNode.placeholderImageNode.contentMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testChangingPlayButtonPerformsProperCleanup
|
|
||||||
{
|
|
||||||
ASButtonNode *firstButton = _videoNode.playButton;
|
|
||||||
XCTAssertTrue([firstButton.allTargets containsObject:_videoNode]);
|
|
||||||
|
|
||||||
ASButtonNode *secondButton = [[ASButtonNode alloc] init];
|
|
||||||
_videoNode.playButton = secondButton;
|
|
||||||
|
|
||||||
XCTAssertTrue([secondButton.allTargets containsObject:_videoNode]);
|
|
||||||
XCTAssertEqual(_videoNode, secondButton.supernode);
|
|
||||||
|
|
||||||
XCTAssertFalse([firstButton.allTargets containsObject:_videoNode]);
|
|
||||||
XCTAssertNotEqual(_videoNode, firstButton.supernode);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testChangingAssetsChangesPlaceholderImage
|
- (void)testChangingAssetsChangesPlaceholderImage
|
||||||
{
|
{
|
||||||
UIImage *firstImage = [[UIImage alloc] init];
|
UIImage *firstImage = [[UIImage alloc] init];
|
||||||
|
|||||||
BIN
examples/ASDKTube/Default-568h@2x.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
examples/ASDKTube/Default-667h@2x.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
examples/ASDKTube/Default-736h@3x.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
5
examples/ASDKTube/Podfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
source 'https://github.com/CocoaPods/Specs.git'
|
||||||
|
platform :ios, '7.0'
|
||||||
|
target 'Sample' do
|
||||||
|
pod 'AsyncDisplayKit', :path => '../..'
|
||||||
|
end
|
||||||
447
examples/ASDKTube/Sample.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
// !$*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 */; };
|
||||||
|
5791C5525B690FA54F26ACE8 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2092CAF5607B3863A3700A2 /* libPods-Sample.a */; };
|
||||||
|
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 */; };
|
||||||
|
8B0768B81CE7AD03002E1453 /* VideoModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768B71CE7AD03002E1453 /* VideoModel.m */; };
|
||||||
|
8B0768BC1CE7B091002E1453 /* VideoContentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768BB1CE7B091002E1453 /* VideoContentCell.m */; };
|
||||||
|
8B0768BF1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768BE1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m */; };
|
||||||
|
8B0768C51CE7C707002E1453 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768C41CE7C707002E1453 /* Utilities.m */; };
|
||||||
|
8B0768C91CE7C889002E1453 /* VideoFeedNodeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B0768C81CE7C889002E1453 /* VideoFeedNodeController.m */; };
|
||||||
|
8B9075851CF386A400F924C1 /* ico-unmute.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B90757F1CF386A400F924C1 /* ico-unmute.png */; };
|
||||||
|
8B9075861CF386A400F924C1 /* ico-unmute@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075801CF386A400F924C1 /* ico-unmute@2x.png */; };
|
||||||
|
8B9075871CF386A400F924C1 /* ico-unmute@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075811CF386A400F924C1 /* ico-unmute@3x.png */; };
|
||||||
|
8B9075881CF386A400F924C1 /* ico-mute.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075821CF386A400F924C1 /* ico-mute.png */; };
|
||||||
|
8B9075891CF386A400F924C1 /* ico-mute@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075831CF386A400F924C1 /* ico-mute@2x.png */; };
|
||||||
|
8B90758A1CF386A400F924C1 /* ico-mute@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8B9075841CF386A400F924C1 /* ico-mute@3x.png */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
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; };
|
||||||
|
8B0768B61CE7AD03002E1453 /* VideoModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoModel.h; sourceTree = "<group>"; };
|
||||||
|
8B0768B71CE7AD03002E1453 /* VideoModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoModel.m; sourceTree = "<group>"; };
|
||||||
|
8B0768BA1CE7B091002E1453 /* VideoContentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoContentCell.h; sourceTree = "<group>"; };
|
||||||
|
8B0768BB1CE7B091002E1453 /* VideoContentCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoContentCell.m; sourceTree = "<group>"; };
|
||||||
|
8B0768BD1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowWithStatusBarUnderlay.h; sourceTree = "<group>"; };
|
||||||
|
8B0768BE1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WindowWithStatusBarUnderlay.m; sourceTree = "<group>"; };
|
||||||
|
8B0768C31CE7C707002E1453 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
|
||||||
|
8B0768C41CE7C707002E1453 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
|
||||||
|
8B0768C71CE7C889002E1453 /* VideoFeedNodeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoFeedNodeController.h; sourceTree = "<group>"; };
|
||||||
|
8B0768C81CE7C889002E1453 /* VideoFeedNodeController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VideoFeedNodeController.m; sourceTree = "<group>"; };
|
||||||
|
8B90757F1CF386A400F924C1 /* ico-unmute.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-unmute.png"; sourceTree = "<group>"; };
|
||||||
|
8B9075801CF386A400F924C1 /* ico-unmute@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-unmute@2x.png"; sourceTree = "<group>"; };
|
||||||
|
8B9075811CF386A400F924C1 /* ico-unmute@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-unmute@3x.png"; sourceTree = "<group>"; };
|
||||||
|
8B9075821CF386A400F924C1 /* ico-mute.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-mute.png"; sourceTree = "<group>"; };
|
||||||
|
8B9075831CF386A400F924C1 /* ico-mute@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-mute@2x.png"; sourceTree = "<group>"; };
|
||||||
|
8B9075841CF386A400F924C1 /* ico-mute@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ico-mute@3x.png"; sourceTree = "<group>"; };
|
||||||
|
A2092CAF5607B3863A3700A2 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
CFD6AA1D30516C27DEE5602B /* 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>"; };
|
||||||
|
E51646FF8D3676A1D826A5AE /* 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>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
05E2127E19D4DB510098F589 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
5791C5525B690FA54F26ACE8 /* 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 = (
|
||||||
|
8B90757E1CF3869100F924C1 /* Icons */,
|
||||||
|
8B0768C61CE7C85F002E1453 /* Controller */,
|
||||||
|
8B0768B91CE7B07E002E1453 /* Nodes */,
|
||||||
|
8B0768B51CE7ACE8002E1453 /* Models */,
|
||||||
|
05E2128819D4DB510098F589 /* AppDelegate.h */,
|
||||||
|
05E2128919D4DB510098F589 /* AppDelegate.m */,
|
||||||
|
05E2128B19D4DB510098F589 /* ViewController.h */,
|
||||||
|
05E2128C19D4DB510098F589 /* ViewController.m */,
|
||||||
|
05E2128419D4DB510098F589 /* Supporting Files */,
|
||||||
|
8B0768BD1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.h */,
|
||||||
|
8B0768BE1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m */,
|
||||||
|
);
|
||||||
|
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 = (
|
||||||
|
A2092CAF5607B3863A3700A2 /* libPods-Sample.a */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
1AE410B73DA5C3BD087ACDD7 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
CFD6AA1D30516C27DEE5602B /* Pods-Sample.debug.xcconfig */,
|
||||||
|
E51646FF8D3676A1D826A5AE /* Pods-Sample.release.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
8B0768B51CE7ACE8002E1453 /* Models */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8B0768C31CE7C707002E1453 /* Utilities.h */,
|
||||||
|
8B0768C41CE7C707002E1453 /* Utilities.m */,
|
||||||
|
8B0768B61CE7AD03002E1453 /* VideoModel.h */,
|
||||||
|
8B0768B71CE7AD03002E1453 /* VideoModel.m */,
|
||||||
|
);
|
||||||
|
path = Models;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
8B0768B91CE7B07E002E1453 /* Nodes */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8B0768BA1CE7B091002E1453 /* VideoContentCell.h */,
|
||||||
|
8B0768BB1CE7B091002E1453 /* VideoContentCell.m */,
|
||||||
|
);
|
||||||
|
path = Nodes;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
8B0768C61CE7C85F002E1453 /* Controller */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8B0768C71CE7C889002E1453 /* VideoFeedNodeController.h */,
|
||||||
|
8B0768C81CE7C889002E1453 /* VideoFeedNodeController.m */,
|
||||||
|
);
|
||||||
|
path = Controller;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
8B90757E1CF3869100F924C1 /* Icons */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8B90757F1CF386A400F924C1 /* ico-unmute.png */,
|
||||||
|
8B9075801CF386A400F924C1 /* ico-unmute@2x.png */,
|
||||||
|
8B9075811CF386A400F924C1 /* ico-unmute@3x.png */,
|
||||||
|
8B9075821CF386A400F924C1 /* ico-mute.png */,
|
||||||
|
8B9075831CF386A400F924C1 /* ico-mute@2x.png */,
|
||||||
|
8B9075841CF386A400F924C1 /* ico-mute@3x.png */,
|
||||||
|
);
|
||||||
|
path = Icons;
|
||||||
|
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 */,
|
||||||
|
93B7780A33739EF25F20366B /* 📦 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 = (
|
||||||
|
8B9075861CF386A400F924C1 /* ico-unmute@2x.png in Resources */,
|
||||||
|
8B9075881CF386A400F924C1 /* ico-mute.png in Resources */,
|
||||||
|
0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */,
|
||||||
|
6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */,
|
||||||
|
6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */,
|
||||||
|
8B90758A1CF386A400F924C1 /* ico-mute@3x.png in Resources */,
|
||||||
|
8B9075851CF386A400F924C1 /* ico-unmute.png in Resources */,
|
||||||
|
8B9075871CF386A400F924C1 /* ico-unmute@3x.png in Resources */,
|
||||||
|
8B9075891CF386A400F924C1 /* ico-mute@2x.png in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
93B7780A33739EF25F20366B /* 📦 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;
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
05E2127D19D4DB510098F589 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
|
||||||
|
8B0768C91CE7C889002E1453 /* VideoFeedNodeController.m in Sources */,
|
||||||
|
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
|
||||||
|
8B0768BC1CE7B091002E1453 /* VideoContentCell.m in Sources */,
|
||||||
|
8B0768BF1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m in Sources */,
|
||||||
|
05E2128719D4DB510098F589 /* main.m in Sources */,
|
||||||
|
8B0768C51CE7C707002E1453 /* Utilities.m in Sources */,
|
||||||
|
8B0768B81CE7AD03002E1453 /* VideoModel.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 = 8.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 = 8.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
05E212A519D4DB510098F589 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = CFD6AA1D30516C27DEE5602B /* 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 = E51646FF8D3676A1D826A5AE /* 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 */;
|
||||||
|
}
|
||||||
7
examples/ASDKTube/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:Sample.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@ -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>
|
||||||
10
examples/ASDKTube/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal 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>
|
||||||
18
examples/ASDKTube/Sample/AppDelegate.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||||
|
* purposes only. Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
|
|
||||||
|
@property (strong, nonatomic) UIWindow *window;
|
||||||
|
|
||||||
|
@end
|
||||||
45
examples/ASDKTube/Sample/AppDelegate.m
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||||
|
* purposes only. Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
#import "WindowWithStatusBarUnderlay.h"
|
||||||
|
#import "Utilities.h"
|
||||||
|
#import "VideoFeedNodeController.h"
|
||||||
|
|
||||||
|
|
||||||
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
|
|
||||||
|
// this UIWindow subclass is neccessary to make the status bar opaque
|
||||||
|
_window = [[WindowWithStatusBarUnderlay alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||||
|
_window.backgroundColor = [UIColor whiteColor];
|
||||||
|
|
||||||
|
|
||||||
|
VideoFeedNodeController *asdkHomeFeedVC = [[VideoFeedNodeController alloc] init];
|
||||||
|
UINavigationController *asdkHomeFeedNavCtrl = [[UINavigationController alloc] initWithRootViewController:asdkHomeFeedVC];
|
||||||
|
|
||||||
|
|
||||||
|
_window.rootViewController = asdkHomeFeedNavCtrl;
|
||||||
|
[_window makeKeyAndVisible];
|
||||||
|
|
||||||
|
// Nav Bar appearance
|
||||||
|
NSDictionary *attributes = @{NSForegroundColorAttributeName:[UIColor whiteColor]};
|
||||||
|
[[UINavigationBar appearance] setTitleTextAttributes:attributes];
|
||||||
|
[[UINavigationBar appearance] setBarTintColor:[UIColor lighOrangeColor]];
|
||||||
|
[[UINavigationBar appearance] setTranslucent:NO];
|
||||||
|
|
||||||
|
[application setStatusBarStyle:UIStatusBarStyleLightContent];
|
||||||
|
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@end
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// VideoFeedNodeController.h
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/15/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
|
||||||
|
@interface VideoFeedNodeController : ASViewController
|
||||||
|
|
||||||
|
@end
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// VideoFeedNodeController.m
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/15/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "VideoFeedNodeController.h"
|
||||||
|
#import <AsyncDisplayKit/ASVideoPlayerNode.h>
|
||||||
|
#import "VideoModel.h"
|
||||||
|
#import "VideoContentCell.h"
|
||||||
|
|
||||||
|
@interface VideoFeedNodeController ()<ASTableDelegate, ASTableDataSource>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation VideoFeedNodeController
|
||||||
|
{
|
||||||
|
ASTableNode *_tableNode;
|
||||||
|
NSMutableArray<VideoModel*> *_videoFeedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
self.navigationItem.title = @"Home";
|
||||||
|
_tableNode = [[ASTableNode alloc] init];
|
||||||
|
_tableNode.delegate = self;
|
||||||
|
_tableNode.dataSource = self;
|
||||||
|
|
||||||
|
if (!(self = [super initWithNode:_tableNode])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadView
|
||||||
|
{
|
||||||
|
[super loadView];
|
||||||
|
|
||||||
|
[self generateFeedData];
|
||||||
|
|
||||||
|
[_tableNode.view reloadData];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)generateFeedData
|
||||||
|
{
|
||||||
|
_videoFeedData = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
[_videoFeedData addObject:[[VideoModel alloc] init]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - ASCollectionDelegate - ASCollectionDataSource
|
||||||
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
|
||||||
|
return _videoFeedData.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
VideoModel *videoObject = [_videoFeedData objectAtIndex:indexPath.row];
|
||||||
|
VideoContentCell *cellNode = [[VideoContentCell alloc] initWithVideoObject:videoObject];
|
||||||
|
return cellNode;
|
||||||
|
}
|
||||||
|
@end
|
||||||
BIN
examples/ASDKTube/Sample/Icons/ico-mute.png
Normal file
|
After Width: | Height: | Size: 483 B |
BIN
examples/ASDKTube/Sample/Icons/ico-mute@2x.png
Normal file
|
After Width: | Height: | Size: 801 B |
BIN
examples/ASDKTube/Sample/Icons/ico-mute@3x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
examples/ASDKTube/Sample/Icons/ico-unmute.png
Normal file
|
After Width: | Height: | Size: 578 B |
BIN
examples/ASDKTube/Sample/Icons/ico-unmute@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
examples/ASDKTube/Sample/Icons/ico-unmute@3x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
38
examples/ASDKTube/Sample/Info.plist
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?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>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
|
<false/>
|
||||||
|
<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>UIRequiredDeviceCapabilities</key>
|
||||||
|
<array>
|
||||||
|
<string>armv7</string>
|
||||||
|
</array>
|
||||||
|
<key>UIStatusBarStyle</key>
|
||||||
|
<string>UIStatusBarStyleLightContent</string>
|
||||||
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
|
<array>
|
||||||
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
40
examples/ASDKTube/Sample/Models/Utilities.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// Utilities.h
|
||||||
|
// ASDKgram
|
||||||
|
//
|
||||||
|
// Created by Hannah Troisi on 3/9/16.
|
||||||
|
// Copyright © 2016 Hannah Troisi. All rights reserved.
|
||||||
|
//
|
||||||
|
#include <UIKit/UIKit.h>
|
||||||
|
@interface UIColor (Additions)
|
||||||
|
|
||||||
|
+ (UIColor *)lighOrangeColor;
|
||||||
|
+ (UIColor *)darkBlueColor;
|
||||||
|
+ (UIColor *)lightBlueColor;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface UIImage (Additions)
|
||||||
|
|
||||||
|
+ (UIImage *)followingButtonStretchableImageForCornerRadius:(CGFloat)cornerRadius following:(BOOL)followingEnabled;
|
||||||
|
+ (void)downloadImageForURL:(NSURL *)url completion:(void (^)(UIImage *))block;
|
||||||
|
|
||||||
|
- (UIImage *)makeCircularImageWithSize:(CGSize)size;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSString (Additions)
|
||||||
|
|
||||||
|
// returns a user friendly elapsed time such as '50s', '6m' or '3w'
|
||||||
|
+ (NSString *)elapsedTimeStringSinceDate:(NSString *)uploadDateString;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSAttributedString (Additions)
|
||||||
|
|
||||||
|
+ (NSAttributedString *)attributedStringWithString:(NSString *)string
|
||||||
|
fontSize:(CGFloat)size
|
||||||
|
color:(UIColor *)color
|
||||||
|
firstWordColor:(UIColor *)firstWordColor;
|
||||||
|
|
||||||
|
@end
|
||||||
230
examples/ASDKTube/Sample/Models/Utilities.m
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
//
|
||||||
|
// Utilities.m
|
||||||
|
// ASDKgram
|
||||||
|
//
|
||||||
|
// Created by Hannah Troisi on 3/9/16.
|
||||||
|
// Copyright © 2016 Hannah Troisi. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "Utilities.h"
|
||||||
|
|
||||||
|
#define StrokeRoundedImages 0
|
||||||
|
|
||||||
|
@implementation UIColor (Additions)
|
||||||
|
|
||||||
|
+ (UIColor *)lighOrangeColor
|
||||||
|
{
|
||||||
|
return [UIColor colorWithRed:1 green:0.506 blue:0.384 alpha:1];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (UIColor *)darkBlueColor
|
||||||
|
{
|
||||||
|
return [UIColor colorWithRed:70.0/255.0 green:102.0/255.0 blue:118.0/255.0 alpha:1.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (UIColor *)lightBlueColor
|
||||||
|
{
|
||||||
|
return [UIColor colorWithRed:70.0/255.0 green:165.0/255.0 blue:196.0/255.0 alpha:1.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation UIImage (Additions)
|
||||||
|
|
||||||
|
+ (UIImage *)followingButtonStretchableImageForCornerRadius:(CGFloat)cornerRadius following:(BOOL)followingEnabled
|
||||||
|
{
|
||||||
|
CGSize unstretchedSize = CGSizeMake(2 * cornerRadius + 1, 2 * cornerRadius + 1);
|
||||||
|
CGRect rect = (CGRect) {CGPointZero, unstretchedSize};
|
||||||
|
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];
|
||||||
|
|
||||||
|
// create a graphics context for the following status button
|
||||||
|
UIGraphicsBeginImageContextWithOptions(unstretchedSize, NO, 0);
|
||||||
|
|
||||||
|
[path addClip];
|
||||||
|
|
||||||
|
if (followingEnabled) {
|
||||||
|
|
||||||
|
[[UIColor whiteColor] setFill];
|
||||||
|
[path fill];
|
||||||
|
|
||||||
|
path.lineWidth = 3;
|
||||||
|
[[UIColor lightBlueColor] setStroke];
|
||||||
|
[path stroke];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
[[UIColor lightBlueColor] setFill];
|
||||||
|
[path fill];
|
||||||
|
}
|
||||||
|
|
||||||
|
UIImage *followingBtnImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
UIImage *followingBtnImageStretchable = [followingBtnImage stretchableImageWithLeftCapWidth:cornerRadius
|
||||||
|
topCapHeight:cornerRadius];
|
||||||
|
return followingBtnImageStretchable;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)downloadImageForURL:(NSURL *)url completion:(void (^)(UIImage *))block
|
||||||
|
{
|
||||||
|
static NSCache *simpleImageCache = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
simpleImageCache = [[NSCache alloc] init];
|
||||||
|
simpleImageCache.countLimit = 10;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if image is cached
|
||||||
|
UIImage *image = [simpleImageCache objectForKey:url];
|
||||||
|
if (image) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
block(image);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// else download image
|
||||||
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
|
||||||
|
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||||
|
if (data) {
|
||||||
|
UIImage *image = [UIImage imageWithData:data];
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
block(image);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
[task resume];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)makeCircularImageWithSize:(CGSize)size
|
||||||
|
{
|
||||||
|
// make a CGRect with the image's size
|
||||||
|
CGRect circleRect = (CGRect) {CGPointZero, size};
|
||||||
|
|
||||||
|
// begin the image context since we're not in a drawRect:
|
||||||
|
UIGraphicsBeginImageContextWithOptions(circleRect.size, NO, 0);
|
||||||
|
|
||||||
|
// create a UIBezierPath circle
|
||||||
|
UIBezierPath *circle = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:circleRect.size.width/2];
|
||||||
|
|
||||||
|
// clip to the circle
|
||||||
|
[circle addClip];
|
||||||
|
|
||||||
|
// draw the image in the circleRect *AFTER* the context is clipped
|
||||||
|
[self drawInRect:circleRect];
|
||||||
|
|
||||||
|
// create a border (for white background pictures)
|
||||||
|
#if StrokeRoundedImages
|
||||||
|
circle.lineWidth = 1;
|
||||||
|
[[UIColor darkGrayColor] set];
|
||||||
|
[circle stroke];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// get an image from the image context
|
||||||
|
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||||
|
|
||||||
|
// end the image context since we're not in a drawRect:
|
||||||
|
UIGraphicsEndImageContext();
|
||||||
|
|
||||||
|
return roundedImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSString (Additions)
|
||||||
|
|
||||||
|
// Returns a user-visible date time string that corresponds to the
|
||||||
|
// specified RFC 3339 date time string. Note that this does not handle
|
||||||
|
// all possible RFC 3339 date time strings, just one of the most common
|
||||||
|
// styles.
|
||||||
|
+ (NSDate *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString
|
||||||
|
{
|
||||||
|
NSDateFormatter * rfc3339DateFormatter;
|
||||||
|
NSLocale * enUSPOSIXLocale;
|
||||||
|
|
||||||
|
// Convert the RFC 3339 date time string to an NSDate.
|
||||||
|
|
||||||
|
rfc3339DateFormatter = [[NSDateFormatter alloc] init];
|
||||||
|
|
||||||
|
enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
||||||
|
|
||||||
|
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
|
||||||
|
[rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ'"];
|
||||||
|
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
|
||||||
|
|
||||||
|
return [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)elapsedTimeStringSinceDate:(NSString *)uploadDateString
|
||||||
|
{
|
||||||
|
// early return if no post date string
|
||||||
|
if (!uploadDateString)
|
||||||
|
{
|
||||||
|
return @"NO POST DATE";
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDate *postDate = [self userVisibleDateTimeStringForRFC3339DateTimeString:uploadDateString];
|
||||||
|
|
||||||
|
if (!postDate) {
|
||||||
|
return @"DATE CONVERSION ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDate *currentDate = [NSDate date];
|
||||||
|
|
||||||
|
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||||
|
|
||||||
|
NSUInteger seconds = [[calendar components:NSCalendarUnitSecond fromDate:postDate toDate:currentDate options:0] second];
|
||||||
|
NSUInteger minutes = [[calendar components:NSCalendarUnitMinute fromDate:postDate toDate:currentDate options:0] minute];
|
||||||
|
NSUInteger hours = [[calendar components:NSCalendarUnitHour fromDate:postDate toDate:currentDate options:0] hour];
|
||||||
|
NSUInteger days = [[calendar components:NSCalendarUnitDay fromDate:postDate toDate:currentDate options:0] day];
|
||||||
|
|
||||||
|
NSString *elapsedTime;
|
||||||
|
|
||||||
|
if (days > 7) {
|
||||||
|
elapsedTime = [NSString stringWithFormat:@"%luw", (long)ceil(days/7.0)];
|
||||||
|
} else if (days > 0) {
|
||||||
|
elapsedTime = [NSString stringWithFormat:@"%lud", (long)days];
|
||||||
|
} else if (hours > 0) {
|
||||||
|
elapsedTime = [NSString stringWithFormat:@"%luh", (long)hours];
|
||||||
|
} else if (minutes > 0) {
|
||||||
|
elapsedTime = [NSString stringWithFormat:@"%lum", (long)minutes];
|
||||||
|
} else if (seconds > 0) {
|
||||||
|
elapsedTime = [NSString stringWithFormat:@"%lus", (long)seconds];
|
||||||
|
} else if (seconds == 0) {
|
||||||
|
elapsedTime = @"1s";
|
||||||
|
} else {
|
||||||
|
elapsedTime = @"ERROR";
|
||||||
|
}
|
||||||
|
|
||||||
|
return elapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSAttributedString (Additions)
|
||||||
|
|
||||||
|
+ (NSAttributedString *)attributedStringWithString:(NSString *)string fontSize:(CGFloat)size
|
||||||
|
color:(nullable UIColor *)color firstWordColor:(nullable UIColor *)firstWordColor
|
||||||
|
{
|
||||||
|
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
|
||||||
|
|
||||||
|
if (string) {
|
||||||
|
NSDictionary *attributes = @{NSForegroundColorAttributeName: color ? : [UIColor blackColor],
|
||||||
|
NSFontAttributeName: [UIFont systemFontOfSize:size]};
|
||||||
|
attributedString = [[NSMutableAttributedString alloc] initWithString:string];
|
||||||
|
[attributedString addAttributes:attributes range:NSMakeRange(0, string.length)];
|
||||||
|
|
||||||
|
if (firstWordColor) {
|
||||||
|
NSRange firstSpaceRange = [string rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
NSRange firstWordRange = NSMakeRange(0, firstSpaceRange.location);
|
||||||
|
[attributedString addAttribute:NSForegroundColorAttributeName value:firstWordColor range:firstWordRange];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
16
examples/ASDKTube/Sample/Models/VideoModel.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// VideoModel.h
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/14/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface VideoModel : NSObject
|
||||||
|
@property (nonatomic, strong, readonly) NSString* title;
|
||||||
|
@property (nonatomic, strong, readonly) NSURL *url;
|
||||||
|
@property (nonatomic, strong, readonly) NSString *userName;
|
||||||
|
@property (nonatomic, strong, readonly) NSURL *avatarUrl;
|
||||||
|
@end
|
||||||
27
examples/ASDKTube/Sample/Models/VideoModel.m
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// VideoModel.m
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/14/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "VideoModel.h"
|
||||||
|
|
||||||
|
@implementation VideoModel
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
NSString *videoUrlString = @"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-3045b261-7e93-4492-b7e5-5d6358376c9f-editedLiveAndDie.mov";
|
||||||
|
NSString *avatarUrlString = [NSString stringWithFormat:@"https://api.adorable.io/avatars/50/%@",[[NSProcessInfo processInfo] globallyUniqueString]];
|
||||||
|
|
||||||
|
_title = @"Demo title";
|
||||||
|
_url = [NSURL URLWithString:videoUrlString];
|
||||||
|
_userName = @"Random User";
|
||||||
|
_avatarUrl = [NSURL URLWithString:avatarUrlString];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
@end
|
||||||
14
examples/ASDKTube/Sample/Nodes/VideoContentCell.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// VideoContentCell.h
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/14/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
#import "VideoModel.h"
|
||||||
|
|
||||||
|
@interface VideoContentCell : ASCellNode
|
||||||
|
- (instancetype)initWithVideoObject:(VideoModel *)video;
|
||||||
|
@end
|
||||||
217
examples/ASDKTube/Sample/Nodes/VideoContentCell.m
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
//
|
||||||
|
// VideoContentCell.m
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/14/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "VideoContentCell.h"
|
||||||
|
#import "ASVideoPlayerNode.h"
|
||||||
|
#import "Utilities.h"
|
||||||
|
|
||||||
|
#define AVATAR_IMAGE_HEIGHT 30
|
||||||
|
#define HORIZONTAL_BUFFER 10
|
||||||
|
#define VERTICAL_BUFFER 5
|
||||||
|
|
||||||
|
@interface VideoContentCell () <ASVideoPlayerNodeDelegate>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation VideoContentCell
|
||||||
|
{
|
||||||
|
VideoModel *_videoModel;
|
||||||
|
ASTextNode *_titleNode;
|
||||||
|
ASNetworkImageNode *_avatarNode;
|
||||||
|
ASVideoPlayerNode *_videoPlayerNode;
|
||||||
|
ASControlNode *_likeButtonNode;
|
||||||
|
ASButtonNode *_muteButtonNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithVideoObject:(VideoModel *)video
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_videoModel = video;
|
||||||
|
|
||||||
|
_titleNode = [[ASTextNode alloc] init];
|
||||||
|
_titleNode.attributedText = [[NSAttributedString alloc] initWithString:_videoModel.title attributes:[self titleNodeStringOptions]];
|
||||||
|
_titleNode.flexGrow = YES;
|
||||||
|
[self addSubnode:_titleNode];
|
||||||
|
|
||||||
|
_avatarNode = [[ASNetworkImageNode alloc] init];
|
||||||
|
_avatarNode.URL = _videoModel.avatarUrl;
|
||||||
|
|
||||||
|
[_avatarNode setImageModificationBlock:^UIImage *(UIImage *image) {
|
||||||
|
CGSize profileImageSize = CGSizeMake(AVATAR_IMAGE_HEIGHT, AVATAR_IMAGE_HEIGHT);
|
||||||
|
return [image makeCircularImageWithSize:profileImageSize];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self addSubnode:_avatarNode];
|
||||||
|
|
||||||
|
_likeButtonNode = [[ASControlNode alloc] init];
|
||||||
|
_likeButtonNode.backgroundColor = [UIColor redColor];
|
||||||
|
[self addSubnode:_likeButtonNode];
|
||||||
|
|
||||||
|
_muteButtonNode = [[ASButtonNode alloc] init];
|
||||||
|
_muteButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
|
||||||
|
[_muteButtonNode addTarget:self action:@selector(didTapMuteButton) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||||
|
|
||||||
|
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url loadAssetWhenNodeBecomesVisible:YES];
|
||||||
|
_videoPlayerNode.delegate = self;
|
||||||
|
_videoPlayerNode.backgroundColor = [UIColor blackColor];
|
||||||
|
[self addSubnode:_videoPlayerNode];
|
||||||
|
|
||||||
|
[self setMuteButtonIcon];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary*)titleNodeStringOptions
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
NSFontAttributeName : [UIFont systemFontOfSize:14.0],
|
||||||
|
NSForegroundColorAttributeName: [UIColor blackColor]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||||
|
{
|
||||||
|
CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;
|
||||||
|
_videoPlayerNode.preferredFrameSize = CGSizeMake(fullWidth, fullWidth * 9 / 16);
|
||||||
|
_avatarNode.preferredFrameSize = CGSizeMake(AVATAR_IMAGE_HEIGHT, AVATAR_IMAGE_HEIGHT);
|
||||||
|
_likeButtonNode.preferredFrameSize = CGSizeMake(50.0, 26.0);
|
||||||
|
|
||||||
|
ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||||
|
headerStack.spacing = HORIZONTAL_BUFFER;
|
||||||
|
headerStack.alignItems = ASStackLayoutAlignItemsCenter;
|
||||||
|
[headerStack setChildren:@[ _avatarNode, _titleNode]];
|
||||||
|
|
||||||
|
UIEdgeInsets headerInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
|
||||||
|
ASInsetLayoutSpec *headerInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:headerInsets child:headerStack];
|
||||||
|
|
||||||
|
ASStackLayoutSpec *bottomControlsStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||||
|
bottomControlsStack.spacing = HORIZONTAL_BUFFER;
|
||||||
|
bottomControlsStack.alignItems = ASStackLayoutAlignItemsCenter;
|
||||||
|
[bottomControlsStack setChildren:@[ _likeButtonNode]];
|
||||||
|
|
||||||
|
UIEdgeInsets bottomControlsInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
|
||||||
|
ASInsetLayoutSpec *bottomControlsInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:bottomControlsInsets child:bottomControlsStack];
|
||||||
|
|
||||||
|
|
||||||
|
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||||
|
verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
|
||||||
|
[verticalStack setChildren:@[ headerInset, _videoPlayerNode, bottomControlsInset ]];
|
||||||
|
return verticalStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMuteButtonIcon
|
||||||
|
{
|
||||||
|
if (_videoPlayerNode.muted) {
|
||||||
|
[_muteButtonNode setImage:[UIImage imageNamed:@"ico-mute"] forState:ASControlStateNormal];
|
||||||
|
} else {
|
||||||
|
[_muteButtonNode setImage:[UIImage imageNamed:@"ico-unmute"] forState:ASControlStateNormal];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didTapMuteButton
|
||||||
|
{
|
||||||
|
_videoPlayerNode.muted = !_videoPlayerNode.muted;
|
||||||
|
[self setMuteButtonIcon];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - ASVideoPlayerNodeDelegate
|
||||||
|
- (void)didTapVideoPlayerNode:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
{
|
||||||
|
if (_videoPlayerNode.playerState == ASVideoNodePlayerStatePlaying) {
|
||||||
|
NSLog(@"TRANSITION");
|
||||||
|
_videoPlayerNode.controlsDisabled = !_videoPlayerNode.controlsDisabled;
|
||||||
|
[_videoPlayerNode pause];
|
||||||
|
} else {
|
||||||
|
[_videoPlayerNode play];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)videoPlayerNodeCustomControls:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
@"muteControl" : _muteButtonNode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)controlsForControlBar:(NSDictionary *)availableControls
|
||||||
|
{
|
||||||
|
NSMutableArray *controls = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
if (availableControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]) {
|
||||||
|
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypePlaybackButton) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]) {
|
||||||
|
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeElapsedText) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
|
||||||
|
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeScrubber) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]) {
|
||||||
|
[controls addObject:availableControls[ @(ASVideoPlayerNodeControlTypeDurationText) ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return controls;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Layout
|
||||||
|
- (ASLayoutSpec*)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize
|
||||||
|
{
|
||||||
|
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||||
|
spacer.flexGrow = YES;
|
||||||
|
|
||||||
|
UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
|
||||||
|
|
||||||
|
if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
|
||||||
|
ASDisplayNode *scrubber = controls[ @(ASVideoPlayerNodeControlTypeScrubber) ];
|
||||||
|
scrubber.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray *controlBarControls = [self controlsForControlBar:controls];
|
||||||
|
NSMutableArray *topBarControls = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
//Our custom control
|
||||||
|
if (controls[@"muteControl"]) {
|
||||||
|
[topBarControls addObject:controls[@"muteControl"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ASStackLayoutSpec *topBarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||||
|
spacing:10.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsCenter
|
||||||
|
children:topBarControls];
|
||||||
|
|
||||||
|
ASInsetLayoutSpec *topBarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:topBarSpec];
|
||||||
|
|
||||||
|
ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||||
|
spacing:10.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsCenter
|
||||||
|
children: controlBarControls ];
|
||||||
|
controlbarSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ASInsetLayoutSpec *controlbarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:controlbarSpec];
|
||||||
|
|
||||||
|
controlbarInsetSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
ASStackLayoutSpec *mainVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||||
|
spacing:0.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsStart
|
||||||
|
children:@[topBarInsetSpec, spacer, controlbarInsetSpec]];
|
||||||
|
|
||||||
|
return mainVerticalStack;
|
||||||
|
|
||||||
|
}
|
||||||
|
@end
|
||||||
16
examples/ASDKTube/Sample/ViewController.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||||
|
* purposes only. Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
|
||||||
|
@interface ViewController : ASViewController
|
||||||
|
|
||||||
|
@end
|
||||||
213
examples/ASDKTube/Sample/ViewController.m
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||||
|
* purposes only. Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "ViewController.h"
|
||||||
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
|
#import <AsyncDisplayKit/ASVideoPlayerNode.h>
|
||||||
|
#import "VideoModel.h"
|
||||||
|
#import "VideoContentCell.h"
|
||||||
|
|
||||||
|
@interface ViewController()<ASVideoPlayerNodeDelegate, ASTableDelegate, ASTableDataSource>
|
||||||
|
@property (nonatomic, strong) ASVideoPlayerNode *videoPlayerNode;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ViewController
|
||||||
|
{
|
||||||
|
ASTableNode *_tableNode;
|
||||||
|
NSMutableArray<VideoModel*> *_videoFeedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
_tableNode = [[ASTableNode alloc] init];
|
||||||
|
_tableNode.delegate = self;
|
||||||
|
_tableNode.dataSource = self;
|
||||||
|
|
||||||
|
if (!(self = [super initWithNode:_tableNode])) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadView
|
||||||
|
{
|
||||||
|
[super loadView];
|
||||||
|
|
||||||
|
_videoFeedData = [[NSMutableArray alloc] initWithObjects:[[VideoModel alloc] init], [[VideoModel alloc] init], nil];
|
||||||
|
|
||||||
|
[_tableNode.view reloadData];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewWillAppear:(BOOL)animated
|
||||||
|
{
|
||||||
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
|
//[self.view addSubnode:self.videoPlayerNode];
|
||||||
|
|
||||||
|
//[self.videoPlayerNode setNeedsLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - ASCollectionDelegate - ASCollectionDataSource
|
||||||
|
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
|
||||||
|
return _videoFeedData.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
VideoModel *videoObject = [_videoFeedData objectAtIndex:indexPath.row];
|
||||||
|
VideoContentCell *cellNode = [[VideoContentCell alloc] initWithVideoObject:videoObject];
|
||||||
|
return cellNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath{
|
||||||
|
// CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;
|
||||||
|
// return ASSizeRangeMake(CGSizeMake(fullWidth, 0.0), CGSizeMake(fullWidth, 400.0));
|
||||||
|
//}
|
||||||
|
|
||||||
|
- (ASVideoPlayerNode *)videoPlayerNode;
|
||||||
|
{
|
||||||
|
if (_videoPlayerNode) {
|
||||||
|
return _videoPlayerNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSURL *fileUrl = [NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-3045b261-7e93-4492-b7e5-5d6358376c9f-editedLiveAndDie.mov"];
|
||||||
|
|
||||||
|
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:fileUrl];
|
||||||
|
_videoPlayerNode.delegate = self;
|
||||||
|
// _videoPlayerNode.disableControls = YES;
|
||||||
|
//
|
||||||
|
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
// _videoPlayerNode.disableControls = NO;
|
||||||
|
// });
|
||||||
|
|
||||||
|
_videoPlayerNode.backgroundColor = [UIColor blackColor];
|
||||||
|
|
||||||
|
return _videoPlayerNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - ASVideoPlayerNodeDelegate
|
||||||
|
//- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
//{
|
||||||
|
// return @[ @(ASVideoPlayerNodeControlTypePlaybackButton),
|
||||||
|
// @(ASVideoPlayerNodeControlTypeElapsedText),
|
||||||
|
// @(ASVideoPlayerNodeControlTypeScrubber),
|
||||||
|
// @(ASVideoPlayerNodeControlTypeDurationText) ];
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//- (UIColor *)videoPlayerNodeScrubberMaximumTrackTint:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
//{
|
||||||
|
// return [UIColor colorWithRed:1 green:1 blue:1 alpha:0.3];
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//- (UIColor *)videoPlayerNodeScrubberMinimumTrackTint:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
//{
|
||||||
|
// return [UIColor whiteColor];
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//- (UIColor *)videoPlayerNodeScrubberThumbTint:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
//{
|
||||||
|
// return [UIColor whiteColor];
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//- (NSDictionary *)videoPlayerNodeTimeLabelAttributes:(ASVideoPlayerNode *)videoPlayerNode timeLabelType:(ASVideoPlayerNodeControlType)timeLabelType
|
||||||
|
//{
|
||||||
|
// NSDictionary *options;
|
||||||
|
//
|
||||||
|
// if (timeLabelType == ASVideoPlayerNodeControlTypeElapsedText) {
|
||||||
|
// options = @{
|
||||||
|
// NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Medium" size:16.0],
|
||||||
|
// NSForegroundColorAttributeName: [UIColor orangeColor]
|
||||||
|
// };
|
||||||
|
// } else if (timeLabelType == ASVideoPlayerNodeControlTypeDurationText) {
|
||||||
|
// options = @{
|
||||||
|
// NSFontAttributeName : [UIFont fontWithName:@"HelveticaNeue-Medium" size:16.0],
|
||||||
|
// NSForegroundColorAttributeName: [UIColor redColor]
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return options;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer
|
||||||
|
forControls:(NSDictionary *)controls
|
||||||
|
forConstrainedSize:(ASSizeRange)constrainedSize
|
||||||
|
{
|
||||||
|
|
||||||
|
NSMutableArray *bottomControls = [[NSMutableArray alloc] init];
|
||||||
|
NSMutableArray *topControls = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
ASDisplayNode *scrubberNode = controls[@(ASVideoPlayerNodeControlTypeScrubber)];
|
||||||
|
ASDisplayNode *playbackButtonNode = controls[@(ASVideoPlayerNodeControlTypePlaybackButton)];
|
||||||
|
ASTextNode *elapsedTexNode = controls[@(ASVideoPlayerNodeControlTypeElapsedText)];
|
||||||
|
ASTextNode *durationTexNode = controls[@(ASVideoPlayerNodeControlTypeDurationText)];
|
||||||
|
|
||||||
|
if (playbackButtonNode) {
|
||||||
|
[bottomControls addObject:playbackButtonNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrubberNode) {
|
||||||
|
scrubberNode.preferredFrameSize = CGSizeMake(constrainedSize.max.width, 44.0);
|
||||||
|
[bottomControls addObject:scrubberNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elapsedTexNode) {
|
||||||
|
[topControls addObject:elapsedTexNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (durationTexNode) {
|
||||||
|
[topControls addObject:durationTexNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||||
|
spacer.flexGrow = YES;
|
||||||
|
|
||||||
|
ASStackLayoutSpec *topBarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||||
|
spacing:10.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentCenter
|
||||||
|
alignItems:ASStackLayoutAlignItemsCenter
|
||||||
|
children:topControls];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
UIEdgeInsets topBarSpecInsets = UIEdgeInsetsMake(20.0, 10.0, 0.0, 10.0);
|
||||||
|
|
||||||
|
ASInsetLayoutSpec *topBarSpecInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:topBarSpecInsets child:topBarSpec];
|
||||||
|
topBarSpecInsetSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||||
|
spacing:10.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsCenter
|
||||||
|
children:bottomControls];
|
||||||
|
controlbarSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
UIEdgeInsets insets = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
|
||||||
|
|
||||||
|
ASInsetLayoutSpec *controlbarInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:controlbarSpec];
|
||||||
|
|
||||||
|
controlbarInsetSpec.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||||
|
|
||||||
|
ASStackLayoutSpec *mainVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||||
|
spacing:0.0
|
||||||
|
justifyContent:ASStackLayoutJustifyContentStart
|
||||||
|
alignItems:ASStackLayoutAlignItemsStart
|
||||||
|
children:@[ topBarSpecInsetSpec, spacer, controlbarInsetSpec ]];
|
||||||
|
|
||||||
|
|
||||||
|
return mainVerticalStack;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@end
|
||||||
13
examples/ASDKTube/Sample/WindowWithStatusBarUnderlay.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// WindowWithStatusBarUnderlay.h
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Hannah Troisi on 4/10/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface WindowWithStatusBarUnderlay : UIWindow
|
||||||
|
|
||||||
|
@end
|
||||||
39
examples/ASDKTube/Sample/WindowWithStatusBarUnderlay.m
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// WindowWithStatusBarUnderlay.m
|
||||||
|
// Sample
|
||||||
|
//
|
||||||
|
// Created by Erekle on 5/15/16.
|
||||||
|
// Copyright © 2016 Facebook. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "WindowWithStatusBarUnderlay.h"
|
||||||
|
#import "Utilities.h"
|
||||||
|
|
||||||
|
@implementation WindowWithStatusBarUnderlay
|
||||||
|
{
|
||||||
|
UIView *_statusBarOpaqueUnderlayView;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
_statusBarOpaqueUnderlayView = [[UIView alloc] init];
|
||||||
|
_statusBarOpaqueUnderlayView.backgroundColor = [UIColor lighOrangeColor];
|
||||||
|
[self addSubview:_statusBarOpaqueUnderlayView];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)layoutSubviews
|
||||||
|
{
|
||||||
|
[super layoutSubviews];
|
||||||
|
|
||||||
|
[self bringSubviewToFront:_statusBarOpaqueUnderlayView];
|
||||||
|
|
||||||
|
CGRect statusBarFrame = CGRectZero;
|
||||||
|
statusBarFrame.size.width = [[UIScreen mainScreen] bounds].size.width;
|
||||||
|
statusBarFrame.size.height = [[UIApplication sharedApplication] statusBarFrame].size.height;
|
||||||
|
_statusBarOpaqueUnderlayView.frame = statusBarFrame;
|
||||||
|
}
|
||||||
|
@end
|
||||||
20
examples/ASDKTube/Sample/main.m
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* This file provided by Facebook is for non-commercial testing and evaluation
|
||||||
|
* purposes only. Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[]) {
|
||||||
|
@autoreleasepool {
|
||||||
|
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -254,12 +254,12 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */;
|
buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */,
|
E080B80F89C34A25B3488E26 /* 📦 Check Pods Manifest.lock */,
|
||||||
05E2127D19D4DB510098F589 /* Sources */,
|
05E2127D19D4DB510098F589 /* Sources */,
|
||||||
05E2127E19D4DB510098F589 /* Frameworks */,
|
05E2127E19D4DB510098F589 /* Frameworks */,
|
||||||
05E2127F19D4DB510098F589 /* Resources */,
|
05E2127F19D4DB510098F589 /* Resources */,
|
||||||
F012A6F39E0149F18F564F50 /* Copy Pods Resources */,
|
F012A6F39E0149F18F564F50 /* 📦 Copy Pods Resources */,
|
||||||
06770D39D4186D6446B1BDD5 /* Embed Pods Frameworks */,
|
06770D39D4186D6446B1BDD5 /* 📦 Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -318,14 +318,14 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
06770D39D4186D6446B1BDD5 /* Embed Pods Frameworks */ = {
|
06770D39D4186D6446B1BDD5 /* 📦 Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
);
|
);
|
||||||
name = "Embed Pods Frameworks";
|
name = "📦 Embed Pods Frameworks";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -333,14 +333,14 @@
|
|||||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n";
|
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */ = {
|
E080B80F89C34A25B3488E26 /* 📦 Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
);
|
);
|
||||||
name = "Check Pods Manifest.lock";
|
name = "📦 Check Pods Manifest.lock";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -348,14 +348,14 @@
|
|||||||
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";
|
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;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
F012A6F39E0149F18F564F50 /* Copy Pods Resources */ = {
|
F012A6F39E0149F18F564F50 /* 📦 Copy Pods Resources */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
);
|
);
|
||||||
name = "Copy Pods Resources";
|
name = "📦 Copy Pods Resources";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
10
examples/ASDKgram/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal 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>
|
||||||
@ -38,7 +38,6 @@
|
|||||||
|
|
||||||
// Video node with custom play button
|
// Video node with custom play button
|
||||||
ASVideoNode *simonVideoNode = self.simonVideoNode;
|
ASVideoNode *simonVideoNode = self.simonVideoNode;
|
||||||
simonVideoNode.playButton = self.playButton;
|
|
||||||
[_rootNode addSubnode:simonVideoNode];
|
[_rootNode addSubnode:simonVideoNode];
|
||||||
|
|
||||||
_rootNode.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
|
_rootNode.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
|
||||||
@ -129,7 +128,7 @@
|
|||||||
|
|
||||||
#pragma mark - Actions
|
#pragma mark - Actions
|
||||||
|
|
||||||
- (void)videoNodeWasTapped:(ASVideoNode *)videoNode
|
- (void)didTapVideoNode:(ASVideoNode *)videoNode
|
||||||
{
|
{
|
||||||
if (videoNode == self.guitarVideoNode) {
|
if (videoNode == self.guitarVideoNode) {
|
||||||
if (videoNode.playerState == ASVideoNodePlayerStatePlaying) {
|
if (videoNode.playerState == ASVideoNodePlayerStatePlaying) {
|
||||||
@ -150,29 +149,29 @@
|
|||||||
|
|
||||||
#pragma mark - ASVideoNodeDelegate
|
#pragma mark - ASVideoNodeDelegate
|
||||||
|
|
||||||
- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toSate
|
- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState
|
||||||
{
|
{
|
||||||
//Ignore nicCageVideo
|
//Ignore nicCageVideo
|
||||||
if (videoNode != _guitarVideoNode) {
|
if (videoNode != _guitarVideoNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toSate == ASVideoNodePlayerStatePlaying) {
|
if (toState == ASVideoNodePlayerStatePlaying) {
|
||||||
NSLog(@"guitarVideoNode is playing");
|
NSLog(@"guitarVideoNode is playing");
|
||||||
} else if (toSate == ASVideoNodePlayerStateFinished) {
|
} else if (toState == ASVideoNodePlayerStateFinished) {
|
||||||
NSLog(@"guitarVideoNode finished");
|
NSLog(@"guitarVideoNode finished");
|
||||||
} else if (toSate == ASVideoNodePlayerStateLoading) {
|
} else if (toState == ASVideoNodePlayerStateLoading) {
|
||||||
NSLog(@"guitarVideoNode is buffering");
|
NSLog(@"guitarVideoNode is buffering");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)videoNode:(ASVideoNode *)videoNode didPlayToSecond:(NSTimeInterval)second
|
- (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval
|
||||||
{
|
{
|
||||||
if (videoNode != _guitarVideoNode) {
|
if (videoNode != _guitarVideoNode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLog(@"guitarVideoNode playback time is: %f",second);
|
NSLog(@"guitarVideoNode playback time is: %f",timeInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||