Adding ASVideoPlayerNode

This commit is contained in:
Erekle 2016-05-14 16:41:32 +04:00 committed by Max Gu
parent b3360a5daf
commit 8c8fc3dba0
22 changed files with 974 additions and 84 deletions

View File

@ -311,6 +311,8 @@
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 */; };
8BDA5FC51CDBDDE1007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; }; 8BDA5FC51CDBDDE1007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; };
8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */; }; 8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8BDA5FC41CDBDDE1007D13B2 /* ASVideoPlayerNode.mm */; };
8BDA5FC71CDBDF91007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; }; 8BDA5FC71CDBDF91007D13B2 /* ASVideoPlayerNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8BDA5FC31CDBDDE1007D13B2 /* ASVideoPlayerNode.h */; };
@ -824,6 +826,8 @@
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>"; }; 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>"; }; 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>"; };
@ -1340,6 +1344,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>";
@ -1600,6 +1606,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 */,
@ -2014,6 +2021,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 */,
@ -2119,8 +2127,6 @@
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 */, 8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */,
ACC945AB1BA9E7C1005E1FB8 /* ASViewController.m in Sources */,
B0F8805B1BEAEC7500D17647 /* ASTableNode.m 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 */,

View File

@ -71,12 +71,12 @@ NS_ASSUME_NONNULL_BEGIN
* @abstract Delegate method invoked in layoutSpecThatFits: * @abstract Delegate method invoked in layoutSpecThatFits:
* @param videoPlayer * @param videoPlayer
* @param controls - Dictionary of controls which are used in videoPlayer; Dictionary keys are ASVideoPlayerNodeControlType * @param controls - Dictionary of controls which are used in videoPlayer; Dictionary keys are ASVideoPlayerNodeControlType
* @param constrainedSize - ASSizeRange for ASVideoPlayerNode * @param maxSize - Maximum size for ASVideoPlayerNode
* @discussion - Developer can layout whole ASVideoPlayerNode as he wants. ASVideoNode is locked and it can't be changed * @discussion - Developer can layout whole ASVideoPlayerNode as he wants. ASVideoNode is locked and it can't be changed
*/ */
- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer - (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer
forControls:(NSDictionary *)controls forControls:(NSDictionary *)controls
forConstrainedSize:(ASSizeRange)constrainedSize; forMaximumSize:(CGSize)maxSize;
#pragma mark Text delegate methods #pragma mark Text delegate methods
/** /**
@ -95,8 +95,16 @@ NS_ASSUME_NONNULL_BEGIN
- (UIColor *)videoPlayerNodeScrubberThumbTint:(ASVideoPlayerNode *)videoPlayer; - (UIColor *)videoPlayerNodeScrubberThumbTint:(ASVideoPlayerNode *)videoPlayer;
- (UIImage *)videoPlayerNodeScrubberThumbImage:(ASVideoPlayerNode *)videoPlayer; - (UIImage *)videoPlayerNodeScrubberThumbImage:(ASVideoPlayerNode *)videoPlayer;
#pragma mark - Playback button delegate methods
- (UIColor *)videoPlayerNodePlaybackButtonTint:(ASVideoPlayerNode *)videoPlayer;
#pragma mark ASVideoNodeDelegate proxy methods #pragma mark ASVideoNodeDelegate proxy methods
/**
* @abstract Delegate method invoked when ASVideoPlayerNode playback time is taped.
* @param videoPlayerNode The ASVideoPlayerNode that was tapped.
*/
- (void)videoPlayerNodeWasTapped:(ASVideoPlayerNode *)videoPlayer;
/** /**
* @abstract Delegate method invoked when ASVideoNode playback time is updated. * @abstract Delegate method invoked when ASVideoNode playback time is updated.
* @param videoPlayerNode The video node that was tapped. * @param videoPlayerNode The video node that was tapped.

View File

@ -7,6 +7,7 @@
// //
#import "ASVideoPlayerNode.h" #import "ASVideoPlayerNode.h"
#import "ASDefaultPlaybackButton.h"
static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
@ -18,6 +19,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
struct { struct {
unsigned int delegateNeededControls:1; unsigned int delegateNeededControls:1;
unsigned int delegatePlaybackButtonTint:1;
unsigned int delegateScrubberMaximumTrackTintColor:1; unsigned int delegateScrubberMaximumTrackTintColor:1;
unsigned int delegateScrubberMinimumTrackTintColor:1; unsigned int delegateScrubberMinimumTrackTintColor:1;
unsigned int delegateScrubberThumbTintColor:1; unsigned int delegateScrubberThumbTintColor:1;
@ -29,6 +31,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
unsigned int delegateVideoNodeWillChangeState:1; unsigned int delegateVideoNodeWillChangeState:1;
unsigned int delegateVideoNodeShouldChangeState:1; unsigned int delegateVideoNodeShouldChangeState:1;
unsigned int delegateVideoNodePlaybackDidFinish:1; unsigned int delegateVideoNodePlaybackDidFinish:1;
unsigned int delegateVideoNodeTapped:1;
} _delegateFlags; } _delegateFlags;
NSURL *_url; NSURL *_url;
@ -40,7 +43,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
NSMutableDictionary *_cachedControls; NSMutableDictionary *_cachedControls;
ASControlNode *_playbackButtonNode; ASDefaultPlaybackButton *_playbackButtonNode;
ASTextNode *_elapsedTextNode; ASTextNode *_elapsedTextNode;
ASTextNode *_durationTextNode; ASTextNode *_durationTextNode;
ASDisplayNode *_scrubberNode; ASDisplayNode *_scrubberNode;
@ -56,6 +59,8 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
BOOL _muted; BOOL _muted;
int32_t _periodicTimeObserverTimescale; int32_t _periodicTimeObserverTimescale;
NSString *_gravity; NSString *_gravity;
UIColor *_defaultControlsColor;
} }
@end @end
@ -103,6 +108,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
- (void)privateInit - (void)privateInit
{ {
_defaultControlsColor = [UIColor whiteColor];
_cachedControls = [[NSMutableDictionary alloc] init]; _cachedControls = [[NSMutableDictionary alloc] init];
_videoNode = [[ASVideoNode alloc] init]; _videoNode = [[ASVideoNode alloc] init];
@ -213,9 +219,13 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
- (void)createPlaybackButton - (void)createPlaybackButton
{ {
if (_playbackButtonNode == nil) { if (_playbackButtonNode == nil) {
_playbackButtonNode = [[ASControlNode alloc] init]; _playbackButtonNode = [[ASDefaultPlaybackButton alloc] init];
_playbackButtonNode.preferredFrameSize = CGSizeMake(20.0, 20.0); _playbackButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
_playbackButtonNode.backgroundColor = [UIColor redColor]; if (_delegateFlags.delegatePlaybackButtonTint) {
_playbackButtonNode.tintColor = [_delegate videoPlayerNodePlaybackButtonTint:self];
} else {
_playbackButtonNode.tintColor = _defaultControlsColor;
}
[_playbackButtonNode addTarget:self action:@selector(playbackButtonTapped:) forControlEvents:ASControlNodeEventTouchUpInside]; [_playbackButtonNode addTarget:self action:@selector(playbackButtonTapped:) forControlEvents:ASControlNodeEventTouchUpInside];
[_cachedControls setObject:_playbackButtonNode forKey:@(ASVideoPlayerNodeControlTypePlaybackButton)]; [_cachedControls setObject:_playbackButtonNode forKey:@(ASVideoPlayerNodeControlTypePlaybackButton)];
} }
@ -316,7 +326,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
} else { } else {
options = @{ options = @{
NSFontAttributeName : [UIFont systemFontOfSize:12.0], NSFontAttributeName : [UIFont systemFontOfSize:12.0],
NSForegroundColorAttributeName: [UIColor whiteColor] NSForegroundColorAttributeName: _defaultControlsColor
}; };
} }
@ -337,6 +347,12 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
_duration = _videoNode.currentItem.duration; _duration = _videoNode.currentItem.duration;
[self updateDurationTimeLabel]; [self updateDurationTimeLabel];
} }
if (toState == ASVideoNodePlayerStatePlaying) {
_playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePause;
} else {
_playbackButtonNode.buttonType = ASDefaultPlaybackButtonTypePlay;
}
} }
- (BOOL)videoNode:(ASVideoNode *)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state - (BOOL)videoNode:(ASVideoNode *)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state
@ -376,15 +392,26 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
} }
} }
- (void)videoNodeWasTapped:(ASVideoNode *)videoNode
{
if (_delegateFlags.delegateVideoNodeTapped) {
[_delegate videoPlayerNodeWasTapped:self];
} else {
if (videoNode.playerState == ASVideoNodePlayerStatePlaying) {
[videoNode pause];
} else {
[videoNode play];
}
}
}
#pragma mark - Actions #pragma mark - Actions
- (void)playbackButtonTapped:(ASControlNode*)node - (void)playbackButtonTapped:(ASControlNode*)node
{ {
if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) { if (_videoNode.playerState == ASVideoNodePlayerStatePlaying) {
[_videoNode pause]; [_videoNode pause];
_playbackButtonNode.backgroundColor = [UIColor greenColor];
} else { } else {
[_videoNode play]; [_videoNode play];
_playbackButtonNode.backgroundColor = [UIColor redColor];
} }
} }
@ -458,25 +485,35 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
#pragma mark - Layout #pragma mark - Layout
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize - (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
{ {
_videoNode.preferredFrameSize = constrainedSize.max; 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; ASLayoutSpec *layoutSpec;
if (_delegateFlags.delegateLayoutSpecForControls) { if (_delegateFlags.delegateLayoutSpecForControls) {
layoutSpec = [_delegate videoPlayerNodeLayoutSpec:self forControls:_cachedControls forConstrainedSize:constrainedSize]; layoutSpec = [_delegate videoPlayerNodeLayoutSpec:self forControls:_cachedControls forMaximumSize:maxSize];
} else { } else {
layoutSpec = [self defaultLayoutSpecThatFits:constrainedSize]; layoutSpec = [self defaultLayoutSpecThatFits:maxSize];
} }
ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec]; ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec];
overlaySpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max); overlaySpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[overlaySpec]]; return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[overlaySpec]];
} }
- (ASLayoutSpec*)defaultLayoutSpecThatFits:(ASSizeRange)constrainedSize - (ASLayoutSpec*)defaultLayoutSpecThatFits:(CGSize)maxSize
{ {
_scrubberNode.preferredFrameSize = CGSizeMake(constrainedSize.max.width, 44.0); _scrubberNode.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.flexGrow = YES; spacer.flexGrow = YES;
@ -521,12 +558,14 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
_delegateFlags.delegateScrubberThumbTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbTint:)]; _delegateFlags.delegateScrubberThumbTintColor = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbTint:)];
_delegateFlags.delegateScrubberThumbImage = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbImage:)]; _delegateFlags.delegateScrubberThumbImage = [_delegate respondsToSelector:@selector(videoPlayerNodeScrubberThumbImage:)];
_delegateFlags.delegateTimeLabelAttributes = [_delegate respondsToSelector:@selector(videoPlayerNodeTimeLabelAttributes:timeLabelType:)]; _delegateFlags.delegateTimeLabelAttributes = [_delegate respondsToSelector:@selector(videoPlayerNodeTimeLabelAttributes:timeLabelType:)];
_delegateFlags.delegateLayoutSpecForControls = [_delegate respondsToSelector:@selector(videoPlayerNodeLayoutSpec:forControls:forConstrainedSize:)]; _delegateFlags.delegateLayoutSpecForControls = [_delegate respondsToSelector:@selector(videoPlayerNodeLayoutSpec:forControls:forMaximumSize:)];
_delegateFlags.delegateVideoNodeDidPlayToTime = [_delegate respondsToSelector:@selector(videoPlayerNode:didPlayToTime:)]; _delegateFlags.delegateVideoNodeDidPlayToTime = [_delegate respondsToSelector:@selector(videoPlayerNode:didPlayToTime:)];
_delegateFlags.delegateVideoNodeWillChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:willChangeVideoNodeState:toVideoNodeState:)]; _delegateFlags.delegateVideoNodeWillChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:willChangeVideoNodeState:toVideoNodeState:)];
_delegateFlags.delegateVideoNodePlaybackDidFinish = [_delegate respondsToSelector:@selector(videoPlayerNodeDidPlayToEnd:)]; _delegateFlags.delegateVideoNodePlaybackDidFinish = [_delegate respondsToSelector:@selector(videoPlayerNodeDidPlayToEnd:)];
_delegateFlags.delegateVideoNodeShouldChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:shouldChangeVideoNodeStateTo:)]; _delegateFlags.delegateVideoNodeShouldChangeState = [_delegate respondsToSelector:@selector(videoPlayerNode:shouldChangeVideoNodeStateTo:)];
_delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)]; _delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)];
_delegateFlags.delegatePlaybackButtonTint = [_delegate respondsToSelector:@selector(videoPlayerNodePlaybackButtonTint:)];
_delegateFlags.delegateVideoNodeTapped = [_delegate respondsToSelector:@selector(videoPlayerNodeWasTapped:)];
} }
} }

View 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

View 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

View File

@ -14,6 +14,11 @@
5791C5525B690FA54F26ACE8 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2092CAF5607B3863A3700A2 /* libPods-Sample.a */; }; 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 */; }; 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 */; }; 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 */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@ -27,6 +32,16 @@
05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; 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; }; 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; }; 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>"; };
A2092CAF5607B3863A3700A2 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 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>"; }; 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>"; }; 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>"; };
@ -68,11 +83,16 @@
05E2128319D4DB510098F589 /* Sample */ = { 05E2128319D4DB510098F589 /* Sample */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8B0768C61CE7C85F002E1453 /* Controller */,
8B0768B91CE7B07E002E1453 /* Nodes */,
8B0768B51CE7ACE8002E1453 /* Models */,
05E2128819D4DB510098F589 /* AppDelegate.h */, 05E2128819D4DB510098F589 /* AppDelegate.h */,
05E2128919D4DB510098F589 /* AppDelegate.m */, 05E2128919D4DB510098F589 /* AppDelegate.m */,
05E2128B19D4DB510098F589 /* ViewController.h */, 05E2128B19D4DB510098F589 /* ViewController.h */,
05E2128C19D4DB510098F589 /* ViewController.m */, 05E2128C19D4DB510098F589 /* ViewController.m */,
05E2128419D4DB510098F589 /* Supporting Files */, 05E2128419D4DB510098F589 /* Supporting Files */,
8B0768BD1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.h */,
8B0768BE1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m */,
); );
path = Sample; path = Sample;
sourceTree = "<group>"; sourceTree = "<group>";
@ -106,6 +126,35 @@
name = Pods; name = Pods;
sourceTree = "<group>"; 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>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -228,8 +277,13 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
05E2128D19D4DB510098F589 /* ViewController.m in Sources */, 05E2128D19D4DB510098F589 /* ViewController.m in Sources */,
8B0768C91CE7C889002E1453 /* VideoFeedNodeController.m in Sources */,
05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */,
8B0768BC1CE7B091002E1453 /* VideoContentCell.m in Sources */,
8B0768BF1CE7C5A1002E1453 /* WindowWithStatusBarUnderlay.m in Sources */,
05E2128719D4DB510098F589 /* main.m in Sources */, 05E2128719D4DB510098F589 /* main.m in Sources */,
8B0768C51CE7C707002E1453 /* Utilities.m in Sources */,
8B0768B81CE7AD03002E1453 /* VideoModel.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -10,18 +10,36 @@
*/ */
#import "AppDelegate.h" #import "AppDelegate.h"
#import "WindowWithStatusBarUnderlay.h"
#import "Utilities.h"
#import "VideoFeedNodeController.h"
#import "ViewController.h"
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // this UIWindow subclass is neccessary to make the status bar opaque
self.window.backgroundColor = [UIColor whiteColor]; _window = [[WindowWithStatusBarUnderlay alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [[ViewController alloc] init]; _window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
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; return YES;
} }
@end @end

