mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-14 10:20:11 +00:00
Merge branch 'master' into update-objc
Conflicts: AsyncDisplayKit/ASDisplayNode+Subclasses.h
This commit is contained in:
commit
b16a9e294e
@ -21,8 +21,9 @@ typedef NSUInteger ASCellNodeAnimation;
|
|||||||
* The notification is done on main thread.
|
* The notification is done on main thread.
|
||||||
*
|
*
|
||||||
* @param node A node informing the delegate about the relayout.
|
* @param node A node informing the delegate about the relayout.
|
||||||
|
* @param sizeChanged `YES` if the node's `calculatedSize` changed during the relayout, `NO` otherwise.
|
||||||
*/
|
*/
|
||||||
- (void)nodeDidRelayout:(ASCellNode *)node;
|
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -53,11 +53,13 @@
|
|||||||
- (void)setNeedsLayout
|
- (void)setNeedsLayout
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertThreadAffinity(self);
|
ASDisplayNodeAssertThreadAffinity(self);
|
||||||
|
CGSize oldSize = self.calculatedSize;
|
||||||
[super setNeedsLayout];
|
[super setNeedsLayout];
|
||||||
|
|
||||||
if (_layoutDelegate != nil) {
|
if (_layoutDelegate != nil) {
|
||||||
|
BOOL sizeChanged = !CGSizeEqualToSize(oldSize, self.calculatedSize);
|
||||||
ASPerformBlockOnMainThread(^{
|
ASPerformBlockOnMainThread(^{
|
||||||
[_layoutDelegate nodeDidRelayout:self];
|
[_layoutDelegate nodeDidRelayout:self sizeChanged:sizeChanged];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -156,6 +156,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
BOOL _asyncDelegateImplementsInsetSection;
|
BOOL _asyncDelegateImplementsInsetSection;
|
||||||
BOOL _collectionViewLayoutImplementsInsetSection;
|
BOOL _collectionViewLayoutImplementsInsetSection;
|
||||||
BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
|
BOOL _asyncDataSourceImplementsConstrainedSizeForNode;
|
||||||
|
BOOL _queuedNodeSizeUpdate;
|
||||||
|
|
||||||
ASBatchContext *_batchContext;
|
ASBatchContext *_batchContext;
|
||||||
|
|
||||||
@ -911,10 +912,26 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
|
|
||||||
#pragma mark - ASCellNodeDelegate
|
#pragma mark - ASCellNodeDelegate
|
||||||
|
|
||||||
- (void)nodeDidRelayout:(ASCellNode *)node
|
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
// Cause UICollectionView to requery for the new height of this node
|
|
||||||
|
if (!sizeChanged || _queuedNodeSizeUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_queuedNodeSizeUpdate = YES;
|
||||||
|
[self performSelector:@selector(requeryNodeSizes)
|
||||||
|
withObject:nil
|
||||||
|
afterDelay:0
|
||||||
|
inModes:@[ NSRunLoopCommonModes ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause UICollectionView to requery for the new size of all nodes
|
||||||
|
- (void)requeryNodeSizes
|
||||||
|
{
|
||||||
|
_queuedNodeSizeUpdate = NO;
|
||||||
|
|
||||||
[super performBatchUpdates:^{} completion:nil];
|
[super performBatchUpdates:^{} completion:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -368,7 +368,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called just before the view is added to a superview.
|
* Called just before the view is added to a window.
|
||||||
*/
|
*/
|
||||||
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
|
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
|
||||||
|
|
||||||
@ -428,9 +428,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@interface ASDisplayNode (ASDisplayNodePrivate)
|
@interface ASDisplayNode (ASDisplayNodePrivate)
|
||||||
// This method has proven helpful in a few rare scenarios, similar to a category extension on UIView,
|
/**
|
||||||
// but it's considered private API for now and its use should not be encouraged.
|
* This method has proven helpful in a few rare scenarios, similar to a category extension on UIView,
|
||||||
- (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass;
|
* but it's considered private API for now and its use should not be encouraged.
|
||||||
|
* @param checkViewHierarchy If YES, and no supernode can be found, method will walk up from `self.view` to find a supernode.
|
||||||
|
* If YES, this method must be called on the main thread and the node must not be layer-backed.
|
||||||
|
*/
|
||||||
|
- (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy;
|
||||||
|
|
||||||
// The two methods below will eventually be exposed, but their names are subject to change.
|
// The two methods below will eventually be exposed, but their names are subject to change.
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -43,18 +43,28 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node);
|
|||||||
typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
|
typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
|
||||||
{
|
{
|
||||||
/** The element is not predicted to be onscreen soon and preloading should not be performed */
|
/** The element is not predicted to be onscreen soon and preloading should not be performed */
|
||||||
ASInterfaceStateNone = 1 << 0,
|
ASInterfaceStateNone = 0,
|
||||||
/** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */
|
/** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */
|
||||||
ASInterfaceStateMeasureLayout = 1 << 1,
|
ASInterfaceStateMeasureLayout = 1 << 0,
|
||||||
/** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */
|
/** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */
|
||||||
ASInterfaceStateFetchData = 1 << 2,
|
ASInterfaceStateFetchData = 1 << 1,
|
||||||
/** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */
|
/** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */
|
||||||
ASInterfaceStateDisplay = 1 << 3,
|
ASInterfaceStateDisplay = 1 << 2,
|
||||||
/** The element is physically onscreen by at least 1 pixel.
|
/** The element is physically onscreen by at least 1 pixel.
|
||||||
In practice, all other bit fields should also be set when this flag is set. */
|
In practice, all other bit fields should also be set when this flag is set. */
|
||||||
ASInterfaceStateVisible = 1 << 4,
|
ASInterfaceStateVisible = 1 << 3,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The node is not contained in a cell but it is in a window.
|
||||||
|
*
|
||||||
|
* Currently we only set `interfaceState` to other values for
|
||||||
|
* nodes contained in table views or collection views.
|
||||||
|
*/
|
||||||
|
ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStateFetchData | ASInterfaceStateDisplay | ASInterfaceStateVisible,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view
|
* An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view
|
||||||
* hierarchy off the main thread, and could do rendering off the main thread as well.
|
* hierarchy off the main thread, and could do rendering off the main thread as well.
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
#import "ASInternalHelpers.h"
|
#import "ASInternalHelpers.h"
|
||||||
#import "ASLayout.h"
|
#import "ASLayout.h"
|
||||||
#import "ASLayoutSpec.h"
|
#import "ASLayoutSpec.h"
|
||||||
|
#import "ASCellNode.h"
|
||||||
|
|
||||||
@interface ASDisplayNode () <UIGestureRecognizerDelegate>
|
@interface ASDisplayNode () <UIGestureRecognizerDelegate>
|
||||||
|
|
||||||
@ -1581,6 +1582,10 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
ASDisplayNodeAssert(_flags.isEnteringHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
|
ASDisplayNodeAssert(_flags.isEnteringHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
|
||||||
ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
||||||
|
|
||||||
|
if (![self supportsInterfaceState]) {
|
||||||
|
self.interfaceState = ASInterfaceStateInHierarchy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didExitHierarchy
|
- (void)didExitHierarchy
|
||||||
@ -1588,6 +1593,10 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
|
ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
|
||||||
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
|
||||||
|
|
||||||
|
if (![self supportsInterfaceState]) {
|
||||||
|
self.interfaceState = ASInterfaceStateNone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)clearContents
|
- (void)clearContents
|
||||||
@ -1635,6 +1644,20 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
[self clearFetchedData];
|
[self clearFetchedData];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We currently only set interface state on nodes
|
||||||
|
* in table/collection views. For other nodes, if they are
|
||||||
|
* in the hierarchy we return `Unknown`, otherwise we return `None`.
|
||||||
|
*
|
||||||
|
* TODO: Avoid traversing up node hierarchy due to possible deadlock.
|
||||||
|
* @see https://github.com/facebook/AsyncDisplayKit/issues/900
|
||||||
|
* Possible solution is to push `isInCellNode` state downward on `addSubnode`/`removeFromSupernode`.
|
||||||
|
*/
|
||||||
|
- (BOOL)supportsInterfaceState {
|
||||||
|
return ([self isKindOfClass:ASCellNode.class]
|
||||||
|
|| [self _supernodeWithClass:ASCellNode.class checkViewHierarchy:NO] != nil);
|
||||||
|
}
|
||||||
|
|
||||||
- (ASInterfaceState)interfaceState
|
- (ASInterfaceState)interfaceState
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
@ -1642,15 +1665,21 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)setInterfaceState:(ASInterfaceState)interfaceState
|
- (void)setInterfaceState:(ASInterfaceState)interfaceState
|
||||||
|
{
|
||||||
|
ASInterfaceState oldValue;
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_propertyLock);
|
ASDN::MutexLocker l(_propertyLock);
|
||||||
if (interfaceState != _interfaceState) {
|
oldValue = _interfaceState;
|
||||||
if ((interfaceState & ASInterfaceStateMeasureLayout) != (_interfaceState & ASInterfaceStateMeasureLayout)) {
|
_interfaceState = interfaceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interfaceState != oldValue) {
|
||||||
|
if ((interfaceState & ASInterfaceStateMeasureLayout) != (oldValue & ASInterfaceStateMeasureLayout)) {
|
||||||
// Trigger asynchronous measurement if it is not already cached or being calculated.
|
// Trigger asynchronous measurement if it is not already cached or being calculated.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entered or exited data loading state.
|
// Entered or exited data loading state.
|
||||||
if ((interfaceState & ASInterfaceStateFetchData) != (_interfaceState & ASInterfaceStateFetchData)) {
|
if ((interfaceState & ASInterfaceStateFetchData) != (oldValue & ASInterfaceStateFetchData)) {
|
||||||
if (interfaceState & ASInterfaceStateFetchData) {
|
if (interfaceState & ASInterfaceStateFetchData) {
|
||||||
[self fetchData];
|
[self fetchData];
|
||||||
} else {
|
} else {
|
||||||
@ -1659,7 +1688,7 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Entered or exited contents rendering state.
|
// Entered or exited contents rendering state.
|
||||||
if ((interfaceState & ASInterfaceStateDisplay) != (_interfaceState & ASInterfaceStateDisplay)) {
|
if ((interfaceState & ASInterfaceStateDisplay) != (oldValue & ASInterfaceStateDisplay)) {
|
||||||
if (interfaceState & ASInterfaceStateDisplay) {
|
if (interfaceState & ASInterfaceStateDisplay) {
|
||||||
// Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
|
// Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
|
||||||
[self setDisplaySuspended:NO];
|
[self setDisplaySuspended:NO];
|
||||||
@ -1670,7 +1699,7 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Entered or exited data loading state.
|
// Entered or exited data loading state.
|
||||||
if ((interfaceState & ASInterfaceStateVisible) != (_interfaceState & ASInterfaceStateVisible)) {
|
if ((interfaceState & ASInterfaceStateVisible) != (oldValue & ASInterfaceStateVisible)) {
|
||||||
if (interfaceState & ASInterfaceStateVisible) {
|
if (interfaceState & ASInterfaceStateVisible) {
|
||||||
// Consider providing a -didBecomeVisible.
|
// Consider providing a -didBecomeVisible.
|
||||||
} else {
|
} else {
|
||||||
@ -1678,7 +1707,6 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_interfaceState = interfaceState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1871,7 +1899,7 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
|
|
||||||
// This method has proved helpful in a few rare scenarios, similar to a category extension on UIView, but assumes knowledge of _ASDisplayView.
|
// This method has proved helpful in a few rare scenarios, similar to a category extension on UIView, but assumes knowledge of _ASDisplayView.
|
||||||
// It's considered private API for now and its use should not be encouraged.
|
// It's considered private API for now and its use should not be encouraged.
|
||||||
- (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass
|
- (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy
|
||||||
{
|
{
|
||||||
ASDisplayNode *supernode = self.supernode;
|
ASDisplayNode *supernode = self.supernode;
|
||||||
while (supernode) {
|
while (supernode) {
|
||||||
@ -1879,6 +1907,9 @@ void recursivelyEnsureDisplayForLayer(CALayer *layer)
|
|||||||
return supernode;
|
return supernode;
|
||||||
supernode = supernode.supernode;
|
supernode = supernode.supernode;
|
||||||
}
|
}
|
||||||
|
if (!checkViewHierarchy) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
UIView *view = self.view.superview;
|
UIView *view = self.view.superview;
|
||||||
while (view) {
|
while (view) {
|
||||||
|
|||||||
@ -181,6 +181,7 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
|
|
||||||
CGFloat _nodesConstrainedWidth;
|
CGFloat _nodesConstrainedWidth;
|
||||||
BOOL _ignoreNodesConstrainedWidthChange;
|
BOOL _ignoreNodesConstrainedWidthChange;
|
||||||
|
BOOL _queuedNodeHeightUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
||||||
@ -908,10 +909,26 @@ static BOOL _isInterceptedSelector(SEL sel)
|
|||||||
|
|
||||||
#pragma mark - ASCellNodeLayoutDelegate
|
#pragma mark - ASCellNodeLayoutDelegate
|
||||||
|
|
||||||
- (void)nodeDidRelayout:(ASCellNode *)node
|
- (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
|
if (!sizeChanged || _queuedNodeHeightUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_queuedNodeHeightUpdate = YES;
|
||||||
|
[self performSelector:@selector(requeryNodeHeights)
|
||||||
|
withObject:nil
|
||||||
|
afterDelay:0
|
||||||
|
inModes:@[ NSRunLoopCommonModes ]];
|
||||||
|
}
|
||||||
|
|
||||||
// Cause UITableView to requery for the new height of this node
|
// Cause UITableView to requery for the new height of this node
|
||||||
|
- (void)requeryNodeHeights
|
||||||
|
{
|
||||||
|
_queuedNodeHeightUpdate = NO;
|
||||||
|
|
||||||
[super beginUpdates];
|
[super beginUpdates];
|
||||||
[super endUpdates];
|
[super endUpdates];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -247,7 +247,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
|
|||||||
@param attribute The attribute that was tapped. Will not be nil.
|
@param attribute The attribute that was tapped. Will not be nil.
|
||||||
@param value The value of the tapped attribute.
|
@param value The value of the tapped attribute.
|
||||||
@param point The point within textNode, in textNode's coordinate system, that was touched to trigger a highlight.
|
@param point The point within textNode, in textNode's coordinate system, that was touched to trigger a highlight.
|
||||||
@discussion If not implemented, the default value is NO.
|
@discussion If not implemented, the default value is YES.
|
||||||
@return YES if the entity attribute should be a link, NO otherwise.
|
@return YES if the entity attribute should be a link, NO otherwise.
|
||||||
*/
|
*/
|
||||||
- (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point;
|
- (BOOL)textNode:(ASTextNode *)textNode shouldHighlightLinkAttribute:(NSString *)attribute value:(id)value atPoint:(CGPoint)point;
|
||||||
|
|||||||
@ -394,13 +394,15 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
return [self _linkAttributeValueAtPoint:point
|
return [self _linkAttributeValueAtPoint:point
|
||||||
attributeName:attributeNameOut
|
attributeName:attributeNameOut
|
||||||
range:rangeOut
|
range:rangeOut
|
||||||
inAdditionalTruncationMessage:NULL];
|
inAdditionalTruncationMessage:NULL
|
||||||
|
forHighlighting:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)_linkAttributeValueAtPoint:(CGPoint)point
|
- (id)_linkAttributeValueAtPoint:(CGPoint)point
|
||||||
attributeName:(out NSString **)attributeNameOut
|
attributeName:(out NSString **)attributeNameOut
|
||||||
range:(out NSRange *)rangeOut
|
range:(out NSRange *)rangeOut
|
||||||
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut
|
inAdditionalTruncationMessage:(out BOOL *)inAdditionalTruncationMessageOut
|
||||||
|
forHighlighting:(BOOL)highlighting
|
||||||
{
|
{
|
||||||
ASTextKitRenderer *renderer = [self _renderer];
|
ASTextKitRenderer *renderer = [self _renderer];
|
||||||
NSRange visibleRange = renderer.visibleRanges[0];
|
NSRange visibleRange = renderer.visibleRanges[0];
|
||||||
@ -453,10 +455,10 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if delegate implements optional method, if not assume NO.
|
// If highlighting, check with delegate first. If not implemented, assume YES.
|
||||||
// Should the text be highlightable/touchable?
|
if (highlighting
|
||||||
if (![_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)] ||
|
&& [_delegate respondsToSelector:@selector(textNode:shouldHighlightLinkAttribute:value:atPoint:)]
|
||||||
![_delegate textNode:self shouldHighlightLinkAttribute:name value:value atPoint:point]) {
|
&& ![_delegate textNode:self shouldHighlightLinkAttribute:name value:value atPoint:point]) {
|
||||||
value = nil;
|
value = nil;
|
||||||
name = nil;
|
name = nil;
|
||||||
}
|
}
|
||||||
@ -758,7 +760,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
id linkAttributeValue = [self _linkAttributeValueAtPoint:point
|
id linkAttributeValue = [self _linkAttributeValueAtPoint:point
|
||||||
attributeName:&linkAttributeName
|
attributeName:&linkAttributeName
|
||||||
range:&range
|
range:&range
|
||||||
inAdditionalTruncationMessage:&inAdditionalTruncationMessage];
|
inAdditionalTruncationMessage:&inAdditionalTruncationMessage
|
||||||
|
forHighlighting:YES];
|
||||||
|
|
||||||
NSUInteger lastCharIndex = NSIntegerMax;
|
NSUInteger lastCharIndex = NSIntegerMax;
|
||||||
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1);
|
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1);
|
||||||
@ -778,11 +781,7 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
|
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
UITouch *touch = [touches anyObject];
|
CGPoint point = [[touches anyObject] locationInView:self.view];
|
||||||
|
|
||||||
UIView *view = touch.view;
|
|
||||||
CGPoint point = [touch locationInView:view];
|
|
||||||
point = [self.view convertPoint:point fromView:view];
|
|
||||||
|
|
||||||
NSRange range = NSMakeRange(0, 0);
|
NSRange range = NSMakeRange(0, 0);
|
||||||
NSString *linkAttributeName = nil;
|
NSString *linkAttributeName = nil;
|
||||||
@ -791,7 +790,8 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
id linkAttributeValue = [self _linkAttributeValueAtPoint:point
|
id linkAttributeValue = [self _linkAttributeValueAtPoint:point
|
||||||
attributeName:&linkAttributeName
|
attributeName:&linkAttributeName
|
||||||
range:&range
|
range:&range
|
||||||
inAdditionalTruncationMessage:&inAdditionalTruncationMessage];
|
inAdditionalTruncationMessage:&inAdditionalTruncationMessage
|
||||||
|
forHighlighting:YES];
|
||||||
|
|
||||||
NSUInteger lastCharIndex = NSIntegerMax;
|
NSUInteger lastCharIndex = NSIntegerMax;
|
||||||
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1);
|
BOOL linkCrossesVisibleRange = (lastCharIndex > range.location) && (lastCharIndex < NSMaxRange(range) - 1);
|
||||||
@ -835,8 +835,21 @@ static NSString *ASTextNodeTruncationTokenAttributeName = @"ASTextNodeTruncation
|
|||||||
{
|
{
|
||||||
[super touchesMoved:touches withEvent:event];
|
[super touchesMoved:touches withEvent:event];
|
||||||
|
|
||||||
|
// If touch has moved out of the current highlight range, clear the highlight.
|
||||||
|
if (_highlightRange.length > 0) {
|
||||||
|
NSRange range = NSMakeRange(0, 0);
|
||||||
|
CGPoint point = [[touches anyObject] locationInView:self.view];
|
||||||
|
[self _linkAttributeValueAtPoint:point
|
||||||
|
attributeName:NULL
|
||||||
|
range:&range
|
||||||
|
inAdditionalTruncationMessage:NULL
|
||||||
|
forHighlighting:YES];
|
||||||
|
|
||||||
|
if (!NSEqualRanges(_highlightRange, range)) {
|
||||||
[self _clearHighlightIfNecessary];
|
[self _clearHighlightIfNecessary];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
|
- (void)_handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer
|
||||||
{
|
{
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#import "ASDisplayNode+Subclasses.h"
|
#import "ASDisplayNode+Subclasses.h"
|
||||||
#import "ASDisplayNodeTestsHelper.h"
|
#import "ASDisplayNodeTestsHelper.h"
|
||||||
#import "UIView+ASConvenience.h"
|
#import "UIView+ASConvenience.h"
|
||||||
|
#import "ASCellNode.h"
|
||||||
|
|
||||||
// Conveniences for making nodes named a certain way
|
// Conveniences for making nodes named a certain way
|
||||||
#define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n
|
#define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n
|
||||||
@ -76,11 +77,15 @@ for (ASDisplayNode *n in @[ nodes ]) {\
|
|||||||
+ (dispatch_queue_t)asyncSizingQueue;
|
+ (dispatch_queue_t)asyncSizingQueue;
|
||||||
- (id)initWithViewClass:(Class)viewClass;
|
- (id)initWithViewClass:(Class)viewClass;
|
||||||
- (id)initWithLayerClass:(Class)layerClass;
|
- (id)initWithLayerClass:(Class)layerClass;
|
||||||
|
|
||||||
|
// FIXME: Importing ASDisplayNodeInternal.h causes a heap of problems.
|
||||||
|
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface ASTestDisplayNode : ASDisplayNode
|
@interface ASTestDisplayNode : ASDisplayNode
|
||||||
@property (atomic, copy) void (^willDeallocBlock)(ASTestDisplayNode *node);
|
@property (atomic, copy) void (^willDeallocBlock)(ASTestDisplayNode *node);
|
||||||
@property (atomic, copy) CGSize(^calculateSizeBlock)(ASTestDisplayNode *node, CGSize size);
|
@property (atomic, copy) CGSize(^calculateSizeBlock)(ASTestDisplayNode *node, CGSize size);
|
||||||
|
@property (atomic) BOOL hasFetchedData;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface ASTestResponderNode : ASTestDisplayNode
|
@interface ASTestResponderNode : ASTestDisplayNode
|
||||||
@ -93,6 +98,18 @@ for (ASDisplayNode *n in @[ nodes ]) {\
|
|||||||
return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero;
|
return _calculateSizeBlock ? _calculateSizeBlock(self, constrainedSize) : CGSizeZero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)fetchData
|
||||||
|
{
|
||||||
|
[super fetchData];
|
||||||
|
self.hasFetchedData = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearFetchedData
|
||||||
|
{
|
||||||
|
[super clearFetchedData];
|
||||||
|
self.hasFetchedData = NO;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
if (_willDeallocBlock) {
|
if (_willDeallocBlock) {
|
||||||
@ -1666,6 +1683,48 @@ static inline BOOL _CGPointEqualToPointWithEpsilon(CGPoint point1, CGPoint point
|
|||||||
[self checkBackgroundColorOpaqueRelationshipWithViewLoaded:NO layerBacked:YES];
|
[self checkBackgroundColorOpaqueRelationshipWithViewLoaded:NO layerBacked:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that nodes who have no cell node (no range controller)
|
||||||
|
// do get their `fetchData` called, and they do report
|
||||||
|
// the fetch data interface state.
|
||||||
|
- (void)testInterfaceStateForNonCellNode
|
||||||
|
{
|
||||||
|
ASTestWindow *window = [ASTestWindow new];
|
||||||
|
ASTestDisplayNode *node = [ASTestDisplayNode new];
|
||||||
|
XCTAssert(node.interfaceState == ASInterfaceStateNone);
|
||||||
|
XCTAssert(!node.hasFetchedData);
|
||||||
|
|
||||||
|
[window addSubview:node.view];
|
||||||
|
XCTAssert(node.hasFetchedData);
|
||||||
|
XCTAssert(node.interfaceState == ASInterfaceStateInHierarchy);
|
||||||
|
|
||||||
|
[node.view removeFromSuperview];
|
||||||
|
XCTAssert(!node.hasFetchedData);
|
||||||
|
XCTAssert(node.interfaceState == ASInterfaceStateNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that nodes who have no cell node (no range controller)
|
||||||
|
// do get their `fetchData` called, and they do report
|
||||||
|
// the fetch data interface state.
|
||||||
|
- (void)testInterfaceStateForCellNode
|
||||||
|
{
|
||||||
|
ASCellNode *cellNode = [ASCellNode new];
|
||||||
|
ASTestDisplayNode *node = [ASTestDisplayNode new];
|
||||||
|
XCTAssert(node.interfaceState == ASInterfaceStateNone);
|
||||||
|
XCTAssert(!node.hasFetchedData);
|
||||||
|
|
||||||
|
// Simulate range handler updating cell node.
|
||||||
|
[cellNode addSubnode:node];
|
||||||
|
[cellNode enterInterfaceState:ASInterfaceStateFetchData];
|
||||||
|
XCTAssert(node.hasFetchedData);
|
||||||
|
XCTAssert(node.interfaceState == ASInterfaceStateFetchData);
|
||||||
|
|
||||||
|
// If the node goes into a view it should not adopt the `InHierarchy` state.
|
||||||
|
ASTestWindow *window = [ASTestWindow new];
|
||||||
|
[window addSubview:cellNode.view];
|
||||||
|
XCTAssert(node.hasFetchedData);
|
||||||
|
XCTAssert(node.interfaceState == ASInterfaceStateFetchData);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)testInitWithViewClass
|
- (void)testInitWithViewClass
|
||||||
{
|
{
|
||||||
ASDisplayNode *scrollNode = [[ASDisplayNode alloc] initWithViewClass:[UIScrollView class]];
|
ASDisplayNode *scrollNode = [[ASDisplayNode alloc] initWithViewClass:[UIScrollView class]];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user