Skip to content

Commit

Permalink
Build a Tips system (TextureGroup#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
Adlai-Holler authored and garrettmoon committed Apr 20, 2017
1 parent 92c1c1b commit 67387c0
Show file tree
Hide file tree
Showing 33 changed files with 1,048 additions and 37 deletions.
72 changes: 72 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Source/ASCellNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,11 @@ - (NSString *)supplementaryElementKind
return self.collectionElement.supplementaryElementKind;
}

- (BOOL)supportsLayerBacking
{
return NO;
}

@end


Expand Down
7 changes: 6 additions & 1 deletion Source/ASControlNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
return YES;
}

- (BOOL)supportsLayerBacking
{
return super.supportsLayerBacking && !self.userInteractionEnabled;
}

#pragma mark - Action Messages

- (void)addTarget:(id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEventMask
Expand Down Expand Up @@ -424,7 +429,7 @@ - (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:
(ASControlNodeEvent controlEvent)
{
// Use a copy to itereate, the action perform could call remove causing a mutation crash.
NSMutableArray *eventTargetActionArray = [_controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)] copy];
NSArray *eventTargetActionArray = [_controlEventDispatchTable[_ASControlNodeEventKeyForControlEvent(controlEvent)] copy];

// Iterate on each target action pair
for (ASControlTargetAction *targetAction in eventTargetActionArray) {
Expand Down
5 changes: 5 additions & 0 deletions Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,11 @@ extern NSInteger const ASDefaultDrawingPriority;
*/
- (CGRect)convertRect:(CGRect)rect fromNode:(nullable ASDisplayNode *)node AS_WARN_UNUSED_RESULT;

/**
* Whether or not the node would support having .layerBacked = YES.
*/
@property (nonatomic, readonly) BOOL supportsLayerBacking;

@end

/**
Expand Down
55 changes: 29 additions & 26 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASWeakProxy.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import <AsyncDisplayKit/ASTipsController.h>

#if ASDisplayNodeLoggingEnabled
#define LOG(...) NSLog(__VA_ARGS__)
Expand Down Expand Up @@ -245,11 +246,6 @@ + (void)load
ASScreenScale();
}

+ (BOOL)layerBackedNodesEnabled
{
return YES;
}

+ (Class)viewClass
{
return [_ASDisplayView class];
Expand All @@ -275,6 +271,8 @@ - (void)_initializeInstance
_eventLog = [[ASEventLog alloc] initWithObject:self];
#endif

_viewClass = [self.class viewClass];
_layerClass = [self.class layerClass];
_contentsScaleForDisplay = ASScreenScale();

_primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
Expand Down Expand Up @@ -574,9 +572,6 @@ - (UIView *)_locked_viewToLoad
_viewBlock = nil;
_viewClass = [view class];
} else {
if (!_viewClass) {
_viewClass = [self.class viewClass];
}
view = [[_viewClass alloc] init];
}

Expand Down Expand Up @@ -610,9 +605,6 @@ - (CALayer *)_locked_layerToLoad
_layerBlock = nil;
_layerClass = [layer class];
} else {
if (!_layerClass) {
_layerClass = [self.class layerClass];
}
layer = [[_layerClass alloc] init];
}

Expand Down Expand Up @@ -805,26 +797,22 @@ - (BOOL)isSynchronous
return _flags.synchronous;
}

- (void)setSynchronous:(BOOL)flag
{
ASDN::MutexLocker l(__instanceLock__);
_flags.synchronous = flag;
}

- (void)setLayerBacked:(BOOL)isLayerBacked
{
if (![self.class layerBackedNodesEnabled]) {
return;
}
// Only call this if assertions are enabled – it could be expensive.
ASDisplayNodeAssert(!isLayerBacked || self.supportsLayerBacking, @"Node %@ does not support layer backing.", self);

ASDN::MutexLocker l(__instanceLock__);
ASDisplayNodeAssert(!_view && !_layer, @"Cannot change isLayerBacked after layer or view has loaded");
ASDisplayNodeAssert(!_viewBlock && !_layerBlock, @"Cannot change isLayerBacked when a layer or view block is provided");
ASDisplayNodeAssert(!_viewClass && !_layerClass, @"Cannot change isLayerBacked when a layer or view class is provided");

if (isLayerBacked != _flags.layerBacked && !_view && !_layer) {
_flags.layerBacked = isLayerBacked;
if (_flags.layerBacked == isLayerBacked) {
return;
}

if ([self _locked_isNodeLoaded]) {
ASDisplayNodeFailAssert(@"Cannot change layerBacked after view/layer has loaded.");
return;
}

_flags.layerBacked = isLayerBacked;
}

- (BOOL)isLayerBacked
Expand All @@ -833,6 +821,12 @@ - (BOOL)isLayerBacked
return _flags.layerBacked;
}

- (BOOL)supportsLayerBacking
{
ASDN::MutexLocker l(__instanceLock__);
return !_flags.synchronous && !_flags.viewEverHadAGestureRecognizerAttached && _viewClass == [_ASDisplayView class] && _layerClass == [_ASDisplayLayer class];
}

- (BOOL)shouldAnimateSizeChanges
{
ASDN::MutexLocker l(__instanceLock__);
Expand All @@ -857,6 +851,12 @@ - (void)setThreadSafeBounds:(CGRect)newBounds
_threadSafeBounds = newBounds;
}

- (void)nodeViewDidAddGestureRecognizer
{
ASDN::MutexLocker l(__instanceLock__);
_flags.viewEverHadAGestureRecognizerAttached = YES;
}

#pragma mark - Layout

#if DEBUG
Expand Down Expand Up @@ -3656,6 +3656,9 @@ - (void)didEnterVisibleState
ASDisplayNodeAssertMainThread();
ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
[_interfaceStateDelegate didEnterVisibleState];
#if AS_ENABLE_TIPS
[ASTipsController.shared nodeDidAppear:self];
#endif
}

- (void)didExitVisibleState
Expand Down
5 changes: 5 additions & 0 deletions Source/ASEditableTextNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,11 @@ - (void)setLayerBacked:(BOOL)layerBacked
[super setLayerBacked:layerBacked];
}

- (BOOL)supportsLayerBacking
{
return NO;
}

#pragma mark - Configuration
@synthesize delegate = _delegate;

Expand Down
6 changes: 6 additions & 0 deletions Source/ASMapNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -427,5 +427,11 @@ - (void)layout
}
}
}

- (BOOL)supportsLayerBacking
{
return NO;
}

@end
#endif
22 changes: 22 additions & 0 deletions Source/ASTextNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,28 @@ - (void)didLoad
}
}

- (BOOL)supportsLayerBacking
{
if (!super.supportsLayerBacking) {
return NO;
}

// If the text contains any links, return NO.
NSAttributedString *attributedText = self.attributedText;
NSRange range = NSMakeRange(0, attributedText.length);
for (NSString *linkAttributeName in _linkAttributeNames) {
__block BOOL hasLink = NO;
[attributedText enumerateAttribute:linkAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
hasLink = (value != nil);
*stop = YES;
}];
if (hasLink) {
return NO;
}
}
return YES;
}

#pragma mark - Renderer Management

- (ASTextKitRenderer *)_renderer
Expand Down
5 changes: 5 additions & 0 deletions Source/ASVideoPlayerNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ - (void)didEnterPreloadState
}
}

- (BOOL)supportsLayerBacking
{
return NO;
}

#pragma mark - UI

- (void)createControls
Expand Down
1 change: 1 addition & 0 deletions Source/AsyncDisplayKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>

#import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
#import <AsyncDisplayKit/AsyncDisplayKit+Tips.h>
#import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>

#import <AsyncDisplayKit/ASCollectionNode+Beta.h>
4 changes: 4 additions & 0 deletions Source/Base/ASBaseDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
# define ASDISPLAYNODE_NOTHROW
#endif

#ifndef AS_ENABLE_TIPS
#define AS_ENABLE_TIPS 0
#endif

/**
* The event backtraces take a static 2KB of memory
* and retain all objects present in all the registers
Expand Down
25 changes: 25 additions & 0 deletions Source/Base/ASDisplayNode+Ancestry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// ASNodeAncestorEnumerator.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 4/12/17.
// Copyright © 2017 Facebook. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASDisplayNode.h>

NS_ASSUME_NONNULL_BEGIN

@interface ASDisplayNode (Ancestry)

- (NSEnumerator *)ancestorEnumeratorWithSelf:(BOOL)includeSelf;

/**
* e.g. "(<MYTextNode: 0xFFFF>, <MYTextContainingNode: 0xFFFF>, <MYCellNode: 0xFFFF>)"
*/
@property (atomic, copy, readonly) NSString *ancestryDescription;

@end

NS_ASSUME_NONNULL_END
55 changes: 55 additions & 0 deletions Source/Base/ASDisplayNode+Ancestry.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// ASNodeAncestorEnumerator.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 4/12/17.
// Copyright © 2017 Facebook. All rights reserved.
//

#import "ASDisplayNode+Ancestry.h"

AS_SUBCLASSING_RESTRICTED
@interface ASNodeAncestryEnumerator : NSEnumerator
@end

@implementation ASNodeAncestryEnumerator {
/// Would be nice to use __unsafe_unretained but nodes can be
/// deallocated on arbitrary threads so nope.
__weak ASDisplayNode * _nextNode;
}

- (instancetype)initWithNode:(ASDisplayNode *)node
{
if (self = [super init]) {
_nextNode = node;
}
return self;
}

- (id)nextObject
{
ASDisplayNode *node = _nextNode;
_nextNode = [node supernode];
return node;
}

@end

@implementation ASDisplayNode (Ancestry)

- (NSEnumerator *)ancestorEnumeratorWithSelf:(BOOL)includeSelf
{
ASDisplayNode *node = includeSelf ? self : self.supernode;
return [[ASNodeAncestryEnumerator alloc] initWithNode:node];
}

- (NSString *)ancestryDescription
{
NSMutableArray *strings = [NSMutableArray array];
for (ASDisplayNode *node in [self ancestorEnumeratorWithSelf:YES]) {
[strings addObject:ASObjectDescriptionMakeTiny(node)];
}
return strings.description;
}

@end
42 changes: 42 additions & 0 deletions Source/Debug/AsyncDisplayKit+Tips.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// AsyncDisplayKit+Tips.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 4/12/17.
// Copyright © 2017 Facebook. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASDisplayNode.h>

NS_ASSUME_NONNULL_BEGIN

typedef void(^ASTipDisplayBlock)(ASDisplayNode *node, NSString *message);

/**
* The methods added to ASDisplayNode to control the tips system.
*
* To enable tips, define AS_ENABLE_TIPS=1 (e.g. modify ASBaseDefines.h).
*/
@interface ASDisplayNode (Tips)

/**
* Whether this class should have tips active. Default YES.
*
* NOTE: This property is for _disabling_ tips on a per-class basis,
* if they become annoying or have false-positives. The tips system
* is completely disabled unless you define AS_ENABLE_TIPS=1.
*/
@property (class) BOOL enableTips;

/**
* A block to be run on the main thread to show text when a tip is tapped.
*
* If nil, the default, the message is just logged to the console with the
* ancestry of the node.
*/
@property (class, nonatomic, copy, null_resettable) ASTipDisplayBlock tipDisplayBlock;

@end

NS_ASSUME_NONNULL_END
Loading

0 comments on commit 67387c0

Please sign in to comment.