From 3b38559c4dd20c40b7be17f75fc6a11105d38747 Mon Sep 17 00:00:00 2001 From: Luke Parham Date: Thu, 28 Jan 2016 01:09:01 -0800 Subject: [PATCH] added ASDefaultPlayButton, but need to make drawing dynamic so its always a centered circle, also fixed video player preview layer stuff --- AsyncDisplayKit.xcodeproj/project.pbxproj | 8 +++ AsyncDisplayKit/ASVideoNode.h | 6 -- AsyncDisplayKit/ASVideoNode.mm | 36 ++++++++-- AsyncDisplayKit/Private/ASDefaultPlayButton.h | 13 ++++ AsyncDisplayKit/Private/ASDefaultPlayButton.m | 72 +++++++++++++++++++ .../Sample.xcodeproj/project.pbxproj | 16 +++++ examples/Videos/Sample/ViewController.m | 8 +-- 7 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 AsyncDisplayKit/Private/ASDefaultPlayButton.h create mode 100644 AsyncDisplayKit/Private/ASDefaultPlayButton.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index a7d2732172..b56a693021 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -360,6 +360,8 @@ ACF6ED611B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */; }; ACF6ED621B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */; }; ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */; }; + AEB7B01A1C5962EA00662EF4 /* ASDefaultPlayButton.h in Headers */ = {isa = PBXBuildFile; fileRef = AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */; }; + AEB7B01B1C5962EA00662EF4 /* ASDefaultPlayButton.m in Sources */ = {isa = PBXBuildFile; fileRef = AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */; }; AEEC47E11C20C2DD00EC1693 /* ASVideoNode.h in Headers */ = {isa = PBXBuildFile; fileRef = AEEC47DF1C20C2DD00EC1693 /* ASVideoNode.h */; }; AEEC47E21C20C2DD00EC1693 /* ASVideoNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */; }; AEEC47E41C21D3D200EC1693 /* ASVideoNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AEEC47E31C21D3D200EC1693 /* ASVideoNodeTests.m */; }; @@ -778,6 +780,8 @@ ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASOverlayLayoutSpecSnapshotTests.mm; sourceTree = ""; }; ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRatioLayoutSpecSnapshotTests.mm; sourceTree = ""; }; ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackLayoutSpecSnapshotTests.mm; sourceTree = ""; }; + AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDefaultPlayButton.h; sourceTree = ""; }; + AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDefaultPlayButton.m; sourceTree = ""; }; AEEC47DF1C20C2DD00EC1693 /* ASVideoNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASVideoNode.h; sourceTree = ""; }; AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASVideoNode.mm; sourceTree = ""; }; AEEC47E31C21D3D200EC1693 /* ASVideoNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVideoNodeTests.m; sourceTree = ""; }; @@ -1153,6 +1157,8 @@ ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, 0442850B1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h */, 0442850C1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm */, + AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */, + AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */, ); path = Private; sourceTree = ""; @@ -1315,6 +1321,7 @@ 205F0E191B37339C007741D0 /* ASAbstractLayoutController.h in Headers */, 058D0A82195D060300B7D73C /* ASAssert.h in Headers */, 0516FA3C1A15563400B4EBED /* ASAvailability.h in Headers */, + AEB7B01A1C5962EA00662EF4 /* ASDefaultPlayButton.h in Headers */, ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */, 058D0A83195D060300B7D73C /* ASBaseDefines.h in Headers */, 054963491A1EA066000F8E56 /* ASBasicImageDownloader.h in Headers */, @@ -1801,6 +1808,7 @@ A32FEDD61C501B6A004F642A /* ASTextKitFontSizeAdjuster.m in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, 055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */, + AEB7B01B1C5962EA00662EF4 /* ASDefaultPlayButton.m in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 0442850F1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */, 257754921BED28F300737CA5 /* ASEqualityHashHelpers.mm in Sources */, diff --git a/AsyncDisplayKit/ASVideoNode.h b/AsyncDisplayKit/ASVideoNode.h index 31b63545e2..7df171d005 100644 --- a/AsyncDisplayKit/ASVideoNode.h +++ b/AsyncDisplayKit/ASVideoNode.h @@ -8,12 +8,6 @@ #import -typedef NS_ENUM(NSUInteger, ASVideoGravity) { - ASVideoGravityResizeAspect, - ASVideoGravityResizeAspectFill, - ASVideoGravityResize -}; - @protocol ASVideoNodeDelegate; // If you need ASVideoNode, please use AsyncDisplayKit master until this comment is removed. diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index ddec7aa586..f6f717f452 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -7,6 +7,7 @@ */ #import "ASVideoNode.h" +#import "ASDefaultPlayButton.h" @interface ASVideoNode () { @@ -43,12 +44,14 @@ return nil; } - _previewQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); - #if DEBUG NSLog(@"*** Warning: ASVideoNode is a new component - the 1.9.6 version may cause performance hiccups."); #endif + _previewQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + + self.playButton = [[ASDefaultPlayButton alloc] init]; + self.gravity = AVLayerVideoGravityResizeAspect; [self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside]; @@ -114,6 +117,8 @@ _playerNode.frame = bounds; _playerNode.layer.frame = bounds; + _playButton.frame = bounds; + CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2; CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2; _playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff); @@ -142,13 +147,23 @@ } else { dispatch_async(_previewQueue, ^{ AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:_asset]; + imageGenerator.appliesPreferredTrackTransform = YES; [imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:CMTimeMake(0, 1)]] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) { UIImage *theImage = [UIImage imageWithCGImage:image]; _placeholderImageNode = [[ASImageNode alloc] init]; _placeholderImageNode.layerBacked = YES; _placeholderImageNode.image = theImage; - _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFit; + + if ([_gravity isEqualToString:AVLayerVideoGravityResize]) { + _placeholderImageNode.contentMode = UIViewContentModeRedraw; + } + if ([_gravity isEqualToString:AVLayerVideoGravityResizeAspect]) { + _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFit; + } + if ([_gravity isEqual:AVLayerVideoGravityResizeAspectFill]) { + _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFill; + } dispatch_async(dispatch_get_main_queue(), ^{ _placeholderImageNode.frame = self.bounds; @@ -321,12 +336,19 @@ return playerLayer; }]; - [self addSubnode:_playerNode]; + if ([self.subnodes containsObject:_playButton]) { + [self insertSubnode:_playerNode belowSubnode:_playButton]; + } else { + [self addSubnode:_playerNode]; + } } [_player play]; _shouldBePlaying = YES; - _playButton.alpha = 0.0; + + [UIView animateWithDuration:0.15 animations:^{ + _playButton.alpha = 0.0; + }]; if (![self ready] && _shouldBePlaying && (self.interfaceState & ASInterfaceStateVisible)) { [self addSubnode:_spinner]; @@ -346,7 +368,9 @@ [_player pause]; [((UIActivityIndicatorView *)_spinner.view) stopAnimating]; _shouldBePlaying = NO; - _playButton.alpha = 1.0; + [UIView animateWithDuration:0.15 animations:^{ + _playButton.alpha = 1.0; + }]; } - (BOOL)isPlaying diff --git a/AsyncDisplayKit/Private/ASDefaultPlayButton.h b/AsyncDisplayKit/Private/ASDefaultPlayButton.h new file mode 100644 index 0000000000..aac725461e --- /dev/null +++ b/AsyncDisplayKit/Private/ASDefaultPlayButton.h @@ -0,0 +1,13 @@ +// +// ASDefaultPlayButton.h +// AsyncDisplayKit +// +// Created by Luke Parham on 1/27/16. +// Copyright © 2016 Facebook. All rights reserved. +// + +#import + +@interface ASDefaultPlayButton : ASButtonNode + +@end diff --git a/AsyncDisplayKit/Private/ASDefaultPlayButton.m b/AsyncDisplayKit/Private/ASDefaultPlayButton.m new file mode 100644 index 0000000000..8411b21ad8 --- /dev/null +++ b/AsyncDisplayKit/Private/ASDefaultPlayButton.m @@ -0,0 +1,72 @@ +// +// ASDefaultPlayButton.m +// AsyncDisplayKit +// +// Created by Luke Parham on 1/27/16. +// Copyright © 2016 Facebook. All rights reserved. +// + +#import "ASDefaultPlayButton.h" + +@implementation ASDefaultPlayButton + +- (instancetype)init +{ + if (!(self = [super init])) { + return nil; + } + + self.opaque = NO; + + return self; +} + ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing +{ + CGRect buttonBounds; + buttonBounds = CGRectMake(bounds.size.width/4, bounds.size.height/4, bounds.size.width/2, bounds.size.height/2); + + if (bounds.size.width < bounds.size.height) { + //then use the width to determine the rect size then calculate the origin x y + buttonBounds = CGRectMake(bounds.size.width/4, bounds.size.width/4, bounds.size.width/2, bounds.size.width/2); + } + if (bounds.size.width > bounds.size.height) { + //use the height + buttonBounds = CGRectMake(bounds.size.height/4, bounds.size.height/4, bounds.size.height/2, bounds.size.height/2); + } + if (bounds.size.width == bounds.size.height) { + //square so easy + buttonBounds = CGRectMake(bounds.size.width/4, bounds.size.height/4, bounds.size.width/2, bounds.size.height/2); + } + + if (!isRasterizing) { + [[UIColor clearColor] set]; + UIRectFill(bounds); + } + + CGContextRef context = UIGraphicsGetCurrentContext(); + + // Circle Drawing + UIBezierPath *ovalPath = [UIBezierPath bezierPathWithOvalInRect: buttonBounds]; + [[UIColor colorWithWhite:0.0 alpha:0.5] setFill]; + [ovalPath stroke]; + [ovalPath fill]; + + // Triangle Drawing + CGContextSaveGState(context); + + CGFloat buttonWidth = buttonBounds.size.width; + + UIBezierPath *trianglePath = [UIBezierPath bezierPath]; + [trianglePath moveToPoint:CGPointMake(bounds.size.width/4 + buttonWidth/3, bounds.size.height/4 + (bounds.size.height/2)/4)]; + [trianglePath addLineToPoint:CGPointMake(bounds.size.width/4 + buttonWidth/3, bounds.size.height - bounds.size.height/4 - (bounds.size.height/2)/4)]; + [trianglePath addLineToPoint:CGPointMake(bounds.size.width - bounds.size.width/4 - buttonWidth/4, bounds.size.height/2)]; + + [trianglePath closePath]; + [[UIColor colorWithWhite:0.9 alpha:0.9] setFill]; + [trianglePath fill]; + + CGContextRestoreGState(context); +} + +@end diff --git a/examples/VideoTableView/Sample.xcodeproj/project.pbxproj b/examples/VideoTableView/Sample.xcodeproj/project.pbxproj index f2ba462aca..9e585d3265 100644 --- a/examples/VideoTableView/Sample.xcodeproj/project.pbxproj +++ b/examples/VideoTableView/Sample.xcodeproj/project.pbxproj @@ -128,6 +128,7 @@ 05E2127E19D4DB510098F589 /* Frameworks */, 05E2127F19D4DB510098F589 /* Resources */, F012A6F39E0149F18F564F50 /* Copy Pods Resources */, + EBE12F047824F0A2C6353B54 /* Embed Pods Frameworks */, ); buildRules = ( ); @@ -199,6 +200,21 @@ 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; }; + EBE12F047824F0A2C6353B54 /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; F012A6F39E0149F18F564F50 /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/examples/Videos/Sample/ViewController.m b/examples/Videos/Sample/ViewController.m index 3731ba9685..942a219515 100644 --- a/examples/Videos/Sample/ViewController.m +++ b/examples/Videos/Sample/ViewController.m @@ -43,7 +43,7 @@ videoNode.backgroundColor = [UIColor lightGrayColor]; - videoNode.playButton = [self playButton]; +// videoNode.playButton = [self playButton]; return videoNode; } @@ -61,7 +61,7 @@ nicCageVideo.backgroundColor = [UIColor lightGrayColor]; nicCageVideo.shouldAutorepeat = YES; - nicCageVideo.playButton = [self playButton]; +// nicCageVideo.playButton = [self playButton]; return nicCageVideo; } @@ -75,11 +75,11 @@ simonVideo.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - ([UIScreen mainScreen].bounds.size.height/3), [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3); - simonVideo.gravity = ASVideoGravityResizeAspect; + simonVideo.gravity = AVLayerVideoGravityResizeAspect; simonVideo.backgroundColor = [UIColor lightGrayColor]; simonVideo.shouldAutorepeat = YES; - simonVideo.playButton = [self playButton]; +// simonVideo.playButton = [self playButton]; simonVideo.shouldAutoplay = YES; return simonVideo;