-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathSpriteBar.m
173 lines (120 loc) · 5.48 KB
/
SpriteBar.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
//
// SpriteBar.m
// SpriteBar
//
// Created by Henry Everett on 27/05/2014.
// Copyright (c) 2014 Henry Everett. All rights reserved.
//
#import "SpriteBar.h"
@interface SpriteBar ()
@property (nonatomic, strong) SKTextureAtlas *atlas;
@property (nonatomic, strong) NSMutableArray *availableTextureAddresses;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) NSTimeInterval timerInterval;
@property (nonatomic, assign) NSTimeInterval currentTime;
@property (nonatomic, strong) id timerTarget;
@property (nonatomic, assign) SEL timerSelector;
/* Called upon timer interval. */
- (void)timerTick:(NSTimer *)timer;
/* Calculate the number of frames in the texture atlas. */
- (NSInteger)numberOfFramesInAnimation:(NSString *)animationName;
/* Find the nearest texture number to a given percent. */
- (NSInteger)closestAvailableToPercent:(NSInteger)percent;
/* Extract the percent identifier from a texture name. */
- (NSNumber *)percentFromTextureName:(NSString *)string;
@end
@implementation SpriteBar
@synthesize atlas;
- (id)init {
SKTextureAtlas *defaultAtlas = [SKTextureAtlas atlasNamed:@"sb_default"];
return [self initWithTextureAtlas:defaultAtlas];
}
- (id)initWithTextureAtlas:(SKTextureAtlas *)textureAtlas {
self = [super init];
if (self) {
self.atlas = textureAtlas;
self.textureReference = @"progress";
}
return self;
}
- (void)resetProgress {
self.texture = [self.atlas textureNamed:[NSString stringWithFormat:@"%@_%lu.png",self.textureReference,[self closestAvailableToPercent:0]]];
self.availableTextureAddresses = [[NSMutableArray alloc] init];
for (NSString *name in self.atlas.textureNames) {
[self.availableTextureAddresses addObject:[self percentFromTextureName:name]];
}
[self invalidateTimer];
// Set defaults
self.currentTime = 0;
}
- (void)startBarProgressWithTimer:(NSTimeInterval)seconds target:(id)target selector:(SEL)selector {
[self resetProgress];
self.timerTarget = target;
self.timerSelector = selector;
// Split the progress time between animation frames
self.timerInterval = seconds / ([self numberOfFramesInAnimation:self.textureReference] - 1);
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timerInterval target:self selector:@selector(timerTick:) userInfo:[NSNumber numberWithDouble:seconds] repeats:YES];
}
- (void)timerTick:(NSTimer *)timer {
// Increment timer interval counter
self.currentTime += self.timerInterval;
// Make sure we don't exceed the total time
if (self.currentTime <= [timer.userInfo doubleValue]) {
[self setProgressWithValue:self.currentTime ofTotal:[timer.userInfo doubleValue]];
}
}
- (void)invalidateTimer {
[self.timer invalidate];
}
- (void)setProgressWithValue:(CGFloat)progress ofTotal:(CGFloat)maxValue {
[self setProgress:progress/maxValue];
}
- (void)setProgress:(CGFloat)progress {
// Set texure
CGFloat percent = lrint(progress * 100);
self.texture = [self.atlas
textureNamed:[NSString stringWithFormat:@"%@_%lu.png",self.textureReference,[self closestAvailableToPercent:percent]]];
// If we have reached 100%, invalidate the timer and perform selector on passed in object.
if (fabsf(progress) >= fabsf(1.0)) {
if (self.timerTarget && [self.timerTarget respondsToSelector:self.timerSelector]) {
IMP imp = [self.timerTarget methodForSelector:self.timerSelector];
void (*func)(id, SEL) = (void *)imp;
func(self.timerTarget, self.timerSelector);
}
[self.timer invalidate];
}
}
- (NSInteger)numberOfFramesInAnimation:(NSString *)animationName {
// Get the number of frames in the animation.
NSArray *allAnimationNames = self.atlas.textureNames;
NSPredicate *nameFilter = [NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] %@",animationName];
return [[allAnimationNames filteredArrayUsingPredicate:nameFilter] count];
}
- (NSInteger)closestAvailableToPercent:(NSInteger)percent {
NSInteger closest = 0;
for (NSNumber *thisPerc in self.availableTextureAddresses) {
if (labs(thisPerc.integerValue - percent) < labs(closest - percent)) {
closest = thisPerc.integerValue;
}
}
return closest;
}
- (NSNumber *)percentFromTextureName:(NSString *)string {
// Get rid of "@2x"
NSString *clippedString = [string stringByReplacingOccurrencesOfString:@"@2x" withString:@""];
// Match the rest of the pattern
NSString *pattern = [NSString stringWithFormat:@"(?<=%@_)([0-9]+)(?=.png)",self.textureReference];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil];
NSArray *matches = [regex matchesInString:clippedString options:0 range:NSMakeRange(0, clippedString.length)];
// If the matches don't equal 1, you have done something wrong.
if (matches.count != 1) {
[NSException raise:@"SpriteBar: Incorrect texture naming."
format:@"Textures should follow naming convention: %@_#.png. Failed texture name: %@",self.textureReference,string];
}
for (NSTextCheckingResult *match in matches) {
NSRange matchRange = [match rangeAtIndex:1];
return [NSNumber numberWithInteger:[[clippedString substringWithRange:matchRange] integerValue]];
}
return nil;
}
@end