Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a Pluggable "Tips" System to Help in Development #19

Merged
merged 1 commit into from
Apr 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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