mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-04-23 11:27:56 +08:00
Merge remote-tracking branch 'facebook/master' into ASVideoPlayerNode
This commit is contained in:
@@ -342,7 +342,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* This is a node-based UICollectionViewDataSource.
|
||||
*/
|
||||
#define ASCollectionViewDataSource ASCollectionDataSource
|
||||
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource, NSObject>
|
||||
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource>
|
||||
|
||||
@optional
|
||||
|
||||
|
||||
@@ -343,10 +343,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
|
||||
// super.dataSource in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
|
||||
|
||||
super.dataSource = nil;
|
||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong
|
||||
// reference to the old dataSource in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
|
||||
NS_VALID_UNTIL_END_OF_SCOPE id oldDataSource = super.dataSource;
|
||||
|
||||
if (asyncDataSource == nil) {
|
||||
_asyncDataSource = nil;
|
||||
_proxyDataSource = _isDeallocating ? nil : [[ASCollectionViewProxy alloc] initWithTarget:nil interceptor:self];
|
||||
@@ -375,13 +375,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
||||
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
|
||||
// super.delegate in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
|
||||
|
||||
// Order is important here, the asyncDelegate must be callable while nilling super.delegate to avoid random crashes
|
||||
// in UIScrollViewAccessibility.
|
||||
|
||||
super.delegate = nil;
|
||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong
|
||||
// reference to the old delegate in this case because calls to ASCollectionViewProxy will start failing and cause crashes.
|
||||
NS_VALID_UNTIL_END_OF_SCOPE id oldDelegate = super.delegate;
|
||||
|
||||
if (asyncDelegate == nil) {
|
||||
_asyncDelegate = nil;
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
@class ASPagerNode;
|
||||
@class ASPagerFlowLayout;
|
||||
|
||||
@protocol ASPagerNodeDataSource <NSObject>
|
||||
#define ASPagerNodeDataSource ASPagerDataSource
|
||||
@protocol ASPagerDataSource <NSObject>
|
||||
|
||||
/**
|
||||
* This method replaces -collectionView:numberOfItemsInSection:
|
||||
@@ -65,25 +66,30 @@
|
||||
|
||||
@end
|
||||
|
||||
@protocol ASPagerDelegate <ASCollectionDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@interface ASPagerNode : ASCollectionNode
|
||||
|
||||
// Configures a default horizontal, paging flow layout with 0 inter-item spacing.
|
||||
/// Configures a default horizontal, paging flow layout with 0 inter-item spacing.
|
||||
- (instancetype)init;
|
||||
|
||||
// Initializer with custom-configured flow layout properties.
|
||||
/// Initializer with custom-configured flow layout properties.
|
||||
- (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout;
|
||||
|
||||
// Data Source is required, and uses a different protocol from ASCollectionNode.
|
||||
- (void)setDataSource:(id <ASPagerNodeDataSource>)dataSource;
|
||||
- (id <ASPagerNodeDataSource>)dataSource;
|
||||
/// Data Source is required, and uses a different protocol from ASCollectionNode.
|
||||
- (void)setDataSource:(id <ASPagerDataSource>)dataSource;
|
||||
- (id <ASPagerDataSource>)dataSource;
|
||||
|
||||
// Delegate is optional, and uses the same protocol as ASCollectionNode.
|
||||
// This includes UIScrollViewDelegate as well as most methods from UICollectionViewDelegate, like willDisplay...
|
||||
@property (nonatomic, weak) id <ASCollectionDelegate> delegate;
|
||||
@property (nonatomic, weak) id <ASPagerDelegate> delegate;
|
||||
|
||||
// The underlying ASCollectionView object.
|
||||
/// The underlying ASCollectionView object.
|
||||
@property (nonatomic, readonly) ASCollectionView *view;
|
||||
|
||||
/// Scroll the contents of the receiver to ensure that the page is visible.
|
||||
- (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated;
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{
|
||||
ASPagerFlowLayout *_flowLayout;
|
||||
ASPagerNodeProxy *_proxy;
|
||||
__weak id <ASPagerNodeDataSource> _pagerDataSource;
|
||||
__weak id <ASPagerDataSource> _pagerDataSource;
|
||||
BOOL _pagerDataSourceImplementsNodeBlockAtIndex;
|
||||
BOOL _pagerDataSourceImplementsConstrainedSizeForNode;
|
||||
}
|
||||
@@ -111,12 +111,12 @@
|
||||
|
||||
#pragma mark - Data Source Proxy
|
||||
|
||||
- (id <ASPagerNodeDataSource>)dataSource
|
||||
- (id <ASPagerDataSource>)dataSource
|
||||
{
|
||||
return _pagerDataSource;
|
||||
}
|
||||
|
||||
- (void)setDataSource:(id <ASPagerNodeDataSource>)pagerDataSource
|
||||
- (void)setDataSource:(id <ASPagerDataSource>)pagerDataSource
|
||||
{
|
||||
if (pagerDataSource != _pagerDataSource) {
|
||||
_pagerDataSource = pagerDataSource;
|
||||
|
||||
@@ -268,10 +268,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to nil out
|
||||
// super.dataSource in this case because calls to ASTableViewProxy will start failing and cause crashes.
|
||||
|
||||
super.dataSource = nil;
|
||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong
|
||||
// reference to the old dataSource in this case because calls to ASTableViewProxy will start failing and cause crashes.
|
||||
NS_VALID_UNTIL_END_OF_SCOPE id oldDataSource = self.dataSource;
|
||||
|
||||
if (asyncDataSource == nil) {
|
||||
_asyncDataSource = nil;
|
||||
@@ -299,13 +298,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
||||
// will return as nil (ARC magic) even though the _proxyDelegate still exists. It's really important to nil out
|
||||
// super.delegate in this case because calls to ASTableViewProxy will start failing and cause crashes.
|
||||
|
||||
// Order is important here, the asyncDelegate must be callable while nilling super.delegate to avoid random crashes
|
||||
// in UIScrollViewAccessibility.
|
||||
|
||||
super.delegate = nil;
|
||||
// will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong
|
||||
// reference to the old delegate in this case because calls to ASTableViewProxy will start failing and cause crashes.
|
||||
NS_VALID_UNTIL_END_OF_SCOPE id oldDelegate = super.delegate;
|
||||
|
||||
if (asyncDelegate == nil) {
|
||||
_asyncDelegate = nil;
|
||||
|
||||
@@ -34,19 +34,19 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
|
||||
@interface ASTextNode : ASControlNode
|
||||
|
||||
/**
|
||||
@abstract The attributed string to show.
|
||||
@abstract The styled text displayed by the node.
|
||||
@discussion Defaults to nil, no text is shown.
|
||||
For inline image attachments, add an attribute of key NSAttachmentAttributeName, with a value of an NSTextAttachment.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *attributedString;
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;
|
||||
|
||||
#pragma mark - Truncation
|
||||
|
||||
/**
|
||||
@abstract The attributedString to use when the text must be truncated.
|
||||
@abstract The attributedText to use when the text must be truncated.
|
||||
@discussion Defaults to a localized ellipsis character.
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString;
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedText;
|
||||
|
||||
/**
|
||||
@summary The second attributed string appended for truncation.
|
||||
@@ -270,4 +270,28 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* @abstract Text node deprecated properties
|
||||
*/
|
||||
@interface ASTextNode (Deprecated)
|
||||
|
||||
/**
|
||||
The attributedString and attributedText properties are equivalent, but attributedText is now the standard API
|
||||
name in order to match UILabel and ASEditableTextNode.
|
||||
|
||||
@see attributedText
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *attributedString;
|
||||
|
||||
|
||||
/**
|
||||
The truncationAttributedString and truncationAttributedText properties are equivalent, but attributedText is now the
|
||||
standard API name in order to match UILabel and ASEditableTextNode.
|
||||
|
||||
@see truncationAttributedText
|
||||
*/
|
||||
@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -67,7 +67,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
||||
|
||||
NSArray *_exclusionPaths;
|
||||
|
||||
NSAttributedString *_composedTruncationString;
|
||||
NSAttributedString *_composedTruncationText;
|
||||
|
||||
NSString *_highlightedLinkAttributeName;
|
||||
id _highlightedLinkAttributeValue;
|
||||
@@ -105,7 +105,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
self.needsDisplayOnBoundsChange = YES;
|
||||
|
||||
_truncationMode = NSLineBreakByWordWrapping;
|
||||
_composedTruncationString = DefaultTruncationAttributedString();
|
||||
_composedTruncationText = DefaultTruncationAttributedString();
|
||||
|
||||
// The common case is for a text node to be non-opaque and blended over some background.
|
||||
self.opaque = NO;
|
||||
@@ -158,8 +158,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSString *plainString = [[_attributedString string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSString *truncationString = [_composedTruncationString string];
|
||||
NSString *plainString = [[_attributedText string] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
|
||||
NSString *truncationString = [_composedTruncationText string];
|
||||
if (plainString.length > 50)
|
||||
plainString = [[plainString substringToIndex:50] stringByAppendingString:@"\u2026"];
|
||||
return [NSString stringWithFormat:@"<%@: %p; text = \"%@\"; truncation string = \"%@\"; frame = %@; renderer = %p>", self.class, self, plainString, truncationString, self.nodeLoaded ? NSStringFromCGRect(self.layer.frame) : nil, _renderer];
|
||||
@@ -237,8 +237,8 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
- (ASTextKitAttributes)_rendererAttributes
|
||||
{
|
||||
return {
|
||||
.attributedString = _attributedString,
|
||||
.truncationAttributedString = _composedTruncationString,
|
||||
.attributedString = _attributedText,
|
||||
.truncationAttributedString = _composedTruncationText,
|
||||
.lineBreakMode = _truncationMode,
|
||||
.maximumNumberOfLines = _maximumNumberOfLines,
|
||||
.exclusionPaths = _exclusionPaths,
|
||||
@@ -340,10 +340,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
[self setNeedsDisplay];
|
||||
|
||||
CGSize size = [[self _renderer] size];
|
||||
if (self.attributedString.length > 0) {
|
||||
if (_attributedText.length > 0) {
|
||||
CGFloat screenScale = ASScreenScale();
|
||||
self.ascender = round([[_attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||
self.descender = round([[_attributedString attribute:NSFontAttributeName atIndex:_attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
|
||||
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||
self.descender = round([[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
|
||||
if (_renderer.currentScaleFactor > 0 && _renderer.currentScaleFactor < 1.0) {
|
||||
// while not perfect, this is a good estimate of what the ascender of the scaled font will be.
|
||||
self.ascender *= _renderer.currentScaleFactor;
|
||||
@@ -355,28 +355,28 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
|
||||
#pragma mark - Modifying User Text
|
||||
|
||||
- (void)setAttributedString:(NSAttributedString *)attributedString
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText
|
||||
{
|
||||
if (attributedString == nil) {
|
||||
attributedString = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
|
||||
if (attributedText == nil) {
|
||||
attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:nil];
|
||||
}
|
||||
|
||||
if (ASObjectIsEqual(attributedString, _attributedString)) {
|
||||
if (ASObjectIsEqual(attributedText, _attributedText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_attributedString = ASCleanseAttributedStringOfCoreTextAttributes(attributedString);
|
||||
_attributedText = ASCleanseAttributedStringOfCoreTextAttributes(attributedText);
|
||||
|
||||
if (_attributedString.length > 0) {
|
||||
if (_attributedText.length > 0) {
|
||||
CGFloat screenScale = ASScreenScale();
|
||||
self.ascender = round([[_attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||
self.descender = round([[_attributedString attribute:NSFontAttributeName atIndex:_attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
|
||||
self.ascender = round([[_attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
|
||||
self.descender = round([[_attributedText attribute:NSFontAttributeName atIndex:_attributedText.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
|
||||
}
|
||||
|
||||
// Sync the truncation string with attributes from the updated _attributedString
|
||||
// Without this, the size calculation of the text with truncation applied will
|
||||
// not take into account the attributes of attributedString in the last line
|
||||
[self _updateComposedTruncationString];
|
||||
// not take into account the attributes of attributedText in the last line
|
||||
[self _updateComposedTruncationText];
|
||||
|
||||
// We need an entirely new renderer
|
||||
[self _invalidateRenderer];
|
||||
@@ -386,10 +386,10 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
|
||||
[self setNeedsDisplay];
|
||||
|
||||
self.accessibilityLabel = _attributedString.string;
|
||||
self.accessibilityLabel = _attributedText.string;
|
||||
|
||||
// We're an accessibility element by default if there is a string.
|
||||
self.isAccessibilityElement = _attributedString.length != 0;
|
||||
self.isAccessibilityElement = _attributedText.length != 0;
|
||||
}
|
||||
|
||||
#pragma mark - Text Layout
|
||||
@@ -470,7 +470,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
|
||||
{
|
||||
ASTextKitRenderer *renderer = [self _renderer];
|
||||
NSRange visibleRange = renderer.visibleRanges[0];
|
||||
NSAttributedString *attributedString = _attributedString;
|
||||
NSAttributedString *attributedString = _attributedText;
|
||||
NSRange clampedRange = NSIntersectionRange(visibleRange, NSMakeRange(0, attributedString.length));
|
||||
|
||||
// Check in a 9-point region around the actual touch point so we make sure
|
||||
@@ -1023,14 +1023,14 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
return defaultTruncationAttributedString;
|
||||
}
|
||||
|
||||
- (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString
|
||||
- (void)setTruncationAttributedText:(NSAttributedString *)truncationAttributedText
|
||||
{
|
||||
if (ASObjectIsEqual(_truncationAttributedString, truncationAttributedString)) {
|
||||
if (ASObjectIsEqual(_truncationAttributedText, truncationAttributedText)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_truncationAttributedString = [truncationAttributedString copy];
|
||||
[self _invalidateTruncationString];
|
||||
_truncationAttributedText = [truncationAttributedText copy];
|
||||
[self _invalidateTruncationText];
|
||||
}
|
||||
|
||||
- (void)setAdditionalTruncationMessage:(NSAttributedString *)additionalTruncationMessage
|
||||
@@ -1040,7 +1040,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
}
|
||||
|
||||
_additionalTruncationMessage = [additionalTruncationMessage copy];
|
||||
[self _invalidateTruncationString];
|
||||
[self _invalidateTruncationText];
|
||||
}
|
||||
|
||||
- (void)setTruncationMode:(NSLineBreakMode)truncationMode
|
||||
@@ -1055,7 +1055,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
- (BOOL)isTruncated
|
||||
{
|
||||
NSRange visibleRange = [self _renderer].visibleRanges[0];
|
||||
return visibleRange.length < _attributedString.length;
|
||||
return visibleRange.length < _attributedText.length;
|
||||
}
|
||||
|
||||
- (void)setPointSizeScaleFactors:(NSArray *)pointSizeScaleFactors
|
||||
@@ -1082,14 +1082,14 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
|
||||
#pragma mark - Truncation Message
|
||||
|
||||
- (void)_updateComposedTruncationString
|
||||
- (void)_updateComposedTruncationText
|
||||
{
|
||||
_composedTruncationString = [self _prepareTruncationStringForDrawing:[self _composedTruncationString]];
|
||||
_composedTruncationText = [self _prepareTruncationStringForDrawing:[self _composedTruncationText]];
|
||||
}
|
||||
|
||||
- (void)_invalidateTruncationString
|
||||
- (void)_invalidateTruncationText
|
||||
{
|
||||
[self _updateComposedTruncationString];
|
||||
[self _updateComposedTruncationText];
|
||||
[self _invalidateRenderer];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
@@ -1111,7 +1111,7 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
NSUInteger additionalTruncationMessageLength = _additionalTruncationMessage.length;
|
||||
// We get the location of the truncation token, then add the length of the
|
||||
// truncation attributed string +1 for the space between.
|
||||
NSRange range = NSMakeRange(truncationTokenIndex + _truncationAttributedString.length + 1, additionalTruncationMessageLength);
|
||||
NSRange range = NSMakeRange(truncationTokenIndex + _truncationAttributedText.length + 1, additionalTruncationMessageLength);
|
||||
return range;
|
||||
}
|
||||
|
||||
@@ -1120,24 +1120,24 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
* additional truncation message and a truncation attributed string, they will
|
||||
* be properly composed.
|
||||
*/
|
||||
- (NSAttributedString *)_composedTruncationString
|
||||
- (NSAttributedString *)_composedTruncationText
|
||||
{
|
||||
//If we have neither return the default
|
||||
if (!_additionalTruncationMessage && !_truncationAttributedString) {
|
||||
return _composedTruncationString;
|
||||
if (!_additionalTruncationMessage && !_truncationAttributedText) {
|
||||
return _composedTruncationText;
|
||||
}
|
||||
// Short circuit if we only have one or the other.
|
||||
if (!_additionalTruncationMessage) {
|
||||
return _truncationAttributedString;
|
||||
return _truncationAttributedText;
|
||||
}
|
||||
if (!_truncationAttributedString) {
|
||||
if (!_truncationAttributedText) {
|
||||
return _additionalTruncationMessage;
|
||||
}
|
||||
|
||||
// If we've reached this point, both _additionalTruncationMessage and
|
||||
// _truncationAttributedString are present. Compose them.
|
||||
|
||||
NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedString];
|
||||
NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedText];
|
||||
[newComposedTruncationString replaceCharactersInRange:NSMakeRange(newComposedTruncationString.length, 0) withString:@" "];
|
||||
[newComposedTruncationString appendAttributedString:_additionalTruncationMessage];
|
||||
return newComposedTruncationString;
|
||||
@@ -1153,9 +1153,9 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
truncationString = ASCleanseAttributedStringOfCoreTextAttributes(truncationString);
|
||||
NSMutableAttributedString *truncationMutableString = [truncationString mutableCopy];
|
||||
// Grab the attributes from the full string
|
||||
if (_attributedString.length > 0) {
|
||||
NSAttributedString *originalString = _attributedString;
|
||||
NSInteger originalStringLength = _attributedString.length;
|
||||
if (_attributedText.length > 0) {
|
||||
NSAttributedString *originalString = _truncationAttributedText;
|
||||
NSInteger originalStringLength = _truncationAttributedText.length;
|
||||
// Add any of the original string's attributes to the truncation string,
|
||||
// but don't overwrite any of the truncation string's attributes
|
||||
NSDictionary *originalStringAttributes = [originalString attributesAtIndex:originalStringLength-1 effectiveRange:NULL];
|
||||
@@ -1170,3 +1170,27 @@ static NSAttributedString *DefaultTruncationAttributedString()
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASTextNode (Deprecated)
|
||||
|
||||
- (void)setAttributedString:(NSAttributedString *)attributedString
|
||||
{
|
||||
self.attributedText = attributedString;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedString
|
||||
{
|
||||
return self.attributedText;
|
||||
}
|
||||
|
||||
- (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString
|
||||
{
|
||||
self.truncationAttributedText = truncationAttributedString;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)truncationAttributedString
|
||||
{
|
||||
return self.truncationAttributedText;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -64,9 +64,9 @@ static NSString * const kStatus = @"status";
|
||||
|
||||
ASImageNode *_placeholderImageNode; // TODO: Make ASVideoNode an ASImageNode subclass; remove this.
|
||||
|
||||
ASButtonNode *_playButton;
|
||||
ASButtonNode *_playButtonNode;
|
||||
ASDisplayNode *_playerNode;
|
||||
ASDisplayNode *_spinner;
|
||||
ASDisplayNode *_spinnerNode;
|
||||
NSString *_gravity;
|
||||
}
|
||||
|
||||
@@ -185,6 +185,45 @@ static NSString * const kStatus = @"status";
|
||||
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
// All subnodes should taking the whole node frame
|
||||
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 ASVideoNode");
|
||||
maxSize = CGSizeZero;
|
||||
}
|
||||
|
||||
// Stretch out play button, placeholder image player node to the max size
|
||||
NSMutableArray *children = [NSMutableArray array];
|
||||
if (_playButtonNode) {
|
||||
_playButtonNode.preferredFrameSize = maxSize;
|
||||
[children addObject:_playButtonNode];
|
||||
}
|
||||
if (_placeholderImageNode) {
|
||||
_placeholderImageNode.preferredFrameSize = maxSize;
|
||||
[children addObject:_placeholderImageNode];
|
||||
}
|
||||
if (_playerNode) {
|
||||
_playerNode.preferredFrameSize = maxSize;
|
||||
[children addObject:_playerNode];
|
||||
}
|
||||
|
||||
// Center spinner node
|
||||
if (_spinnerNode) {
|
||||
ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode];
|
||||
centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
|
||||
[children addObject:_spinnerNode];
|
||||
}
|
||||
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
||||
}
|
||||
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
@@ -192,17 +231,9 @@ static NSString * const kStatus = @"status";
|
||||
CGRect bounds = self.bounds;
|
||||
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
_placeholderImageNode.frame = bounds;
|
||||
_playerNode.frame = bounds;
|
||||
_playButton.frame = bounds;
|
||||
|
||||
CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2;
|
||||
CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2;
|
||||
_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
|
||||
|
||||
_spinner.bounds = CGRectMake(0, 0, 44, 44);
|
||||
_spinner.position = CGPointMake(bounds.size.width/2, bounds.size.height/2);
|
||||
CGFloat horizontalDiff = (CGRectGetWidth(bounds) - CGRectGetWidth(_playButtonNode.bounds))/2;
|
||||
CGFloat verticalDiff = (CGRectGetHeight(bounds) - CGRectGetHeight(_playButtonNode.bounds))/2;
|
||||
_playButtonNode.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
|
||||
}
|
||||
|
||||
- (void)generatePlaceholderImage
|
||||
@@ -372,6 +403,7 @@ static NSString * const kStatus = @"status";
|
||||
|
||||
|
||||
#pragma mark - Video Properties
|
||||
|
||||
- (void)setPlayerState:(ASVideoNodePlayerState)playerState
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
@@ -387,28 +419,26 @@ static NSString * const kStatus = @"status";
|
||||
}
|
||||
|
||||
_playerState = playerState;
|
||||
|
||||
}
|
||||
|
||||
- (void)setPlayButton:(ASButtonNode *)playButton
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
[_playButton removeTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[_playButton removeFromSupernode];
|
||||
[_playButtonNode removeTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[_playButtonNode removeFromSupernode];
|
||||
|
||||
_playButtonNode = playButton;
|
||||
[_playButtonNode addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
|
||||
_playButton = playButton;
|
||||
|
||||
[self addSubnode:playButton];
|
||||
|
||||
[_playButton addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (ASButtonNode *)playButton
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
return _playButton;
|
||||
return _playButtonNode;
|
||||
}
|
||||
|
||||
- (void)setAsset:(AVAsset *)asset
|
||||
@@ -473,14 +503,12 @@ static NSString * const kStatus = @"status";
|
||||
- (NSString *)gravity
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
return _gravity;
|
||||
}
|
||||
|
||||
- (BOOL)muted
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
return _muted;
|
||||
}
|
||||
|
||||
@@ -509,11 +537,13 @@ static NSString * const kStatus = @"status";
|
||||
if (_playerNode == nil) {
|
||||
_playerNode = [self constructPlayerNode];
|
||||
|
||||
if (_playButton.supernode == self) {
|
||||
[self insertSubnode:_playerNode belowSubnode:_playButton];
|
||||
if (_playButtonNode.supernode == self) {
|
||||
[self insertSubnode:_playerNode belowSubnode:_playButtonNode];
|
||||
} else {
|
||||
[self addSubnode:_playerNode];
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
|
||||
@@ -521,7 +551,7 @@ static NSString * const kStatus = @"status";
|
||||
_shouldBePlaying = YES;
|
||||
|
||||
[UIView animateWithDuration:0.15 animations:^{
|
||||
_playButton.alpha = 0.0;
|
||||
_playButtonNode.alpha = 0.0;
|
||||
}];
|
||||
if (![self ready]) {
|
||||
[self showSpinner];
|
||||
@@ -540,28 +570,29 @@ static NSString * const kStatus = @"status";
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
if (!_spinner) {
|
||||
_spinner = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{
|
||||
if (!_spinnerNode) {
|
||||
_spinnerNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{
|
||||
UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init];
|
||||
spinnnerView.color = [UIColor whiteColor];
|
||||
|
||||
return spinnnerView;
|
||||
}];
|
||||
|
||||
[self addSubnode:_spinner];
|
||||
_spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0);
|
||||
|
||||
[self addSubnode:_spinnerNode];
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
[(UIActivityIndicatorView *)_spinner.view startAnimating];
|
||||
[(UIActivityIndicatorView *)_spinnerNode.view startAnimating];
|
||||
}
|
||||
|
||||
- (void)removeSpinner
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
|
||||
if (!_spinner) {
|
||||
if (!_spinnerNode) {
|
||||
return;
|
||||
}
|
||||
[_spinner removeFromSupernode];
|
||||
_spinner = nil;
|
||||
[_spinnerNode removeFromSupernode];
|
||||
_spinnerNode = nil;
|
||||
}
|
||||
|
||||
- (void)pause
|
||||
@@ -575,7 +606,7 @@ static NSString * const kStatus = @"status";
|
||||
[self removeSpinner];
|
||||
_shouldBePlaying = NO;
|
||||
[UIView animateWithDuration:0.15 animations:^{
|
||||
_playButton.alpha = 1.0;
|
||||
_playButtonNode.alpha = 1.0;
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -636,7 +667,7 @@ static NSString * const kStatus = @"status";
|
||||
- (ASDisplayNode *)spinner
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
return _spinner;
|
||||
return _spinnerNode;
|
||||
}
|
||||
|
||||
- (ASImageNode *)placeholderImageNode
|
||||
@@ -672,6 +703,8 @@ static NSString * const kStatus = @"status";
|
||||
{
|
||||
ASDN::MutexLocker l(_videoLock);
|
||||
_playerNode = playerNode;
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setPlayer:(AVPlayer *)player
|
||||
@@ -700,7 +733,7 @@ static NSString * const kStatus = @"status";
|
||||
{
|
||||
[_player removeTimeObserver:_timeObserver];
|
||||
_timeObserver = nil;
|
||||
[_playButton removeTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[_playButtonNode removeTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
[self removePlayerItemObservers:_currentPlayerItem];
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
- (BOOL)interceptsSelector:(SEL)selector
|
||||
{
|
||||
return (
|
||||
// handled by ASPagerNodeDataSource node<->cell machinery
|
||||
// handled by ASPagerDataSource node<->cell machinery
|
||||
selector == @selector(collectionView:nodeForItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:nodeBlockForItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:numberOfItemsInSection:) ||
|
||||
@@ -125,7 +125,15 @@
|
||||
if (_target) {
|
||||
return [_target respondsToSelector:aSelector] ? _target : nil;
|
||||
} else {
|
||||
[_interceptor proxyTargetHasDeallocated:self];
|
||||
// The _interceptor needs to be nilled out in this scenario. For that a strong reference needs to be created
|
||||
// to be able to nil out the _interceptor but still let it know that the proxy target has deallocated
|
||||
// We have to hold a strong reference to the interceptor as we have to nil it out and call the proxyTargetHasDeallocated
|
||||
// The reason that the interceptor needs to be nilled out is that there maybe a change of a infinite loop, for example
|
||||
// if a method will be called in the proxyTargetHasDeallocated: that again would trigger a whole new forwarding cycle
|
||||
id <ASDelegateProxyInterceptor> interceptor = _interceptor;
|
||||
_interceptor = nil;
|
||||
[interceptor proxyTargetHasDeallocated:self];
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observ
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if ([_containerLayers count]) {
|
||||
NSHashTable *containerLayersToCommit = [_containerLayers copy];
|
||||
[_containerLayers removeAllObjects];
|
||||
NSHashTable *containerLayersToCommit = _containerLayers;
|
||||
_containerLayers = [NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPointerPersonality];
|
||||
|
||||
for (CALayer *containerLayer in containerLayersToCommit) {
|
||||
// Note that the act of committing a transaction may open a new transaction,
|
||||
|
||||
@@ -137,25 +137,30 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
|
||||
- (void)_calculateSize
|
||||
{
|
||||
[self truncater];
|
||||
// if we have no scale factors or an unconstrained width, there is no reason to try to adjust the font size
|
||||
if (isinf(_constrainedSize.width) == NO && [_attributes.pointSizeScaleFactors count] > 0) {
|
||||
_currentScaleFactor = [[self fontSizeAdjuster] scaleFactor];
|
||||
}
|
||||
|
||||
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by
|
||||
// -usedRectForTextContainer:).
|
||||
__block NSTextStorage *scaledTextStorage = nil;
|
||||
BOOL isScaled = [self isScaled];
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
if (isScaled) {
|
||||
if (isScaled) {
|
||||
// apply the string scale before truncating or else we may truncate the string after we've done the work to shrink it.
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage];
|
||||
[ASTextKitFontSizeAdjuster adjustFontSizeForAttributeString:scaledString withScaleFactor:_currentScaleFactor];
|
||||
scaledTextStorage = [[NSTextStorage alloc] initWithAttributedString:scaledString];
|
||||
|
||||
[textStorage removeLayoutManager:layoutManager];
|
||||
[scaledTextStorage addLayoutManager:layoutManager];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
[[self truncater] truncate];
|
||||
|
||||
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by
|
||||
// -usedRectForTextContainer:).
|
||||
[[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
}];
|
||||
|
||||
@@ -251,7 +256,9 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet()
|
||||
|
||||
- (std::vector<NSRange>)visibleRanges
|
||||
{
|
||||
return [self truncater].visibleRanges;
|
||||
ASTextKitTailTruncater *truncater = [self truncater];
|
||||
[truncater truncate];
|
||||
return truncater.visibleRanges;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,8 +30,6 @@
|
||||
_context = context;
|
||||
_truncationAttributedString = truncationAttributedString;
|
||||
_avoidTailTruncationSet = avoidTailTruncationSet;
|
||||
|
||||
[self _truncate];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -153,7 +151,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_truncate
|
||||
- (void)truncate
|
||||
{
|
||||
[_context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
NSUInteger originalStringLength = textStorage.length;
|
||||
|
||||
@@ -33,4 +33,9 @@
|
||||
truncationAttributedString:(NSAttributedString *)truncationAttributedString
|
||||
avoidTailTruncationSet:(NSCharacterSet *)avoidTailTruncationSet;
|
||||
|
||||
/**
|
||||
* Actually do the truncation.
|
||||
*/
|
||||
- (void)truncate;
|
||||
|
||||
@end
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
|
||||
truncationAttributedString:nil
|
||||
avoidTailTruncationSet:nil];
|
||||
[tailTruncater truncate];
|
||||
XCTAssert(NSEqualRanges(textKitVisibleRange, tailTruncater.visibleRanges[0]));
|
||||
}
|
||||
|
||||
@@ -71,6 +72,7 @@
|
||||
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
|
||||
truncationAttributedString:[self _simpleTruncationAttributedString]
|
||||
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@""]];
|
||||
[tailTruncater truncate];
|
||||
__block NSString *drawnString;
|
||||
[context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
drawnString = textStorage.string;
|
||||
@@ -95,7 +97,7 @@
|
||||
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
|
||||
truncationAttributedString:[self _simpleTruncationAttributedString]
|
||||
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]];
|
||||
(void)tailTruncater;
|
||||
[tailTruncater truncate];
|
||||
__block NSString *drawnString;
|
||||
[context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
drawnString = textStorage.string;
|
||||
@@ -120,8 +122,7 @@
|
||||
ASTextKitTailTruncater *tailTruncater = [[ASTextKitTailTruncater alloc] initWithContext:context
|
||||
truncationAttributedString:[self _simpleTruncationAttributedString]
|
||||
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]];
|
||||
// So Xcode doesn't yell at me for an unused var...
|
||||
(void)tailTruncater;
|
||||
[tailTruncater truncate];
|
||||
__block NSString *drawnString;
|
||||
[context performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) {
|
||||
drawnString = textStorage.string;
|
||||
@@ -144,9 +145,9 @@
|
||||
layoutManagerDelegate:nil
|
||||
textStorageCreationBlock:nil];
|
||||
|
||||
XCTAssertNoThrow([[ASTextKitTailTruncater alloc] initWithContext:context
|
||||
XCTAssertNoThrow([[[ASTextKitTailTruncater alloc] initWithContext:context
|
||||
truncationAttributedString:[self _simpleTruncationAttributedString]
|
||||
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]]);
|
||||
avoidTailTruncationSet:[NSCharacterSet characterSetWithCharactersInString:@"."]] truncate]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,7 +44,7 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
@interface ASTextNodeTests : XCTestCase
|
||||
|
||||
@property (nonatomic, readwrite, strong) ASTextNode *textNode;
|
||||
@property (nonatomic, readwrite, copy) NSAttributedString *attributedString;
|
||||
@property (nonatomic, readwrite, copy) NSAttributedString *attributedText;
|
||||
|
||||
@end
|
||||
|
||||
@@ -80,8 +80,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
[mas addAttribute:NSParagraphStyleAttributeName value:lastLinePara
|
||||
range:NSMakeRange(mas.length - 1, 1)];
|
||||
|
||||
_attributedString = mas;
|
||||
_textNode.attributedString = _attributedString;
|
||||
_attributedText = mas;
|
||||
_textNode.attributedText = _attributedText;
|
||||
}
|
||||
|
||||
#pragma mark - ASTextNode
|
||||
@@ -97,8 +97,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
- (void)testSettingTruncationMessage
|
||||
{
|
||||
NSAttributedString *truncation = [[NSAttributedString alloc] initWithString:@"..." attributes:nil];
|
||||
_textNode.truncationAttributedString = truncation;
|
||||
XCTAssertTrue([_textNode.truncationAttributedString isEqualToAttributedString:truncation], @"Failed to set truncation message");
|
||||
_textNode.truncationAttributedText = truncation;
|
||||
XCTAssertTrue([_textNode.truncationAttributedText isEqualToAttributedString:truncation], @"Failed to set truncation message");
|
||||
}
|
||||
|
||||
- (void)testSettingAdditionalTruncationMessage
|
||||
@@ -142,11 +142,11 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
|
||||
- (void)testAccessibility
|
||||
{
|
||||
_textNode.attributedString = _attributedString;
|
||||
_textNode.attributedText = _attributedText;
|
||||
XCTAssertTrue(_textNode.isAccessibilityElement, @"Should be an accessibility element");
|
||||
XCTAssertTrue(_textNode.accessibilityTraits == UIAccessibilityTraitStaticText, @"Should have static text accessibility trait, instead has %llu", _textNode.accessibilityTraits);
|
||||
|
||||
XCTAssertTrue([_textNode.accessibilityLabel isEqualToString:_attributedString.string], @"Accessibility label is incorrectly set to \n%@\n when it should be \n%@\n", _textNode.accessibilityLabel, _attributedString.string);
|
||||
XCTAssertTrue([_textNode.accessibilityLabel isEqualToString:_attributedText.string], @"Accessibility label is incorrectly set to \n%@\n when it should be \n%@\n", _textNode.accessibilityLabel, _attributedText.string);
|
||||
}
|
||||
|
||||
- (void)testLinkAttribute
|
||||
@@ -156,7 +156,7 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
NSString *linkString = @"Link";
|
||||
NSRange linkRange = NSMakeRange(0, linkString.length);
|
||||
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:linkString attributes:@{ linkAttributeName : linkAttributeValue}];
|
||||
_textNode.attributedString = attributedString;
|
||||
_textNode.attributedText = attributedString;
|
||||
_textNode.linkAttributeNames = @[linkAttributeName];
|
||||
|
||||
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
|
||||
@@ -178,7 +178,7 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
NSString *linkString = @"Link notalink";
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:linkString];
|
||||
[attributedString addAttribute:linkAttributeName value:linkAttributeValue range:NSMakeRange(0, 4)];
|
||||
_textNode.attributedString = attributedString;
|
||||
_textNode.attributedText = attributedString;
|
||||
_textNode.linkAttributeNames = @[linkAttributeName];
|
||||
|
||||
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
|
||||
|
||||
@@ -10,101 +10,125 @@
|
||||
*/
|
||||
|
||||
#import "ViewController.h"
|
||||
#import "ASLayoutSpec.h"
|
||||
#import "ASStaticLayoutSpec.h"
|
||||
|
||||
@interface ViewController()<ASVideoNodeDelegate>
|
||||
@property (nonatomic, strong) ASDisplayNode *rootNode;
|
||||
@property (nonatomic, strong) ASVideoNode *guitarVideoNode;
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
#pragma mark - UIViewController
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// Root node for the view controller
|
||||
_rootNode = [ASDisplayNode new];
|
||||
_rootNode.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
|
||||
[self.view addSubnode:self.guitarVideoNode];
|
||||
ASVideoNode *guitarVideoNode = self.guitarVideoNode;
|
||||
[_rootNode addSubnode:self.guitarVideoNode];
|
||||
|
||||
ASVideoNode *nicCageVideo = [self nicCageVideo];
|
||||
[self.view addSubnode:nicCageVideo];
|
||||
ASVideoNode *nicCageVideoNode = self.nicCageVideoNode;
|
||||
[_rootNode addSubnode:nicCageVideoNode];
|
||||
|
||||
ASVideoNode *simonVideo = [self simonVideo];
|
||||
[self.view addSubnode:simonVideo];
|
||||
// Video node with custom play button
|
||||
ASVideoNode *simonVideoNode = self.simonVideoNode;
|
||||
simonVideoNode.playButton = self.playButton;
|
||||
[_rootNode addSubnode:simonVideoNode];
|
||||
|
||||
_rootNode.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
|
||||
guitarVideoNode.layoutPosition = CGPointMake(0, 0);
|
||||
guitarVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
nicCageVideoNode.layoutPosition = CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
nicCageVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
simonVideoNode.layoutPosition = CGPointMake(0, [UIScreen mainScreen].bounds.size.height - ([UIScreen mainScreen].bounds.size.height/3));
|
||||
simonVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode]];
|
||||
};
|
||||
[self.view addSubnode:_rootNode];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
// After all subviews are layed out we have to measure it and move the root node to the right place
|
||||
CGSize viewSize = self.view.bounds.size;
|
||||
[self.rootNode measureWithSizeRange:ASSizeRangeMake(viewSize, viewSize)];
|
||||
[self.rootNode setNeedsLayout];
|
||||
}
|
||||
|
||||
#pragma mark - Getter / Setter
|
||||
|
||||
- (ASVideoNode *)guitarVideoNode;
|
||||
{
|
||||
if (_guitarVideoNode) {
|
||||
return _guitarVideoNode;
|
||||
}
|
||||
|
||||
_guitarVideoNode = [[ASVideoNode alloc] init];
|
||||
|
||||
_guitarVideoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-3045b261-7e93-4492-b7e5-5d6358376c9f-editedLiveAndDie.mov"]];
|
||||
|
||||
_guitarVideoNode.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
_guitarVideoNode.gravity = AVLayerVideoGravityResizeAspectFill;
|
||||
|
||||
_guitarVideoNode.backgroundColor = [UIColor lightGrayColor];
|
||||
|
||||
_guitarVideoNode.periodicTimeObserverTimescale = 1; //Default is 100
|
||||
|
||||
_guitarVideoNode.delegate = self;
|
||||
|
||||
return _guitarVideoNode;
|
||||
}
|
||||
|
||||
- (ASVideoNode *)nicCageVideo;
|
||||
- (ASVideoNode *)nicCageVideoNode;
|
||||
{
|
||||
ASVideoNode *nicCageVideo = [[ASVideoNode alloc] init];
|
||||
ASVideoNode *nicCageVideoNode = [[ASVideoNode alloc] init];
|
||||
nicCageVideoNode.delegate = self;
|
||||
nicCageVideoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||
nicCageVideoNode.gravity = AVLayerVideoGravityResize;
|
||||
nicCageVideoNode.backgroundColor = [UIColor lightGrayColor];
|
||||
nicCageVideoNode.shouldAutorepeat = YES;
|
||||
nicCageVideoNode.shouldAutoplay = YES;
|
||||
nicCageVideoNode.muted = YES;
|
||||
|
||||
nicCageVideo.delegate = self;
|
||||
|
||||
nicCageVideo.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||
|
||||
nicCageVideo.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3, [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
nicCageVideo.gravity = AVLayerVideoGravityResize;
|
||||
|
||||
nicCageVideo.backgroundColor = [UIColor lightGrayColor];
|
||||
nicCageVideo.shouldAutorepeat = YES;
|
||||
nicCageVideo.shouldAutoplay = YES;
|
||||
nicCageVideo.muted = YES;
|
||||
|
||||
return nicCageVideo;
|
||||
return nicCageVideoNode;
|
||||
}
|
||||
|
||||
- (ASVideoNode *)simonVideo;
|
||||
- (ASVideoNode *)simonVideoNode
|
||||
{
|
||||
ASVideoNode *simonVideo = [[ASVideoNode alloc] init];
|
||||
ASVideoNode *simonVideoNode = [[ASVideoNode alloc] init];
|
||||
|
||||
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"simon" ofType:@"mp4"]];
|
||||
simonVideo.asset = [AVAsset assetWithURL:url];
|
||||
simonVideoNode.asset = [AVAsset assetWithURL:url];
|
||||
simonVideoNode.gravity = AVLayerVideoGravityResizeAspect;
|
||||
simonVideoNode.backgroundColor = [UIColor lightGrayColor];
|
||||
simonVideoNode.shouldAutorepeat = YES;
|
||||
simonVideoNode.shouldAutoplay = YES;
|
||||
simonVideoNode.muted = YES;
|
||||
|
||||
simonVideo.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - ([UIScreen mainScreen].bounds.size.height/3), [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||
|
||||
simonVideo.gravity = AVLayerVideoGravityResizeAspect;
|
||||
|
||||
simonVideo.backgroundColor = [UIColor lightGrayColor];
|
||||
simonVideo.shouldAutorepeat = YES;
|
||||
simonVideo.shouldAutoplay = YES;
|
||||
simonVideo.muted = YES;
|
||||
|
||||
return simonVideo;
|
||||
return simonVideoNode;
|
||||
}
|
||||
|
||||
- (ASButtonNode *)playButton;
|
||||
{
|
||||
ASButtonNode *playButton = [[ASButtonNode alloc] init];
|
||||
ASButtonNode *playButtonNode = [[ASButtonNode alloc] init];
|
||||
|
||||
UIImage *image = [UIImage imageNamed:@"playButton@2x.png"];
|
||||
[playButton setImage:image forState:ASControlStateNormal];
|
||||
[playButton measure:CGSizeMake(50, 50)];
|
||||
playButton.bounds = CGRectMake(0, 0, playButton.calculatedSize.width, playButton.calculatedSize.height);
|
||||
playButton.position = CGPointMake([UIScreen mainScreen].bounds.size.width/4, ([UIScreen mainScreen].bounds.size.height/3)/2);
|
||||
[playButton setImage:[UIImage imageNamed:@"playButtonSelected@2x.png"] forState:ASControlStateHighlighted];
|
||||
[playButtonNode setImage:image forState:ASControlStateNormal];
|
||||
[playButtonNode setImage:[UIImage imageNamed:@"playButtonSelected@2x.png"] forState:ASControlStateHighlighted];
|
||||
|
||||
return playButton;
|
||||
// Change placement of play button if necessary
|
||||
//playButtonNode.contentHorizontalAlignment = ASHorizontalAlignmentStart;
|
||||
//playButtonNode.contentVerticalAlignment = ASVerticalAlignmentCenter;
|
||||
|
||||
return playButtonNode;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)videoNodeWasTapped:(ASVideoNode *)videoNode
|
||||
{
|
||||
if (videoNode == self.guitarVideoNode) {
|
||||
@@ -125,6 +149,7 @@
|
||||
}
|
||||
|
||||
#pragma mark - ASVideoNodeDelegate
|
||||
|
||||
- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toSate
|
||||
{
|
||||
//Ignore nicCageVideo
|
||||
|
||||
Reference in New Issue
Block a user