Merge pull request #1096 from facebook/pr/1059

[tvOS] Initial changes to support building AsyncDisplayKit for tvOS.
This commit is contained in:
appleguy
2016-01-23 12:28:43 -08:00
15 changed files with 167 additions and 23 deletions

View File

@@ -47,4 +47,5 @@ Pod::Spec.new do |spec|
} }
spec.ios.deployment_target = '7.0' spec.ios.deployment_target = '7.0'
spec.tvos.deployment_target = '9.0'
end end

View File

@@ -641,7 +641,9 @@ NS_ASSUME_NONNULL_END
@property (atomic, assign) UIViewContentMode contentMode; // default=UIViewContentModeScaleToFill @property (atomic, assign) UIViewContentMode contentMode; // default=UIViewContentModeScaleToFill
@property (atomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes) @property (atomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes)
#if TARGET_OS_IOS
@property (atomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO @property (atomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; // default=NO
#endif
@property (atomic, assign, nullable) CGColorRef shadowColor; // default=opaque rgb black @property (atomic, assign, nullable) CGColorRef shadowColor; // default=opaque rgb black
@property (atomic, assign) CGFloat shadowOpacity; // default=0.0 @property (atomic, assign) CGFloat shadowOpacity; // default=0.0
@property (atomic, assign) CGSize shadowOffset; // default=(0, -3) @property (atomic, assign) CGSize shadowOffset; // default=(0, -3)
@@ -658,6 +660,16 @@ NS_ASSUME_NONNULL_END
- (BOOL)isFirstResponder; - (BOOL)isFirstResponder;
- (BOOL)canPerformAction:(nonnull SEL)action withSender:(nonnull id)sender; - (BOOL)canPerformAction:(nonnull SEL)action withSender:(nonnull id)sender;
#if TARGET_OS_TV
//Focus Engine
- (void)setNeedsFocusUpdate;
- (BOOL)canBecomeFocused;
- (void)updateFocusIfNeeded;
- (void)didUpdateFocusInContext:(nonnull UIFocusUpdateContext *)context withAnimationCoordinator:(nonnull UIFocusAnimationCoordinator *)coordinator;
- (BOOL)shouldUpdateFocusInContext:(nonnull UIFocusUpdateContext *)context;
- (nullable UIView *)preferredFocusedView;
#endif
// Accessibility support // Accessibility support
@property (atomic, assign) BOOL isAccessibilityElement; @property (atomic, assign) BOOL isAccessibilityElement;
@property (nullable, atomic, copy) NSString *accessibilityLabel; @property (nullable, atomic, copy) NSString *accessibilityLabel;

View File

@@ -2312,6 +2312,38 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
return self; return self;
} }
#if TARGET_OS_TV
#pragma mark - UIFocusEnvironment Protocol (tvOS)
- (void)setNeedsFocusUpdate
{
}
- (void)updateFocusIfNeeded
{
}
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
return YES;
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
}
- (UIView *)preferredFocusedView
{
if (self.nodeLoaded) {
return self.view;
} else {
return nil;
}
}
#endif
@end @end
@implementation ASDisplayNode (Debugging) @implementation ASDisplayNode (Debugging)

View File

@@ -163,7 +163,9 @@
_textKitComponents.textView = self.textView; _textKitComponents.textView = self.textView;
//_textKitComponents.textView = NO; // Unfortunately there's a bug here with iOS 7 DP5 that causes the text-view to only be one line high when scrollEnabled is NO. rdar://14729288 //_textKitComponents.textView = NO; // Unfortunately there's a bug here with iOS 7 DP5 that causes the text-view to only be one line high when scrollEnabled is NO. rdar://14729288
_textKitComponents.textView.delegate = self; _textKitComponents.textView.delegate = self;
#if TARGET_OS_IOS
_textKitComponents.textView.editable = YES; _textKitComponents.textView.editable = YES;
#endif
_textKitComponents.textView.typingAttributes = _typingAttributes; _textKitComponents.textView.typingAttributes = _typingAttributes;
_textKitComponents.textView.returnKeyType = _returnKeyType; _textKitComponents.textView.returnKeyType = _returnKeyType;
_textKitComponents.textView.accessibilityHint = _placeholderTextKitComponents.textStorage.string; _textKitComponents.textView.accessibilityHint = _placeholderTextKitComponents.textStorage.string;

View File

@@ -7,6 +7,7 @@
*/ */
#import <AsyncDisplayKit/ASImageNode.h> #import <AsyncDisplayKit/ASImageNode.h>
#if TARGET_OS_IOS
#import <MapKit/MapKit.h> #import <MapKit/MapKit.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@@ -48,3 +49,5 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
#endif

View File

