-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathASProgressPopUpView.m
executable file
·314 lines (259 loc) · 10.1 KB
/
ASProgressPopUpView.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//
// ASProgressPopUpView.h
// ASProgressPopUpView
//
// Created by Alan Skipp on 19/10/2013.
// Copyright (c) 2013 Alan Skipp. All rights reserved.
//
#import "ASPopUpView.h"
#import "ASProgressPopUpView.h"
@interface ASProgressPopUpView() <ASPopUpViewDelegate>
@property (strong, nonatomic) NSNumberFormatter *numberFormatter;
@property (strong, nonatomic) ASPopUpView *popUpView;
@end
@implementation ASProgressPopUpView
{
CGSize _defaultPopUpViewSize; // size that fits string ‘100%’
CGSize _popUpViewSize; // usually == _defaultPopUpViewSize, but can vary if dataSource is used
UIColor *_popUpViewColor;
NSArray *_keyTimes;
}
#pragma mark - initialization
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setup];
}
return self;
}
#pragma mark - public
- (void)showPopUpViewAnimated:(BOOL)animated
{
if (self.popUpView.alpha == 1.0) return;
[self.delegate progressViewWillDisplayPopUpView:self];
[self.popUpView showAnimated:animated];
}
- (void)hidePopUpViewAnimated:(BOOL)animated
{
if (self.popUpView.alpha == 0.0) return;
[self.popUpView hideAnimated:animated completionBlock:^{
if ([self.delegate respondsToSelector:@selector(progressViewDidHidePopUpView:)]) {
[self.delegate progressViewDidHidePopUpView:self];
}
}];
}
- (void)setAutoAdjustTrackColor:(BOOL)autoAdjust
{
if (_autoAdjustTrackColor == autoAdjust) return;
_autoAdjustTrackColor = autoAdjust;
// setProgressTintColor has been overridden to also set autoAdjustTrackColor to NO
// therefore super's implementation must be called to set progressTintColor
if (autoAdjust == NO) {
super.progressTintColor = nil; // sets track to default blue color
} else {
super.progressTintColor = [self.popUpView opaqueColor];
}
}
- (void)setTextColor:(UIColor *)color
{
_textColor = color;
[self.popUpView setTextColor:color];
}
- (void)setFont:(UIFont *)font
{
NSAssert(font, @"font can not be nil, it must be a valid UIFont");
_font = font;
[self.popUpView setFont:font];
[self calculatePopUpViewSize];
}
// return the currently displayed color if possible, otherwise return _popUpViewColor
// if animated colors are set, the color will change each time the progress view updates
- (UIColor *)popUpViewColor
{
return [self.popUpView color] ?: _popUpViewColor;
}
- (void)setPopUpViewColor:(UIColor *)popUpViewColor
{
_popUpViewColor = popUpViewColor;
_popUpViewAnimatedColors = nil; // animated colors should be discarded
[self.popUpView setColor:popUpViewColor];
if (_autoAdjustTrackColor) {
super.progressTintColor = [self.popUpView opaqueColor];
}
}
- (void)setPopUpViewAnimatedColors:(NSArray *)popUpViewAnimatedColors
{
[self setPopUpViewAnimatedColors:popUpViewAnimatedColors withPositions:nil];
}
// if 2 or more colors are present, set animated colors
// if only 1 color is present then call 'setPopUpViewColor:'
// if arg is nil then restore previous _popUpViewColor
- (void)setPopUpViewAnimatedColors:(NSArray *)popUpViewAnimatedColors withPositions:(NSArray *)positions
{
if (positions) {
NSAssert([popUpViewAnimatedColors count] == [positions count], @"popUpViewAnimatedColors and locations should contain the same number of items");
}
_popUpViewAnimatedColors = popUpViewAnimatedColors;
_keyTimes = positions;
if ([popUpViewAnimatedColors count] >= 2) {
[self.popUpView setAnimatedColors:popUpViewAnimatedColors withKeyTimes:_keyTimes];
} else {
[self setPopUpViewColor:[popUpViewAnimatedColors lastObject] ?: _popUpViewColor];
}
}
- (void)setPopUpViewCornerRadius:(CGFloat)popUpViewCornerRadius
{
_popUpViewCornerRadius = popUpViewCornerRadius;
[self.popUpView setCornerRadius:popUpViewCornerRadius];
}
- (void)setDataSource:(id<ASProgressPopUpViewDataSource>)dataSource
{
_dataSource = dataSource;;
[self calculatePopUpViewSize];
}
#pragma mark - ASPopUpViewDelegate
- (void)colorDidUpdate:(UIColor *)opaqueColor;
{
super.progressTintColor = opaqueColor;
}
// returns the current progress in the range 0.0 – 1.0
- (CGFloat)currentValueOffset
{
return self.progress;
}
#pragma mark - private
- (void)setup
{
_autoAdjustTrackColor = YES;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterPercentStyle];
_numberFormatter = formatter;
self.popUpView = [[ASPopUpView alloc] initWithFrame:CGRectZero];
self.popUpViewColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.5 alpha:0.8];
self.popUpViewCornerRadius = 4.0;
self.popUpView.alpha = 0.0;
self.popUpView.delegate = self;
[self addSubview:self.popUpView];
self.textColor = [UIColor whiteColor];
self.font = [UIFont boldSystemFontOfSize:20.0f];
}
// ensure animation restarts if app is closed then becomes active again
- (void)didBecomeActiveNotification:(NSNotification *)note
{
if (self.popUpViewAnimatedColors) {
[self.popUpView setAnimatedColors:_popUpViewAnimatedColors withKeyTimes:_keyTimes];
}
}
- (void)updatePopUpView
{
NSString *progressString; // ask dataSource for string, if nil get string from _numberFormatter
progressString = [self.dataSource progressView:self stringForProgress:self.progress] ?: [_numberFormatter stringFromNumber:@(self.progress)];
if (progressString.length == 0) progressString = @"???"; // replacement for blank string
// set _popUpViewSize to appropriate size for the progressString if required
if ([self.dataSource respondsToSelector:@selector(progressViewShouldPreCalculatePopUpViewSize:)] &&
[self.dataSource progressViewShouldPreCalculatePopUpViewSize:self] == NO)
{
if ([self.dataSource progressView:self stringForProgress:self.progress]) {
_popUpViewSize = [self.popUpView popUpSizeForString:progressString];
} else {
_popUpViewSize = _defaultPopUpViewSize;
}
}
// calculate the popUpView frame
CGRect bounds = self.bounds;
CGFloat xPos = (CGRectGetWidth(bounds) * self.progress) - _popUpViewSize.width/2;
CGRect popUpRect = CGRectMake(xPos, CGRectGetMinY(bounds)-_popUpViewSize.height,
_popUpViewSize.width, _popUpViewSize.height);
// determine if popUpRect extends beyond the frame of the progress view
// if so adjust frame and set the center offset of the PopUpView's arrow
CGFloat minOffsetX = CGRectGetMinX(popUpRect);
CGFloat maxOffsetX = CGRectGetMaxX(popUpRect) - CGRectGetWidth(bounds);
CGFloat offset = minOffsetX < 0.0 ? minOffsetX : (maxOffsetX > 0.0 ? maxOffsetX : 0.0);
popUpRect.origin.x -= offset;
[self.popUpView setFrame:popUpRect arrowOffset:offset text:progressString];
}
- (void)calculatePopUpViewSize
{
_defaultPopUpViewSize = [self.popUpView popUpSizeForString:[_numberFormatter stringFromNumber:@1.0]];;
// if there isn't a dataSource, set _popUpViewSize to _defaultPopUpViewSize
if (!self.dataSource) {
_popUpViewSize = _defaultPopUpViewSize;
return;
}
// if dataSource doesn't want popUpView size precalculated then return early from method
if ([self.dataSource respondsToSelector:@selector(progressViewShouldPreCalculatePopUpViewSize:)]) {
if ([self.dataSource progressViewShouldPreCalculatePopUpViewSize:self] == NO) return;
}
// calculate the largest popUpView size needed to keep the size consistent
// ask the dataSource for values between 0.0 - 1.0 in 0.01 increments
// set size to the largest width and height returned from the dataSource
CGFloat width = 0.0, height = 0.0;
for (int i=0; i<=100; i++) {
NSString *string = [self.dataSource progressView:self stringForProgress:i/100.0];
if (string) {
CGSize size = [self.popUpView popUpSizeForString:string];
if (size.width > width) width = size.width;
if (size.height > height) height = size.height;
}
}
_popUpViewSize = (width > 0.0 && height > 0.0) ? CGSizeMake(width, height) : _defaultPopUpViewSize;
}
#pragma mark - subclassed
-(void)layoutSubviews
{
[super layoutSubviews];
[self updatePopUpView];
}
- (void)didMoveToWindow
{
if (!self.window) { // removed from window - cancel observers and notifications
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
else { // added to window - register observers, notifications and reset animated colors if needed
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didBecomeActiveNotification:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
if (self.popUpViewAnimatedColors) {
[self.popUpView setAnimatedColors:_popUpViewAnimatedColors withKeyTimes:_keyTimes];
}
}
}
- (void)setProgressTintColor:(UIColor *)color
{
self.autoAdjustTrackColor = NO; // if a custom value is set then prevent auto coloring
[super setProgressTintColor:color];
}
- (void)setProgress:(float)progress
{
[super setProgress:progress];
[self.popUpView setAnimationOffset:progress returnColor:^(UIColor *opaqueReturnColor) {
super.progressTintColor = opaqueReturnColor;
}];
}
- (void)setProgress:(float)progress animated:(BOOL)animated
{
if (animated) {
[self.popUpView animateBlock:^(CFTimeInterval duration) {
[UIView animateWithDuration:duration animations:^{
[super setProgress:progress animated:animated];
[self.popUpView setAnimationOffset:progress returnColor:^(UIColor *opaqueReturnColor) {
super.progressTintColor = opaqueReturnColor;
}];
[self layoutIfNeeded];
}];
}];
} else {
[super setProgress:progress animated:animated];
}
}
@end