forked from facebookarchive/AsyncDisplayKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_ASDisplayView.mm
More file actions
216 lines (183 loc) · 6.73 KB
/
_ASDisplayView.mm
File metadata and controls
216 lines (183 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "_ASDisplayView.h"
#import <objc/runtime.h>
#import "_ASCoreAnimationExtras.h"
#import "_ASAsyncTransactionContainer.h"
#import "ASAssert.h"
#import "ASDisplayNodeExtras.h"
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+Subclasses.h"
@interface _ASDisplayView ()
@property (nonatomic, assign, readwrite) ASDisplayNode *asyncdisplaykit_node;
// Keep the node alive while its view is active. If you create a view, add its layer to a layer hierarchy, then release
// the view, the layer retains the view to prevent a crash. This replicates this behaviour for the node abstraction.
@property (nonatomic, retain, readwrite) ASDisplayNode *keepalive_node;
@end
@implementation _ASDisplayView
{
__unsafe_unretained ASDisplayNode *_node; // Though UIView has a .node property added via category, since we can add an ivar to a subclass, use that for performance.
BOOL _inHitTest;
BOOL _inPointInside;
}
@synthesize asyncdisplaykit_node = _node;
+ (Class)layerClass
{
return [_ASDisplayLayer class];
}
#pragma mark - NSObject Overrides
- (id)init
{
return [self initWithFrame:CGRectZero];
}
- (NSString *)description
{
// The standard UIView description is useless for debugging because all ASDisplayNode subclasses have _ASDisplayView-type views.
// This allows us to at least see the name of the node subclass and get its pointer directly from [[UIWindow keyWindow] recursiveDescription].
return [NSString stringWithFormat:@"<%@, view = %@>", _node, [super description]];
}
#pragma mark - UIView Overrides
- (id)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame]))
return nil;
return self;
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
// Keep the node alive while the view is in a view hierarchy. This helps ensure that async-drawing views can always
// display their contents as long as they are visible somewhere, and aids in lifecycle management because the
// lifecycle of the node can be treated as the same as the lifecycle of the view (let the view hierarchy own the
// view).
UIView *currentSuperview = self.superview;
if (!currentSuperview && newSuperview) {
self.keepalive_node = _node;
}
else if (currentSuperview && !newSuperview) {
self.keepalive_node = nil;
}
}
- (void)willMoveToWindow:(UIWindow *)newWindow
{
BOOL visible = newWindow != nil;
if (visible && !_node.inWindow) {
[_node __enterHierarchy];
} else if (!visible && _node.inWindow) {
[_node __exitHierarchy];
}
}
- (void)didMoveToSuperview
{
// FIXME maybe move this logic into ASDisplayNode addSubnode/removeFromSupernode
UIView *superview = self.superview;
// If superview's node is different from supernode's view, fix it by setting supernode to the new superview's node. Got that?
if (!superview)
[_node __setSupernode:nil];
else if (superview != _node.supernode.view)
[_node __setSupernode:superview.asyncdisplaykit_node];
}
- (void)setNeedsDisplay
{
// Standard implementation does not actually get to the layer, at least for views that don't implement drawRect:.
if (ASDisplayNodeThreadIsMain()) {
[self.layer setNeedsDisplay];
} else {
dispatch_async(dispatch_get_main_queue(), ^ {
[self.layer setNeedsDisplay];
});
}
}
- (void)setNeedsLayout
{
if (ASDisplayNodeThreadIsMain()) {
[super setNeedsLayout];
} else {
dispatch_async(dispatch_get_main_queue(), ^ {
[super setNeedsLayout];
});
}
}
- (void)layoutSubviews
{
if (ASDisplayNodeThreadIsMain()) {
[_node __layout];
} else {
// FIXME: CRASH This should not be happening because of the way we gate -setNeedsLayout, but it has been seen.
ASDisplayNodeFailAssert(@"not reached assertion");
dispatch_async(dispatch_get_main_queue(), ^ {
[_node __layout];
});
}
}
- (UIViewContentMode)contentMode
{
return ASDisplayNodeUIContentModeFromCAContentsGravity(self.layer.contentsGravity);
}
- (void)setContentMode:(UIViewContentMode)contentMode
{
ASDisplayNodeAssert(contentMode != UIViewContentModeRedraw, @"Don't do this. Use needsDisplayOnBoundsChange instead.");
// Do our own mapping so as not to call super and muck up needsDisplayOnBoundsChange. If we're in a production build, fall back to resize if we see redraw
self.layer.contentsGravity = (contentMode != UIViewContentModeRedraw) ? ASDisplayNodeCAContentsGravityFromUIContentMode(contentMode) : kCAGravityResize;
}
#pragma mark - Event Handling + UIResponder Overrides
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_node touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[_node touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[_node touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[_node touchesCancelled:touches withEvent:event];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// REVIEW: We should optimize these types of messages by setting a boolean in the associated ASDisplayNode subclass if
// they actually override the method. Same goes for -pointInside:withEvent: below. Many UIKit classes use that
// pattern for meaningful reductions of message send overhead in hot code (especially event handling).
// Set boolean so this method can be re-entrant. If the node subclass wants to default to / make use of UIView
// hitTest:, it will call it on the view, which is _ASDisplayView. After calling into the node, any additional calls
// should use the UIView implementation of hitTest:
if (!_inHitTest) {
_inHitTest = YES;
UIView *hitView = [_node hitTest:point withEvent:event];
_inHitTest = NO;
return hitView;
} else {
return [super hitTest:point withEvent:event];
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
// See comments in -hitTest:withEvent: for the strategy here.
if (!_inPointInside) {
_inPointInside = YES;
BOOL result = [_node pointInside:point withEvent:event];
_inPointInside = NO;
return result;
} else {
return [super pointInside:point withEvent:event];
}
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_6_0
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return [_node gestureRecognizerShouldBegin:gestureRecognizer];
}
#endif
- (void)asyncdisplaykit_asyncTransactionContainerStateDidChange
{
[_node asyncdisplaykit_asyncTransactionContainerStateDidChange];
}
@end