Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 3f574a3

Browse files
committed
Merge pull request #521 from jflinter/jack-uiresponder-additions
Add UIResponder methods to ASDisplayNode
2 parents 58a55f0 + 5889c70 commit 3f574a3

File tree

6 files changed

+133
-6
lines changed

6 files changed

+133
-6
lines changed

AsyncDisplayKit/ASDisplayNode.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
432432
*/
433433
- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node;
434434

435+
/** @name UIResponder methods */
436+
437+
// By default these fall through to the underlying view, but can be overridden.
438+
- (BOOL)canBecomeFirstResponder; // default==NO
439+
- (BOOL)becomeFirstResponder; // default==NO (no-op)
440+
- (BOOL)canResignFirstResponder; // default==YES
441+
- (BOOL)resignFirstResponder; // default==NO (no-op)
442+
- (BOOL)isFirstResponder;
443+
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender;
444+
435445
@end
436446

437447

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,35 @@ - (void)_enqueueAsyncSizingWithSentinel:(ASSentinel *)sentinel completion:(void(
16981698

16991699
}
17001700

1701+
- (BOOL)canBecomeFirstResponder {
1702+
return NO;
1703+
}
1704+
1705+
- (BOOL)canResignFirstResponder {
1706+
return YES;
1707+
}
1708+
1709+
- (BOOL)isFirstResponder {
1710+
ASDisplayNodeAssertMainThread();
1711+
return _view != nil && [_view isFirstResponder];
1712+
}
1713+
1714+
// Note: this implicitly loads the view if it hasn't been loaded yet.
1715+
- (BOOL)becomeFirstResponder {
1716+
ASDisplayNodeAssertMainThread();
1717+
return !self.layerBacked && [self canBecomeFirstResponder] && [self.view becomeFirstResponder];
1718+
}
1719+
1720+
- (BOOL)resignFirstResponder {
1721+
ASDisplayNodeAssertMainThread();
1722+
return !self.layerBacked && [self canResignFirstResponder] && [_view resignFirstResponder];
1723+
}
1724+
1725+
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
1726+
ASDisplayNodeAssertMainThread();
1727+
return !self.layerBacked && [self.view canPerformAction:action withSender:sender];
1728+
}
1729+
17011730
@end
17021731

17031732
@implementation ASDisplayNode (Debugging)

AsyncDisplayKit/ASEditableTextNode.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@
5757
- (BOOL)isFirstResponder;
5858

5959
//! @abstract Makes the receiver's text view the first responder.
60-
- (void)becomeFirstResponder;
60+
- (BOOL)becomeFirstResponder;
6161

6262
//! @abstract Resigns the receiver's text view from first-responder status, if it has it.
63-
- (void)resignFirstResponder;
63+
- (BOOL)resignFirstResponder;
6464

6565
#pragma mark - Geometry
6666
/**

AsyncDisplayKit/ASEditableTextNode.mm

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,16 +352,26 @@ - (BOOL)isFirstResponder
352352
return [_textKitComponents.textView isFirstResponder];
353353
}
354354

355-
- (void)becomeFirstResponder
355+
- (BOOL)canBecomeFirstResponder {
356+
ASDN::MutexLocker l(_textKitLock);
357+
return [_textKitComponents.textView canBecomeFirstResponder];
358+
}
359+
360+
- (BOOL)becomeFirstResponder
356361
{
357362
ASDN::MutexLocker l(_textKitLock);
358-
[_textKitComponents.textView becomeFirstResponder];
363+
return [_textKitComponents.textView becomeFirstResponder];
364+
}
365+
366+
- (BOOL)canResignFirstResponder {
367+
ASDN::MutexLocker l(_textKitLock);
368+
return [_textKitComponents.textView canResignFirstResponder];
359369
}
360370

361-
- (void)resignFirstResponder
371+
- (BOOL)resignFirstResponder
362372
{
363373
ASDN::MutexLocker l(_textKitLock);
364-
[_textKitComponents.textView resignFirstResponder];
374+
return [_textKitComponents.textView resignFirstResponder];
365375
}
366376

367377
#pragma mark - UITextView Delegate

AsyncDisplayKit/Details/_ASDisplayView.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ - (void)tintColorDidChange
243243
[_node tintColorDidChange];
244244
}
245245

246+
- (BOOL)canBecomeFirstResponder {
247+
return [_node canBecomeFirstResponder];
248+
}
249+
250+
- (BOOL)canResignFirstResponder {
251+
return [_node canResignFirstResponder];
252+
}
253+
246254
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
247255
{
248256
// We forward responder-chain actions to our node if we can't handle them ourselves. See -targetForAction:withSender:.

AsyncDisplayKitTests/ASDisplayNodeTests.m

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ @interface ASTestDisplayNode : ASDisplayNode
7171
@property (atomic, copy) CGSize(^calculateSizeBlock)(ASTestDisplayNode *node, CGSize size);
7272
@end
7373

74+
@interface ASTestResponderNode : ASTestDisplayNode
75+
@end
76+
7477
@implementation ASTestDisplayNode
7578

7679
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
@@ -91,9 +94,57 @@ - (void)dealloc
9194
@interface UIDisplayNodeTestView : UIView
9295
@end
9396

97+
@interface UIResponderNodeTestView : _ASDisplayView
98+
@property(nonatomic) BOOL isFirstResponder;
99+
@end
100+
94101
@implementation UIDisplayNodeTestView
95102
@end
96103

104+
@interface ASTestWindow : UIWindow
105+
@end
106+
107+
@implementation ASTestWindow
108+
109+
- (id)firstResponder {
110+
return self.subviews.firstObject;
111+
}
112+
113+
@end
114+
115+
@implementation ASTestResponderNode
116+
117+
+ (Class)viewClass {
118+
return [UIResponderNodeTestView class];
119+
}
120+
121+
- (BOOL)canBecomeFirstResponder {
122+
return YES;
123+
}
124+
125+
@end
126+
127+
@implementation UIResponderNodeTestView
128+
129+
- (BOOL)becomeFirstResponder {
130+
self.isFirstResponder = YES;
131+
return YES;
132+
}
133+
134+
- (BOOL)canResignFirstResponder {
135+
return YES;
136+
}
137+
138+
- (BOOL)resignFirstResponder {
139+
if (self.isFirstResponder) {
140+
self.isFirstResponder = NO;
141+
return YES;
142+
}
143+
return NO;
144+
}
145+
146+
@end
147+
97148
@interface ASDisplayNodeTests : XCTestCase
98149
@end
99150

@@ -102,6 +153,25 @@ @implementation ASDisplayNodeTests
102153
dispatch_queue_t queue;
103154
}
104155

156+
- (void)testOverriddenFirstResponderBehavior {
157+
ASTestDisplayNode *node = [[ASTestResponderNode alloc] init];
158+
XCTAssertTrue([node canBecomeFirstResponder]);
159+
XCTAssertTrue([node becomeFirstResponder]);
160+
}
161+
162+
- (void)testDefaultFirstResponderBehavior {
163+
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
164+
XCTAssertFalse([node canBecomeFirstResponder]);
165+
XCTAssertFalse([node becomeFirstResponder]);
166+
}
167+
168+
- (void)testLayerBackedFirstResponderBehavior {
169+
ASTestDisplayNode *node = [[ASTestResponderNode alloc] init];
170+
node.layerBacked = YES;
171+
XCTAssertTrue([node canBecomeFirstResponder]);
172+
XCTAssertFalse([node becomeFirstResponder]);
173+
}
174+
105175
- (void)setUp
106176
{
107177
[super setUp];

0 commit comments

Comments
 (0)