@@ -6,6 +6,7 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#if TARGET_OS_IOS
#import "ASMapNode.h" #import "ASMapNode.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h> #import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h> #import <AsyncDisplayKit/ASDisplayNodeExtras.h>
@@ -246,4 +247,5 @@
} }
} }
} }
@end @end
#endif

View File

@@ -6,9 +6,10 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#if TARGET_OS_IOS
#import <AsyncDisplayKit/ASImageNode.h> #import <AsyncDisplayKit/ASImageNode.h>
#import <AsyncDisplayKit/ASImageProtocols.h> #import <AsyncDisplayKit/ASImageProtocols.h>
#import <Photos/Photos.h> #import <Photos/Photos.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@@ -116,13 +117,14 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) {
*/ */
@property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier; @property (nullable, nonatomic, readonly) ASImageIdentifier displayedImageIdentifier;
#if TARGET_OS_IOS
/** /**
* @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is `nil` (the default), then `PHImageManager.defaultManager` is used. * @abstract The image manager that this image node should use when requesting images from the Photos framework. If this is `nil` (the default), then `PHImageManager.defaultManager` is used.
* @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below. * @see `+[NSURL URLWithAssetLocalIdentifier:targetSize:contentMode:options:]` below.
*/ */
@property (nonatomic, strong) PHImageManager *imageManager; @property (nonatomic, strong) PHImageManager *imageManager;
#endif
@end @end
@@ -229,6 +231,7 @@ didFinishDownloadingImageWithIdentifier:(ASImageIdentifier)imageIdentifier
*/ */
- (nullable NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(ASImageIdentifier)imageIdentifier; - (nullable NSURL *)multiplexImageNode:(ASMultiplexImageNode *)imageNode URLForImageIdentifier:(ASImageIdentifier)imageIdentifier;
#if TARGET_OS_IOS
/** /**
* @abstract A PHAsset for the specific asset local identifier * @abstract A PHAsset for the specific asset local identifier
* @param imageNode The sender. * @param imageNode The sender.
@@ -240,11 +243,11 @@ didFinishDownloadingImageWithIdentifier:(ASImageIdentifier)imageIdentifier
* @return A PHAsset corresponding to `assetLocalIdentifier`, or nil if none is available. * @return A PHAsset corresponding to `assetLocalIdentifier`, or nil if none is available.
*/ */
- (nullable PHAsset *)multiplexImageNode:(ASMultiplexImageNode *)imageNode assetForLocalIdentifier:(NSString *)assetLocalIdentifier; - (nullable PHAsset *)multiplexImageNode:(ASMultiplexImageNode *)imageNode assetForLocalIdentifier:(NSString *)assetLocalIdentifier;
#endif
@end @end
#pragma mark - #pragma mark -
#if TARGET_OS_IOS
@interface NSURL (ASPhotosFrameworkURLs) @interface NSURL (ASPhotosFrameworkURLs)
/** /**
@@ -261,5 +264,8 @@ didFinishDownloadingImageWithIdentifier:(ASImageIdentifier)imageIdentifier
options:(PHImageRequestOptions *)options; options:(PHImageRequestOptions *)options;
@end @end
#endif
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
#endif

View File

@@ -6,12 +6,12 @@
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
*/ */
#import "ASMultiplexImageNode.h" #if TARGET_OS_IOS
#import "ASMultiplexImageNode.h"
#import <AssetsLibrary/AssetsLibrary.h> #import <AssetsLibrary/AssetsLibrary.h>
#import <Photos/Photos.h> #import <Photos/Photos.h>
#import <libkern/OSAtomic.h> #import <libkern/OSAtomic.h>
#import "ASAvailability.h" #import "ASAvailability.h"
@@ -112,6 +112,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
*/ */
- (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock; - (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock;
#if TARGET_OS_IOS
/** /**
@abstract Loads the image corresponding to the given assetURL from the device's Assets Library. @abstract Loads the image corresponding to the given assetURL from the device's Assets Library.
@param imageIdentifier The identifier for the image to be loaded. May not be nil. @param imageIdentifier The identifier for the image to be loaded. May not be nil.
@@ -131,7 +132,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
@param error An error describing why the load failed, if it failed; nil otherwise. @param error An error describing why the load failed, if it failed; nil otherwise.
*/ */
- (void)_loadPHAssetWithRequest:(ASPhotosFrameworkImageRequest *)request identifier:(id)imageIdentifier completion:(void (^)(UIImage *image, NSError *error))completionBlock; - (void)_loadPHAssetWithRequest:(ASPhotosFrameworkImageRequest *)request identifier:(id)imageIdentifier completion:(void (^)(UIImage *image, NSError *error))completionBlock;
#endif
/** /**
@abstract Downloads the image corresponding to the given imageIdentifier from the given URL. @abstract Downloads the image corresponding to the given imageIdentifier from the given URL.
@param imageIdentifier The identifier for the image to be downloaded. May not be nil. @param imageIdentifier The identifier for the image to be downloaded. May not be nil.
@@ -262,7 +263,9 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
_dataSource = dataSource; _dataSource = dataSource;
_dataSourceFlags.image = [_dataSource respondsToSelector:@selector(multiplexImageNode:imageForImageIdentifier:)]; _dataSourceFlags.image = [_dataSource respondsToSelector:@selector(multiplexImageNode:imageForImageIdentifier:)];
_dataSourceFlags.URL = [_dataSource respondsToSelector:@selector(multiplexImageNode:URLForImageIdentifier:)]; _dataSourceFlags.URL = [_dataSource respondsToSelector:@selector(multiplexImageNode:URLForImageIdentifier:)];
#if TARGET_OS_IOS
_dataSourceFlags.asset = [_dataSource respondsToSelector:@selector(multiplexImageNode:assetForLocalIdentifier:)]; _dataSourceFlags.asset = [_dataSource respondsToSelector:@selector(multiplexImageNode:assetForLocalIdentifier:)];
#endif
} }
#pragma mark - #pragma mark -
@@ -455,6 +458,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
return; return;
} }
#if TARGET_OS_IOS
// If it's an assets-library URL, we need to fetch it from the assets library. // If it's an assets-library URL, we need to fetch it from the assets library.
if ([[nextImageURL scheme] isEqualToString:kAssetsLibraryURLScheme]) { if ([[nextImageURL scheme] isEqualToString:kAssetsLibraryURLScheme]) {
// Load the asset. // Load the asset.
@@ -470,6 +474,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
finishedLoadingBlock(image, nextImageIdentifier, error); finishedLoadingBlock(image, nextImageIdentifier, error);
}]; }];
} }
#endif
else // Otherwise, it's a web URL that we can download. else // Otherwise, it's a web URL that we can download.
{ {
// First, check the cache. // First, check the cache.
@@ -499,7 +504,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}]; }];
} }
} }
#if TARGET_OS_IOS
- (void)_loadALAssetWithIdentifier:(id)imageIdentifier URL:(NSURL *)assetURL completion:(void (^)(UIImage *image, NSError *error))completionBlock - (void)_loadALAssetWithIdentifier:(id)imageIdentifier URL:(NSURL *)assetURL completion:(void (^)(UIImage *image, NSError *error))completionBlock
{ {
ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required");
@@ -609,7 +614,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
_phImageRequestOperation = newImageRequestOp; _phImageRequestOperation = newImageRequestOp;
[phImageRequestQueue addOperation:newImageRequestOp]; [phImageRequestQueue addOperation:newImageRequestOp];
} }
#endif
- (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock - (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock
{ {
ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required"); ASDisplayNodeAssertNotNil(imageIdentifier, @"imageIdentifier is required");
@@ -708,7 +713,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
} }
@end @end
#if TARGET_OS_IOS
@implementation NSURL (ASPhotosFrameworkURLs) @implementation NSURL (ASPhotosFrameworkURLs)
+ (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(PHImageRequestOptions *)options + (NSURL *)URLWithAssetLocalIdentifier:(NSString *)assetLocalIdentifier targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(PHImageRequestOptions *)options
@@ -720,4 +725,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
return request.url; return request.url;
} }
@end @end
#endif
#endif

View File

@@ -48,12 +48,13 @@
[super didLoad]; [super didLoad];
ASCollectionView *cv = self.view; ASCollectionView *cv = self.view;
#if TARGET_OS_IOS
cv.pagingEnabled = YES; cv.pagingEnabled = YES;
cv.scrollsToTop = NO;
#endif
cv.allowsSelection = NO; cv.allowsSelection = NO;
cv.showsVerticalScrollIndicator = NO; cv.showsVerticalScrollIndicator = NO;
cv.showsHorizontalScrollIndicator = NO; cv.showsHorizontalScrollIndicator = NO;
cv.scrollsToTop = NO;
// Zeroing contentInset is important, as UIKit will set the top inset for the navigation bar even though // Zeroing contentInset is important, as UIKit will set the top inset for the navigation bar even though
// our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning. // our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning.

View File

@@ -73,8 +73,9 @@ NS_ASSUME_NONNULL_BEGIN
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath; - (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
#if TARGET_OS_IOS
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath; - (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath;
#endif
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath; - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath; - (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath;

View File

@@ -5,7 +5,7 @@
// Created by Adlai Holler on 9/25/15. // Created by Adlai Holler on 9/25/15.
// Copyright © 2015 Facebook. All rights reserved. // Copyright © 2015 Facebook. All rights reserved.
// //
#if TARGET_OS_IOS
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <Photos/Photos.h> #import <Photos/Photos.h>
@@ -64,3 +64,4 @@ extern NSString *const ASPhotosURLScheme;
@end @end
// NS_ASSUME_NONNULL_END // NS_ASSUME_NONNULL_END
#endif

View File

@@ -5,7 +5,7 @@
// Created by Adlai Holler on 9/25/15. // Created by Adlai Holler on 9/25/15.
// Copyright © 2015 Facebook. All rights reserved. // Copyright © 2015 Facebook. All rights reserved.
// //
#if TARGET_OS_IOS
#import "ASPhotosFrameworkImageRequest.h" #import "ASPhotosFrameworkImageRequest.h"
#import "ASBaseDefines.h" #import "ASBaseDefines.h"
#import "ASAvailability.h" #import "ASAvailability.h"
@@ -159,3 +159,4 @@ static NSString *const _ASPhotosURLQueryKeyCropHeight = @"crop_h";
} }
@end @end
#endif

View File

@@ -331,4 +331,36 @@
return _node; return _node;
} }
#if TARGET_OS_TV
#pragma mark - tvOS
- (BOOL)canBecomeFocused
{
return [_node canBecomeFocused];
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
return [_node didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
}
- (void)setNeedsFocusUpdate
{
return [_node setNeedsFocusUpdate];
}
- (void)updateFocusIfNeeded
{
return [_node updateFocusIfNeeded];
}
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
return [_node shouldUpdateFocusInContext:context];
}
- (UIView *)preferredFocusedView
{
return [_node preferredFocusedView];
}
#endif
@end @end

View File

@@ -81,6 +81,46 @@
return YES; return YES;
} }
#if TARGET_OS_TV
// Focus Engine
- (BOOL)canBecomeFocused
{
return YES;
}
- (void)setNeedsFocusUpdate
{
ASDisplayNodeAssertMainThread();
[_view setNeedsFocusUpdate];
}
- (void)updateFocusIfNeeded
{
ASDisplayNodeAssertMainThread();
[_view updateFocusIfNeeded];
}
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
return YES;
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
}
- (UIView *)preferredFocusedView
{
if (self.nodeLoaded) {
return _view;
}
else {
return nil;
}
}
#endif
- (BOOL)isFirstResponder - (BOOL)isFirstResponder
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
@@ -298,7 +338,7 @@
_bridge_prologue; _bridge_prologue;
_setToViewOnly(userInteractionEnabled, enabled); _setToViewOnly(userInteractionEnabled, enabled);
} }
#if TARGET_OS_IOS
- (BOOL)isExclusiveTouch - (BOOL)isExclusiveTouch
{ {
_bridge_prologue; _bridge_prologue;
@@ -310,7 +350,7 @@
_bridge_prologue; _bridge_prologue;
_setToViewOnly(exclusiveTouch, exclusiveTouch); _setToViewOnly(exclusiveTouch, exclusiveTouch);
} }
#endif
- (BOOL)clipsToBounds - (BOOL)clipsToBounds
{ {
_bridge_prologue; _bridge_prologue;

View File

@@ -716,9 +716,11 @@ static UIColor *defaultTintColor = nil;
if (_flags.setUserInteractionEnabled) if (_flags.setUserInteractionEnabled)
view.userInteractionEnabled = userInteractionEnabled; view.userInteractionEnabled = userInteractionEnabled;
#if TARGET_OS_IOS
if (_flags.setExclusiveTouch) if (_flags.setExclusiveTouch)
view.exclusiveTouch = exclusiveTouch; view.exclusiveTouch = exclusiveTouch;
#endif
if (_flags.setShadowColor) if (_flags.setShadowColor)
layer.shadowColor = shadowColor; layer.shadowColor = shadowColor;
@@ -943,10 +945,10 @@ static UIColor *defaultTintColor = nil;
pendingState.userInteractionEnabled = view.userInteractionEnabled; pendingState.userInteractionEnabled = view.userInteractionEnabled;
(pendingState->_flags).setUserInteractionEnabled = YES; (pendingState->_flags).setUserInteractionEnabled = YES;
#if TARGET_OS_IOS
pendingState.exclusiveTouch = view.exclusiveTouch; pendingState.exclusiveTouch = view.exclusiveTouch;
(pendingState->_flags).setExclusiveTouch = YES; (pendingState->_flags).setExclusiveTouch = YES;
#endif
pendingState.shadowColor = layer.shadowColor; pendingState.shadowColor = layer.shadowColor;
(pendingState->_flags).setShadowColor = YES; (pendingState->_flags).setShadowColor = YES;