View File

@ -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

View File

@ -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

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
@ -26,11 +28,11 @@
<array> <array>
<string>armv7</string> <string>armv7</string>
</array> </array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,151 @@
//
// 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;
}
- (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];
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url];
_videoPlayerNode.delegate = self;
[self addSubnode:_videoPlayerNode];
}
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;
}
#pragma mark - ASVideoPlayerNodeDelegate
- (void)videoPlayerNodeWasTapped:(ASVideoPlayerNode *)videoPlayer
{
if (_videoPlayerNode.playerState == ASVideoNodePlayerStatePlaying) {
NSLog(@"TRANSITION");
} else {
[_videoPlayerNode play];
}
}
- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer
{
return @[ @(ASVideoPlayerNodeControlTypePlaybackButton) ];
}
- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(ASVideoPlayerNode *)videoPlayer forControls:(NSDictionary *)controls forMaximumSize:(CGSize)maxSize
{
NSMutableArray *bottomControls = [[NSMutableArray alloc] init];
ASDisplayNode *playbackButtonNode = controls[@(ASVideoPlayerNodeControlTypePlaybackButton)];
if (playbackButtonNode) {
[bottomControls addObject:playbackButtonNode];
}
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.flexGrow = YES;
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:@[ spacer, controlbarInsetSpec ]];
return mainVerticalStack;
}
@end

