forked from facebook/componentkit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCKTextComponentLayer.mm
137 lines (120 loc) · 4.41 KB
/
CKTextComponentLayer.mm
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
/*
* 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 "CKTextComponentLayer.h"
#import <ComponentKit/CKInternalHelpers.h>
#import <ComponentKit/CKTextKitAttributes.h>
#import <ComponentKit/CKTextKitRenderer.h>
#import <ComponentKit/CKTextKitRendererCache.h>
#import <ComponentKit/CKAssert.h>
#import "CKTextComponentLayerHighlighter.h"
static CK::TextKit::Renderer::Cache *rasterContentsCache()
{
// 6MB raster contents cache that evicts 20% of the least recently used bitmaps it contains when it hits 6MB
static CK::TextKit::Renderer::Cache *__rasterContentsCache (new CK::TextKit::Renderer::Cache("CKTextComponentRasterContentsCache", 6 * 1024 * 1025, 0.2));
return __rasterContentsCache;
}
@implementation CKTextComponentLayer
{
CKTextComponentLayerHighlighter *_highlighter;
}
+ (id)defaultValueForKey:(NSString *)key
{
if ([key isEqualToString:@"contentsScale"]) {
return @(CKScreenScale());
} else if ([key isEqualToString:@"backgroundColor"]) {
return (id)[UIColor whiteColor].CGColor;
} else if ([key isEqualToString:@"opaque"]) {
return (id)kCFBooleanTrue;
} else if ([key isEqualToString:@"userInteractionEnabled"]) {
return (id)kCFBooleanTrue;
} else if ([key isEqualToString:@"needsDisplayOnBoundsChange"]) {
return (id)kCFBooleanTrue;
}
return [super defaultValueForKey:key];
}
- (void)setNeedsDisplayOnBoundsChange:(BOOL)needsDisplayOnBoundsChange
{
// Don't allow this property to be disabled. Unfortunately, UIView will turn this off when setting the
// backgroundColor, for reasons that cannot be understood. Even worse, it doesn't ever set it back, so it will
// subsequently stay off. Just make sure that it never gets overridden, because the text will not be drawn in the
// correct way (or even at all) if this is set to NO.
if (needsDisplayOnBoundsChange) {
[super setNeedsDisplayOnBoundsChange:needsDisplayOnBoundsChange];
}
}
- (void)setRenderer:(CKTextKitRenderer *)renderer
{
CKAssertMainThread();
if (renderer != _renderer) {
if (renderer && _renderer) {
if (renderer.attributes == _renderer.attributes
&& CGSizeEqualToSize(renderer.constrainedSize, renderer.constrainedSize)) {
// If the renderers are identical there's no point in re-rendering
_renderer = renderer;
return;
} else {
// If the renderers are truly not equal we need to nil out the contents so we don't display old text
// from a previous renderer.
self.contents = nil;
}
}
_renderer = renderer;
[self setNeedsDisplay];
}
}
- (NSObject *)drawParameters
{
return _renderer;
}
- (id)willDisplayAsynchronouslyWithDrawParameters:(id<NSObject>)drawParameters
{
return rasterContentsCache()->objectForKey({_renderer.attributes, _renderer.constrainedSize});
}
- (void)didDisplayAsynchronously:(id)newContents withDrawParameters:(id<NSObject>)drawParameters
{
if (newContents) {
CGImageRef imageRef = (__bridge CGImageRef)newContents;
NSUInteger bytes = CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef);
rasterContentsCache()->cacheObject({_renderer.attributes, _renderer.constrainedSize}, newContents, bytes);
}
}
+ (void)drawInContext:(CGContextRef)context parameters:(CKTextKitRenderer *)renderer
{
CGRect boundsRect = CGContextGetClipBoundingBox(context);
[renderer drawInContext:context bounds:boundsRect];
}
- (void)drawInContext:(CGContextRef)ctx
{
// When we're drawing synchronously we need to manually fill the bg color because CKAsyncLayer doesn't.
if (self.opaque && self.backgroundColor != NULL) {
CGRect boundsRect = CGContextGetClipBoundingBox(ctx);
CGContextSetFillColorWithColor(ctx, self.backgroundColor);
CGContextFillRect(ctx, boundsRect);
}
[super drawInContext:ctx];
}
#pragma mark - Highlighting
- (CKTextComponentLayerHighlighter *)highlighter
{
CKAssertMainThread();
if (!_highlighter) {
_highlighter = [[CKTextComponentLayerHighlighter alloc] initWithTextComponentLayer:self];
}
return _highlighter;
}
- (void)layoutSublayers
{
// Do not generate a highlighter if one doesn't already exist
if (_highlighter) {
[_highlighter layoutHighlight];
}
[super layoutSublayers];
}
@end