mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-10 16:29:55 +00:00
Merge pull request #1575 from aaronschubert0/tvOS
[tvOS] Improve tvOS code based upon comments.
This commit is contained in:
commit
77713019d2
@ -302,6 +302,14 @@
|
||||
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, ); }; };
|
||||
81EE38501C8E94F000456208 /* ASRunLoopQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */; };
|
||||
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 */; };
|
||||
92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */; };
|
||||
92074A641CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */; };
|
||||
92074A671CC8BADA00918F75 /* ASControlNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 92074A651CC8BADA00918F75 /* ASControlNode+tvOS.h */; };
|
||||
92074A681CC8BADA00918F75 /* ASControlNode+tvOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 92074A651CC8BADA00918F75 /* ASControlNode+tvOS.h */; };
|
||||
92074A691CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 92074A661CC8BADA00918F75 /* ASControlNode+tvOS.m */; };
|
||||
92074A6A1CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */ = {isa = PBXBuildFile; fileRef = 92074A661CC8BADA00918F75 /* ASControlNode+tvOS.m */; };
|
||||
92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */; };
|
||||
92DD2FE61BF4D05E0074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; };
|
||||
@ -784,6 +792,10 @@
|
||||
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>"; };
|
||||
81EE384E1C8E94F000456208 /* ASRunLoopQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRunLoopQueue.mm; path = ../ASRunLoopQueue.mm; 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>"; };
|
||||
92074A651CC8BADA00918F75 /* ASControlNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+tvOS.h"; sourceTree = "<group>"; };
|
||||
92074A661CC8BADA00918F75 /* ASControlNode+tvOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASControlNode+tvOS.m"; sourceTree = "<group>"; };
|
||||
92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMapNode.h; sourceTree = "<group>"; };
|
||||
92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMapNode.mm; sourceTree = "<group>"; };
|
||||
92DD2FE51BF4D05E0074C9DD /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; };
|
||||
@ -1069,6 +1081,7 @@
|
||||
764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */,
|
||||
764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */,
|
||||
DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */,
|
||||
92074A5E1CC8B9DD00918F75 /* tvOS */,
|
||||
058D09E1195D050800B7D73C /* Details */,
|
||||
058D0A01195D050800B7D73C /* Private */,
|
||||
AC6456051B0A333200CF11B8 /* Layout */,
|
||||
@ -1346,6 +1359,17 @@
|
||||
name = "Data Controller";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92074A5E1CC8B9DD00918F75 /* tvOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92074A5F1CC8BA1900918F75 /* ASImageNode+tvOS.h */,
|
||||
92074A601CC8BA1900918F75 /* ASImageNode+tvOS.m */,
|
||||
92074A651CC8BADA00918F75 /* ASControlNode+tvOS.h */,
|
||||
92074A661CC8BADA00918F75 /* ASControlNode+tvOS.m */,
|
||||
);
|
||||
name = tvOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AC6456051B0A333200CF11B8 /* Layout */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1480,6 +1504,7 @@
|
||||
058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */,
|
||||
AC7A2C171BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */,
|
||||
058D0A4D195D05CB00B7D73C /* ASDisplayNodeExtras.h in Headers */,
|
||||
92074A671CC8BADA00918F75 /* ASControlNode+tvOS.h in Headers */,
|
||||
68355B3B1CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h in Headers */,
|
||||
68B0277A1C1A79CC0041016B /* ASDisplayNode+Beta.h in Headers */,
|
||||
767E7F8D1C9019130066C000 /* AsyncDisplayKit+Debug.h in Headers */,
|
||||
@ -1561,6 +1586,7 @@
|
||||
058D0A66195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.h in Headers */,
|
||||
205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */,
|
||||
058D0A6F195D05EC00B7D73C /* UIView+ASConvenience.h in Headers */,
|
||||
92074A611CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1604,6 +1630,7 @@
|
||||
B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */,
|
||||
B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */,
|
||||
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
|
||||
92074A681CC8BADA00918F75 /* ASControlNode+tvOS.h in Headers */,
|
||||
044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */,
|
||||
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
|
||||
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
|
||||
@ -1614,6 +1641,7 @@
|
||||
254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */,
|
||||
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
|
||||
CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */,
|
||||
92074A621CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */,
|
||||
B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */,
|
||||
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */,
|
||||
B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */,
|
||||
@ -1974,6 +2002,7 @@
|
||||
698548651CA9E025008A345F /* ASEnvironment.m in Sources */,
|
||||
ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */,
|
||||
DB55C2631C6408D6004EDCF5 /* _ASTransitionContext.m in Sources */,
|
||||
92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */,
|
||||
251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */,
|
||||
ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */,
|
||||
257754B01BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm in Sources */,
|
||||
@ -2009,6 +2038,7 @@
|
||||
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
|
||||
257754A61BEE44CD00737CA5 /* ASTextKitAttributes.mm in Sources */,
|
||||
81EE38501C8E94F000456208 /* ASRunLoopQueue.mm in Sources */,
|
||||
92074A691CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */,
|
||||
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
|
||||
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
|
||||
68355B311CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */,
|
||||
@ -2128,6 +2158,7 @@
|
||||
CC3B208C1C3F7A5400798563 /* ASWeakSet.m in Sources */,
|
||||
B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */,
|
||||
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
|
||||
92074A641CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */,
|
||||
B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */,
|
||||
68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */,
|
||||
B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */,
|
||||
@ -2163,6 +2194,7 @@
|
||||
DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */,
|
||||
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
|
||||
34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */,
|
||||
92074A6A1CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */,
|
||||
DB78412E1C6BCE1600A9E2B4 /* _ASTransitionContext.m in Sources */,
|
||||
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,
|
||||
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */,
|
||||
|
||||
@ -69,7 +69,11 @@
|
||||
{
|
||||
if (!_titleNode) {
|
||||
_titleNode = [[ASTextNode alloc] init];
|
||||
#if TARGET_OS_IOS
|
||||
// tvOS needs access to the underlying view
|
||||
// of the button node to add a touch handler.
|
||||
[_titleNode setLayerBacked:YES];
|
||||
#endif
|
||||
[_titleNode setFlexShrink:YES];
|
||||
}
|
||||
return _titleNode;
|
||||
|
||||
15
AsyncDisplayKit/ASControlNode+tvOS.h
Normal file
15
AsyncDisplayKit/ASControlNode+tvOS.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// ASControlNode+tvOS.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Aaron Schubert on 21/04/2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ASControlNode (tvOS)
|
||||
|
||||
@end
|
||||
#endif
|
||||
88
AsyncDisplayKit/ASControlNode+tvOS.m
Normal file
88
AsyncDisplayKit/ASControlNode+tvOS.m
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// ASControlNode+tvOS.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Aaron Schubert on 21/04/2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
#if TARGET_OS_TV
|
||||
#import "ASControlNode+tvOS.h"
|
||||
|
||||
@implementation ASControlNode (tvOS)
|
||||
|
||||
#pragma mark - tvOS
|
||||
- (void)pressDown
|
||||
{
|
||||
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationCurveLinear animations:^{
|
||||
[self setPressedState];
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationCurveLinear animations:^{
|
||||
[self setFocusedState];
|
||||
} completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFocused
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldUpdateFocusInContext:(nonnull UIFocusUpdateContext *)context
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
||||
{
|
||||
//FIXME: This is never valid inside an ASCellNode
|
||||
if (context.nextFocusedView && context.nextFocusedView == self.view) {
|
||||
//Focused
|
||||
[coordinator addCoordinatedAnimations:^{
|
||||
[self setFocusedState];
|
||||
} completion:nil];
|
||||
} else{
|
||||
//Not focused
|
||||
[coordinator addCoordinatedAnimations:^{
|
||||
[self setDefaultFocusAppearance];
|
||||
} completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFocusedState
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
layer.shadowOffset = CGSizeMake(2, 10);
|
||||
[self applyDefaultShadowProperties: layer];
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
|
||||
}
|
||||
|
||||
- (void)setPressedState
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
layer.shadowOffset = CGSizeMake(2, 2);
|
||||
[self applyDefaultShadowProperties: layer];
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
|
||||
}
|
||||
|
||||
- (void)applyDefaultShadowProperties:(CALayer *)layer
|
||||
{
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 12.0;
|
||||
layer.shadowOpacity = 0.45;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
}
|
||||
|
||||
- (void)setDefaultFocusAppearance
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
layer.shadowOffset = CGSizeZero;
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 0;
|
||||
layer.shadowOpacity = 0;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
@ -126,7 +126,7 @@ typedef NS_OPTIONS(NSUInteger, ASControlState) {
|
||||
/**
|
||||
@abstract How the node looks when it isn't focused. Exposed here so that subclasses can override.
|
||||
*/
|
||||
- (void)setDefaultState;
|
||||
- (void)setDefaultFocusAppearance;
|
||||
#endif
|
||||
@end
|
||||
|
||||
|
||||
@ -96,7 +96,8 @@ static BOOL _enableHitTestDebug = NO;
|
||||
#if TARGET_OS_TV
|
||||
- (void)didLoad
|
||||
{
|
||||
//On tvOS all control views, such as buttons, interact with the focus system even if they don't have a target set on them. Here we add our own internal tap gesture to handle this behaviour.
|
||||
// On tvOS all controls, such as buttons, interact with the focus system even if they don't have a target set on them.
|
||||
// Here we add our own internal tap gesture to handle this behaviour.
|
||||
self.userInteractionEnabled = YES;
|
||||
UITapGestureRecognizer *tapGestureRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pressDown)];
|
||||
tapGestureRec.allowedPressTypes = @[@(UIPressTypeSelect)];
|
||||
@ -468,82 +469,6 @@ void _ASEnumerateControlEventsIncludedInMaskWithBlock(ASControlNodeEvent mask, v
|
||||
{
|
||||
}
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#pragma mark - tvOS
|
||||
- (void)pressDown
|
||||
{
|
||||
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationCurveLinear animations:^{
|
||||
[self setPressedState];
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationCurveLinear animations:^{
|
||||
[self setFocusedState];
|
||||
} completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeFocused
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)shouldUpdateFocusInContext:(nonnull UIFocusUpdateContext *)context
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
||||
{
|
||||
//FIXME: This is never valid inside an ASCellNode
|
||||
if (context.nextFocusedView && context.nextFocusedView == self.view) {
|
||||
//Focused
|
||||
[coordinator addCoordinatedAnimations:^{
|
||||
[self setFocusedState];
|
||||
} completion:nil];
|
||||
} else{
|
||||
//Not focused
|
||||
[coordinator addCoordinatedAnimations:^{
|
||||
[self setDefaultState];
|
||||
} completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFocusedState
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
layer.shadowOffset = CGSizeMake(2, 10);
|
||||
[self applyDefaultShadowProperties: layer];
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
|
||||
}
|
||||
|
||||
- (void)setPressedState
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
layer.shadowOffset = CGSizeMake(2, 2);
|
||||
[self applyDefaultShadowProperties: layer];
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
|
||||
}
|
||||
|
||||
- (void)applyDefaultShadowProperties:(CALayer *)layer
|
||||
{
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 12.0;
|
||||
layer.shadowOpacity = 0.45;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
}
|
||||
|
||||
- (void)setDefaultState
|
||||
{
|
||||
CALayer *layer = self.layer;
|
||||
layer.shadowOffset = CGSizeZero;
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 0;
|
||||
layer.shadowOpacity = 0;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
|
||||
}
|
||||
#endif //TARGET_OS_TV
|
||||
#pragma mark - Debug
|
||||
// Layout method required when _enableHitTestDebug is enabled.
|
||||
- (void)layout
|
||||
|
||||
@ -36,6 +36,8 @@
|
||||
|
||||
@implementation ASPanningOverriddenUITextView
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
// tvOS doesn't support self.scrollsToTop
|
||||
- (BOOL)scrollEnabled
|
||||
{
|
||||
return _shouldBlockPanGesture;
|
||||
@ -48,6 +50,7 @@
|
||||
|
||||
[super setScrollEnabled:YES];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
|
||||
15
AsyncDisplayKit/ASImageNode+tvOS.h
Normal file
15
AsyncDisplayKit/ASImageNode+tvOS.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// ASImageNode+tvOS.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Aaron Schubert on 21/04/2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ASImageNode (tvOS)
|
||||
@end
|
||||
#endif
|
||||
|
||||
184
AsyncDisplayKit/ASImageNode+tvOS.m
Normal file
184
AsyncDisplayKit/ASImageNode+tvOS.m
Normal file
@ -0,0 +1,184 @@
|
||||
//
|
||||
// ASImageNode+tvOS.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Aaron Schubert on 21/04/2016.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#if TARGET_OS_TV
|
||||
#import "ASImageNode+tvOS.h"
|
||||
#import <GLKit/GLKit.h>
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
|
||||
@implementation ASImageNode (tvOS)
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
self.isDefaultFocusAppearance = NO;
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
|
||||
CGSize targetShadowOffset = CGSizeMake(0.0, self.bounds.size.height/8);
|
||||
[layer removeAllAnimations];
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:^{
|
||||
layer.shadowOffset = targetShadowOffset;
|
||||
}];
|
||||
|
||||
CABasicAnimation *shadowOffsetAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOffset"];
|
||||
shadowOffsetAnimation.toValue = [NSValue valueWithCGSize:targetShadowOffset];
|
||||
shadowOffsetAnimation.duration = 0.4;
|
||||
shadowOffsetAnimation.removedOnCompletion = NO;
|
||||
shadowOffsetAnimation.fillMode = kCAFillModeForwards;
|
||||
shadowOffsetAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeOut"];
|
||||
[layer addAnimation:shadowOffsetAnimation forKey:@"shadowOffset"];
|
||||
[CATransaction commit];
|
||||
|
||||
CABasicAnimation *shadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
|
||||
shadowOpacityAnimation.toValue = [NSNumber numberWithFloat:0.45];
|
||||
shadowOpacityAnimation.duration = 0.4;
|
||||
shadowOpacityAnimation.removedOnCompletion = false;
|
||||
shadowOpacityAnimation.fillMode = kCAFillModeForwards;
|
||||
shadowOpacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeOut"];
|
||||
[layer addAnimation:shadowOpacityAnimation forKey:@"shadowOpacityAnimation"];
|
||||
|
||||
view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25);
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesMoved:touches withEvent:event];
|
||||
|
||||
// TODO: Clean up, and improve visuals.
|
||||
|
||||
if (!self.isDefaultFocusAppearance) {
|
||||
// This view may correspond to either self.view
|
||||
// or our superview if we are in a ASCellNode
|
||||
UIView *view = [self getView];
|
||||
|
||||
UITouch *touch = [touches anyObject];
|
||||
// Get the specific point that was touched
|
||||
|
||||
// This is quite messy in it's current state so is not ready for production.
|
||||
// The reason it is here is for others to contribute and to make it clear what is occuring.
|
||||
|
||||
// We get the touch location in self.view because
|
||||
// we are operating in that coordinate system.
|
||||
// BUT we apply our transforms to *view since we want to apply
|
||||
// the transforms to the root view (L: 107)
|
||||
CGPoint point = [touch locationInView:self.view];
|
||||
float pitch = 0;
|
||||
float yaw = 0;
|
||||
BOOL topHalf = NO;
|
||||
if (point.y > CGRectGetHeight(self.view.frame)) {
|
||||
pitch = 15;
|
||||
} else if (point.y < -CGRectGetHeight(self.view.frame)) {
|
||||
pitch = -15;
|
||||
} else {
|
||||
pitch = (point.y/CGRectGetHeight(self.view.frame))*15;
|
||||
}
|
||||
if (pitch < 0) {
|
||||
topHalf = YES;
|
||||
}
|
||||
|
||||
if (point.x > CGRectGetWidth(self.view.frame)) {
|
||||
yaw = 10;
|
||||
} else if (point.x < -CGRectGetWidth(self.view.frame)) {
|
||||
yaw = -10;
|
||||
} else {
|
||||
yaw = (point.x/CGRectGetWidth(self.view.frame))*10;
|
||||
}
|
||||
if (!topHalf) {
|
||||
if (yaw > 0) {
|
||||
yaw = -yaw;
|
||||
} else {
|
||||
yaw = fabsf(yaw);
|
||||
}
|
||||
}
|
||||
|
||||
CATransform3D pitchTransform = CATransform3DMakeRotation(GLKMathDegreesToRadians(pitch),1.0,0.0,0.0);
|
||||
CATransform3D yawTransform = CATransform3DMakeRotation(GLKMathDegreesToRadians(yaw),0.0,1.0,0.0);
|
||||
CATransform3D transform = CATransform3DConcat(pitchTransform, yawTransform);
|
||||
CATransform3D scaleAndTransform = CATransform3DConcat(transform, CATransform3DMakeAffineTransform(CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25)));
|
||||
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
view.layer.transform = scaleAndTransform;
|
||||
}];
|
||||
} else {
|
||||
[self setDefaultFocusAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
[self finishTouches];
|
||||
}
|
||||
|
||||
- (void)finishTouches
|
||||
{
|
||||
if (!self.isDefaultFocusAppearance) {
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
|
||||
CGSize targetShadowOffset = CGSizeMake(0.0, self.bounds.size.height/8);
|
||||
CATransform3D targetScaleTransform = CATransform3DMakeScale(1.2, 1.2, 1.2);
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:^{
|
||||
layer.shadowOffset = targetShadowOffset;
|
||||
}];
|
||||
[CATransaction commit];
|
||||
|
||||
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
|
||||
view.layer.transform = targetScaleTransform;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[layer removeAnimationForKey:@"shadowOffset"];
|
||||
[layer removeAnimationForKey:@"shadowOpacity"];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self setDefaultFocusAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFocusedState
|
||||
{
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
layer.shadowOffset = CGSizeMake(2, 10);
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 12.0;
|
||||
layer.shadowOpacity = 0.45;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25);
|
||||
}
|
||||
|
||||
- (void)setDefaultFocusAppearance
|
||||
{
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
view.transform = CGAffineTransformIdentity;
|
||||
layer.shadowOpacity = 0;
|
||||
layer.shadowOffset = CGSizeZero;
|
||||
layer.shadowRadius = 0;
|
||||
layer.shadowPath = nil;
|
||||
[layer removeAnimationForKey:@"shadowOffset"];
|
||||
[layer removeAnimationForKey:@"shadowOpacity"];
|
||||
self.isDefaultFocusAppearance = YES;
|
||||
}
|
||||
|
||||
- (UIView *)getView
|
||||
{
|
||||
// TODO: This needs to be re-visited to handle all possibilities.
|
||||
// If we are inside a ASCellNode, then we need to apply our focus effects to the ASCellNode view/layer rather than the ASImageNode view/layer.
|
||||
return ASDisplayNodeUltimateParentOfNode(self).view;
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
@ -112,6 +112,15 @@ typedef UIImage * _Nullable (^asimagenode_modification_block_t)(UIImage *image);
|
||||
*/
|
||||
- (void)setNeedsDisplayWithCompletion:(void (^ _Nullable)(BOOL canceled))displayCompletionBlock;
|
||||
|
||||
#if TARGET_OS_TV
|
||||
/**
|
||||
* A bool to track if the current appearance of the node
|
||||
* is the default focus appearance.
|
||||
* Exposed here so the category methods can set it.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL isDefaultFocusAppearance;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@interface ASImageNode (AnimatedImage)
|
||||
|
||||
@ -72,11 +72,6 @@
|
||||
|
||||
void (^_displayCompletionBlock)(BOOL canceled);
|
||||
ASDN::RecursiveMutex _imageLock;
|
||||
|
||||
#if TARGET_OS_TV
|
||||
//tvOS
|
||||
BOOL isDefaultState;
|
||||
#endif
|
||||
|
||||
// Cropping.
|
||||
BOOL _cropEnabled; // Defaults to YES.
|
||||
@ -458,174 +453,6 @@
|
||||
_imageModificationBlock = imageModificationBlock;
|
||||
}
|
||||
|
||||
|
||||
#if TARGET_OS_TV
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
isDefaultState = NO;
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
|
||||
CGSize targetShadowOffset = CGSizeMake(0.0, self.bounds.size.height/8);
|
||||
[layer removeAllAnimations];
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:^{
|
||||
layer.shadowOffset = targetShadowOffset;
|
||||
}];
|
||||
|
||||
CABasicAnimation *shadowOffsetAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOffset"];
|
||||
shadowOffsetAnimation.toValue = [NSValue valueWithCGSize:targetShadowOffset];
|
||||
shadowOffsetAnimation.duration = 0.4;
|
||||
shadowOffsetAnimation.removedOnCompletion = NO;
|
||||
shadowOffsetAnimation.fillMode = kCAFillModeForwards;
|
||||
shadowOffsetAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeOut"];
|
||||
[layer addAnimation:shadowOffsetAnimation forKey:@"shadowOffset"];
|
||||
[CATransaction commit];
|
||||
|
||||
CABasicAnimation *shadowOpacityAnimation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
|
||||
shadowOpacityAnimation.toValue = [NSNumber numberWithFloat:0.45];
|
||||
shadowOpacityAnimation.duration = 0.4;
|
||||
shadowOpacityAnimation.removedOnCompletion = false;
|
||||
shadowOpacityAnimation.fillMode = kCAFillModeForwards;
|
||||
shadowOpacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:@"easeOut"];
|
||||
[layer addAnimation:shadowOpacityAnimation forKey:@"shadowOpacityAnimation"];
|
||||
|
||||
view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25);
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
[super touchesMoved:touches withEvent:event];
|
||||
|
||||
if (!isDefaultState) {
|
||||
UIView *view = [self getView];
|
||||
|
||||
UITouch *touch = [touches anyObject];
|
||||
// Get the specific point that was touched
|
||||
// This is quite messy in it's current state so is not ready for production. The reason it is here is for others to contribute and to make it clear what is occuring.
|
||||
// TODO: Clean up, and improve visuals.
|
||||
CGPoint point = [touch locationInView:self.view];
|
||||
float pitch = 0;
|
||||
float yaw = 0;
|
||||
BOOL topHalf = NO;
|
||||
if (point.y > CGRectGetHeight(self.view.frame)) {
|
||||
pitch = 15;
|
||||
} else if (point.y < -CGRectGetHeight(self.view.frame)) {
|
||||
pitch = -15;
|
||||
} else {
|
||||
pitch = (point.y/CGRectGetHeight(self.view.frame))*15;
|
||||
}
|
||||
if (pitch < 0) {
|
||||
topHalf = YES;
|
||||
}
|
||||
|
||||
if (point.x > CGRectGetWidth(self.view.frame)) {
|
||||
yaw = 10;
|
||||
} else if (point.x < -CGRectGetWidth(self.view.frame)) {
|
||||
yaw = -10;
|
||||
} else {
|
||||
yaw = (point.x/CGRectGetWidth(self.view.frame))*10;
|
||||
}
|
||||
if (!topHalf) {
|
||||
if (yaw > 0) {
|
||||
yaw = -yaw;
|
||||
} else {
|
||||
yaw = fabsf(yaw);
|
||||
}
|
||||
}
|
||||
|
||||
CATransform3D pitchTransform = CATransform3DMakeRotation([self degressToRadians:pitch],1.0,0.0,0.0);
|
||||
CATransform3D yawTransform = CATransform3DMakeRotation([self degressToRadians:yaw],0.0,1.0,0.0);
|
||||
CATransform3D transform = CATransform3DConcat(pitchTransform, yawTransform);
|
||||
CATransform3D scaleAndTransform = CATransform3DConcat(transform, CATransform3DMakeAffineTransform(CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25)));
|
||||
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
view.layer.transform = scaleAndTransform;
|
||||
}];
|
||||
} else {
|
||||
[self setDefaultState];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
[self finishTouches];
|
||||
}
|
||||
|
||||
- (void)finishTouches
|
||||
{
|
||||
if (!isDefaultState) {
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
|
||||
CGSize targetShadowOffset = CGSizeMake(0.0, self.bounds.size.height/8);
|
||||
CATransform3D targetScaleTransform = CATransform3DMakeScale(1.2, 1.2, 1.2);
|
||||
[CATransaction begin];
|
||||
[CATransaction setCompletionBlock:^{
|
||||
layer.shadowOffset = targetShadowOffset;
|
||||
}];
|
||||
[CATransaction commit];
|
||||
|
||||
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
|
||||
view.layer.transform = targetScaleTransform;
|
||||
} completion:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[layer removeAnimationForKey:@"shadowOffset"];
|
||||
[layer removeAnimationForKey:@"shadowOpacity"];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self setDefaultState];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFocusedState
|
||||
{
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
layer.shadowOffset = CGSizeMake(2, 10);
|
||||
layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
layer.shadowRadius = 12.0;
|
||||
layer.shadowOpacity = 0.45;
|
||||
layer.shadowPath = [UIBezierPath bezierPathWithRect:self.layer.bounds].CGPath;
|
||||
view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.25, 1.25);
|
||||
}
|
||||
|
||||
- (void)setDefaultState
|
||||
{
|
||||
UIView *view = [self getView];
|
||||
CALayer *layer = view.layer;
|
||||
view.transform = CGAffineTransformIdentity;
|
||||
layer.shadowOpacity = 0;
|
||||
layer.shadowOffset = CGSizeZero;
|
||||
layer.shadowRadius = 0;
|
||||
layer.shadowPath = nil;
|
||||
[layer removeAnimationForKey:@"shadowOffset"];
|
||||
[layer removeAnimationForKey:@"shadowOpacity"];
|
||||
isDefaultState = YES;
|
||||
}
|
||||
|
||||
- (UIView *)getView
|
||||
{
|
||||
UIView *view = self.view;
|
||||
//If we are inside a ASCellNode, then we need to apply our focus effects to the ASCellNode view/layer rather than the ASImageNode view/layer.
|
||||
if (CGSizeEqualToSize(self.view.superview.frame.size, self.view.frame.size) && self.view.superview.superview) {
|
||||
view = self.view.superview.superview;
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
- (float)degressToRadians:(float)value
|
||||
{
|
||||
return value * M_PI / 180;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark - Debug
|
||||
- (void)layout
|
||||
{
|
||||
|
||||
@ -64,6 +64,5 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)videoNodeWasTapped:(ASVideoNode *)videoNode;
|
||||
@end
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user