View File

@ -8,8 +8,8 @@
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * 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. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#import <AsyncDisplayKit/AsyncDisplayKit.h> #import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASVideoPlayerNode.h>
@interface ViewController : ASViewController @interface ViewController : ASViewController

View File

@ -10,31 +10,73 @@
*/ */
#import "ViewController.h" #import "ViewController.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASVideoPlayerNode.h>
#import "VideoModel.h"
#import "VideoContentCell.h"
@interface ViewController()<ASVideoPlayerNodeDelegate> @interface ViewController()<ASVideoPlayerNodeDelegate, ASTableDelegate, ASTableDataSource>
@property (nonatomic, strong) ASVideoPlayerNode *videoPlayerNode; @property (nonatomic, strong) ASVideoPlayerNode *videoPlayerNode;
@end @end
@implementation ViewController @implementation ViewController
{
ASTableNode *_tableNode;
NSMutableArray<VideoModel*> *_videoFeedData;
}
- (instancetype)init - (instancetype)init
{ {
if (!(self = [super initWithNode:self.videoPlayerNode])) { _tableNode = [[ASTableNode alloc] init];
_tableNode.delegate = self;
_tableNode.dataSource = self;
if (!(self = [super initWithNode:_tableNode])) {
return nil; return nil;
} }
return self; return self;
} }
- (void)loadView
{
[super loadView];
_videoFeedData = [[NSMutableArray alloc] initWithObjects:[[VideoModel alloc] init], [[VideoModel alloc] init], nil];
[_tableNode.view reloadData];
}
- (void)viewWillAppear:(BOOL)animated - (void)viewWillAppear:(BOOL)animated
{ {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
//[self.view addSubnode:self.videoPlayerNode]; //[self.view addSubnode:self.videoPlayerNode];
//[self.videoPlayerNode setNeedsLayout]; //[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; - (ASVideoPlayerNode *)videoPlayerNode;
{ {
if (_videoPlayerNode) { if (_videoPlayerNode) {
@ -57,49 +99,49 @@
} }
#pragma mark - ASVideoPlayerNodeDelegate #pragma mark - ASVideoPlayerNodeDelegate
- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer //- (NSArray *)videoPlayerNodeNeededControls:(ASVideoPlayerNode *)videoPlayer
{ //{
return @[ @(ASVideoPlayerNodeControlTypePlaybackButton), // return @[ @(ASVideoPlayerNodeControlTypePlaybackButton),
@(ASVideoPlayerNodeControlTypeElapsedText), // @(ASVideoPlayerNodeControlTypeElapsedText),
@(ASVideoPlayerNodeControlTypeScrubber), // @(ASVideoPlayerNodeControlTypeScrubber),
@(ASVideoPlayerNodeControlTypeDurationText) ]; // @(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;
//}
- (UIColor *)videoPlayerNodeScrubberMaximumTrackTint:(ASVideoPlayerNode *)videoPlayer /*- (ASLayoutSpec *)videoPlayerNodeLayoutSpec:(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 forControls:(NSDictionary *)controls
forConstrainedSize:(ASSizeRange)constrainedSize forConstrainedSize:(ASSizeRange)constrainedSize
{ {
@ -166,6 +208,6 @@
return mainVerticalStack; return mainVerticalStack;
} }*/
@end @end

View 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

View 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

View File

@ -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 = (
); );
@ -319,14 +319,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;
@ -334,14 +334,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;
@ -349,14 +349,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;

View File

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