forked from pocketsvg/PocketSVG
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SVGImageView.m
193 lines (173 loc) · 5.7 KB
/
SVGImageView.m
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
/*
* This file is part of the PocketSVG package.
* Copyright (c) Ponderwell, Ariel Elkin, Fjölnir Ásgeirsson, and Contributors
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SVGImageView.h"
#import "SVGLayer.h"
#import "SVGPortability.h"
#import "SVGBezierPath.h"
@interface SVGImageView ()
// Allows for setting the SVG via IB
@property(nonatomic, copy) IBInspectable NSString *svgName;
@end
@implementation SVGImageView {
SVGLayer *_svgLayer;
#ifdef DEBUG
dispatch_source_t _fileWatcher;
#endif
}
#if TARGET_OS_IPHONE
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_svgLayer = (SVGLayer *)self.layer;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder])) {
_svgLayer = (SVGLayer *)self.layer;
}
return self;
}
#else
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_svgLayer = [SVGLayer new];
self.wantsLayer = YES;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder])) {
_svgLayer = [SVGLayer new];
self.wantsLayer = YES;
}
return self;
}
#endif
- (instancetype)initWithContentsOfURL:(NSURL *)url {
if (self = [self init]) {
[self _cr_loadSVGFromURL:url];
}
return self;
}
#if TARGET_OS_IPHONE
+ (Class)layerClass
{
return [SVGLayer class];
}
#else
- (CALayer *)makeBackingLayer
{
return _svgLayer;
}
- (BOOL)isFlipped
{
return YES;
}
- (void)sizeToFit
{
self.bounds = (NSRect) { self.bounds.origin, [self sizeThatFits:CGSizeZero] };
}
#endif
- (NSArray<SVGBezierPath*> *)paths { return _svgLayer.paths; }
- (void)setPaths:(NSArray<SVGBezierPath *> *)paths
{
#if defined(DEBUG) && !defined(POCKETSVG_DISABLE_FILEWATCH)
if(_fileWatcher)
(void)((dispatch_source_cancel(_fileWatcher))), _fileWatcher = NULL;
#endif
_svgLayer.paths = paths;
}
- (void)setSvgName:(NSString *)svgName
{
#if !TARGET_INTERFACE_BUILDER
NSBundle * const bundle = [NSBundle mainBundle];
NSURL *url = [bundle URLForResource:svgName withExtension:@"svg"];
NSParameterAssert(!svgName || url);
#else
NSString *path = nil;
NSPredicate * const pred = [NSPredicate predicateWithFormat:@"lastPathComponent LIKE[c] %@",
[svgName stringByAppendingPathExtension:@"svg"]];
NSString * const sourceDirs = [[NSProcessInfo processInfo] environment][@"IB_PROJECT_SOURCE_DIRECTORIES"];
for(__strong NSString *dir in [sourceDirs componentsSeparatedByString:@":"]) {
// Go up the hierarchy until we don't find an xcodeproj
NSString *projectDir = dir;
NSPredicate *xcodePredicate = [NSPredicate predicateWithFormat:@"self ENDSWITH[c] %@", @".xcodeproj"];
do {
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:NULL];
if([[contents filteredArrayUsingPredicate:xcodePredicate] count] > 0) {
projectDir = dir;
}
NSLog(@"%@", dir);
} while(![(dir = dir.stringByDeletingLastPathComponent) isEqual:@"/"]);
NSArray * const results = [[[NSFileManager defaultManager] subpathsAtPath:projectDir]
filteredArrayUsingPredicate:pred];
if([results count] > 0) {
path = [projectDir stringByAppendingPathComponent:results[0]];
break;
}
}
NSURL *url = path ? [NSURL fileURLWithPath:path] : nil;
#endif
[self _cr_loadSVGFromURL:url];
}
- (void)_cr_loadSVGFromURL:(NSURL *)url
{
#if defined(DEBUG) && !defined(POCKETSVG_DISABLE_FILEWATCH)
if(_fileWatcher)
dispatch_source_cancel(_fileWatcher);
int const fdes = open([url fileSystemRepresentation], O_RDONLY);
_fileWatcher = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdes,
DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE,
dispatch_get_main_queue());
dispatch_source_set_event_handler(_fileWatcher, ^{
unsigned long const l = dispatch_source_get_data(self->_fileWatcher);
if(l & DISPATCH_VNODE_DELETE || l & DISPATCH_VNODE_WRITE) {
NSLog(@"Reloading %@", url.lastPathComponent);
dispatch_source_cancel(self->_fileWatcher);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[SVGBezierPath resetCache];
[self _cr_loadSVGFromURL:url];
});
}
});
dispatch_source_set_cancel_handler(_fileWatcher, ^{
close(fdes);
});
dispatch_resume(_fileWatcher);
#endif
_svgLayer.paths = [SVGBezierPath pathsFromSVGAtURL:url];
}
- (void)dealloc
{
#ifdef DEBUG
if(_fileWatcher)
dispatch_source_cancel(_fileWatcher);
#endif
}
- (PSVGColor *)fillColor { return _svgLayer.fillColor
? [PSVGColor colorWithCGColor:_svgLayer.fillColor]
: nil; }
- (void)setFillColor:(PSVGColor * const)aColor { _svgLayer.fillColor = aColor.CGColor; }
- (PSVGColor *)strokeColor { return _svgLayer.strokeColor
? [PSVGColor colorWithCGColor:_svgLayer.strokeColor]
: nil; }
- (void)setStrokeColor:(PSVGColor * const)aColor { _svgLayer.strokeColor = aColor.CGColor; }
- (CGSize)sizeThatFits:(CGSize)aSize
{
return self.layer.preferredFrameSize;
}
- (CGSize)intrinsicContentSize
{
return [self sizeThatFits:CGSizeZero];
}
@end