-
Notifications
You must be signed in to change notification settings - Fork 1
/
PDKTProgress.m
239 lines (220 loc) · 8 KB
/
PDKTProgress.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
//
// THProgressHandler.m
// Thoughts
//
// Created by Daniel García García on 6/2/15.
// Copyright (c) 2015 Produkt. All rights reserved.
//
#import "PDKTProgress.h"
static NSString * const subprogressKey = @"subprogress";
static NSString * const subprogressWeightKey = @"subprogressWeight";
@interface PDKTProgress()
@property (assign,nonatomic,readwrite) CGFloat fakeProgressLimit;
@property (assign,nonatomic,readwrite) CGFloat fakeProgressIncrement;
@property (strong,nonatomic) NSHashTable *observersTable;
@property (strong,nonatomic) NSMutableArray *subprogressesWeights;
@property (copy, readwrite) NSDictionary *userInfo;
@end
@interface PDKTProgress (_Observer)
- (void)notifyObserversOfProgressUpdate:(CGFloat)progress;
@end
@interface PDKTProgress (_Subprogress)
- (CGFloat)progressBasedOnSubprogresses;
@end
@implementation PDKTProgress
@synthesize progress = _progress;
@synthesize fakeProgressTimer = _fakeProgressTimer;
- (instancetype)init {
self = [super init];
if (self) {
_observersTable = [NSHashTable weakObjectsHashTable];
_subprogressesWeights = [NSMutableArray array];
}
return self;
}
- (void)reset{
[self performInManThread:^{
for (PDKTProgress *subprogress in self.subprogresses) {
[subprogress reset];
}
self->_progress = 0;
}];
}
- (void)setProgress:(CGFloat)progress{
if (![self isValidProgess:progress]) {
return;
}
[self performInManThread:^{
[self updateAndNotifyProgressWithProgress:progress];
self.fakeProgressTimer = nil;
}];
}
- (void)updateProgressWithProgress:(CGFloat)progress{
_progress = progress;
}
- (void)updateAndNotifyProgressWithProgress:(CGFloat)progress{
[self updateProgressWithProgress:progress];
[self notifyObserversOfProgressUpdate:progress];
}
- (BOOL)isValidProgess:(CGFloat)progress{
return progress>=0 && progress<=1;
}
- (CGFloat)progress{
__block CGFloat currentProgress = 0;
[self performInManThread:^{
if (self.subprogresses.count) {
currentProgress = [self progressBasedOnSubprogresses];
}else{
currentProgress = self->_progress;
}
}];
return currentProgress;
}
- (void)startFakeProgressUntil:(CGFloat)progressLimit withDuration:(NSTimeInterval)progressDuration{
[self performInManThread:^{
self.fakeProgressLimit = progressLimit;
self.fakeProgressIncrement = (progressLimit - self.progress)/(progressDuration/self.fakeProgressTimer.timeInterval);
[self.fakeProgressTimer fire];
}];
}
- (void)performInManThread:(void(^)(void))block{
if ([NSThread isMainThread]) {
block();
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
block();
});
}
- (void)updateFakeProgress{
if (self.progress < self.fakeProgressLimit) {
[self updateAndNotifyProgressWithProgress:(self.progress + self.fakeProgressIncrement)];
}
}
- (NSTimer *)fakeProgressTimer{
if (!_fakeProgressTimer) {
_fakeProgressTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateFakeProgress) userInfo:nil repeats:YES];
}
return _fakeProgressTimer;
}
- (void)setFakeProgressTimer:(NSTimer *)fakeProgressTimer{
if ([_fakeProgressTimer isValid]) {
[_fakeProgressTimer invalidate];
}
_fakeProgressTimer = fakeProgressTimer;
}
- (void)setUserInfoObject:(nullable id)objectOrNil forKey:(nonnull NSString *)key {
NSMutableDictionary *mutableUserInfo = [NSMutableDictionary dictionaryWithDictionary:self.userInfo];
if (!objectOrNil) {
[mutableUserInfo removeObjectForKey:key];
} else {
[mutableUserInfo setObject:objectOrNil forKey:key];
}
self.userInfo = [NSDictionary dictionaryWithDictionary:mutableUserInfo];
}
- (NSString *)description{
NSMutableString *description = [NSMutableString stringWithFormat:@"<%@> (%f)",NSStringFromClass([self class]),self.progress];
if (self.subprogresses.count) {
[description appendFormat:@": %@",self.subprogresses];
}
return description;
}
@end
@implementation PDKTProgress (Observer)
- (NSArray *)observers{
return [self.observersTable allObjects];
}
- (void)addObserver:(id<PDKTProgressObserver>)observer{
[self.observersTable addObject:observer];
}
- (void)removeObserver:(id<PDKTProgressObserver>)observer{
[self.observersTable removeObject:observer];
}
- (void)notifyObserversOfProgressUpdate:(CGFloat)progress{
[self performInManThread:^{
[self notifyObserversOnCurrentThreadOfProgressUpdate:progress];
}];
}
- (void)notifyObserversOnCurrentThreadOfProgressUpdate:(CGFloat)progress{
for (id<PDKTProgressObserver> observer in self.observers) {
[observer progressHandler:self didUpdateProgress:progress];
}
}
#pragma mark - THProgressHandlerObserver
- (void)progressHandler:(PDKTProgress *)progressHandler didUpdateProgress:(CGFloat)progress{
[self notifyObserversOfProgressUpdate:[self progressBasedOnSubprogresses]];
}
@end
@implementation PDKTProgress (Subprogress)
- (NSArray *)subprogresses{
NSMutableArray *subprogresses = [NSMutableArray array];
for (NSDictionary *subprogressWeight in self.subprogressesWeights) {
PDKTProgress *subprogress = subprogressWeight[subprogressKey];
[subprogresses addObject:subprogress];
}
return subprogresses;
}
- (void)addSubprogress:(PDKTProgress *)subprogress{
NSDictionary *subprogressWeight = @{
subprogressKey : subprogress
};
[self addSubprogressWeight:subprogressWeight];
}
- (void)addSubprogress:(PDKTProgress *)subprogress withWeight:(CGFloat)weight{
NSDictionary *subprogressWeight = @{
subprogressKey : subprogress,
subprogressWeightKey : @(weight)
};
[self addSubprogressWeight:subprogressWeight];
}
- (void)addSubprogressWeight:(NSDictionary *)subprogressWeight{
PDKTProgress *subprogress = subprogressWeight[subprogressKey];
NSParameterAssert(subprogress);
[self.subprogressesWeights addObject:subprogressWeight];
[subprogress addObserver:self];
}
- (void)removeSubprogress:(PDKTProgress *)subprogress{
[[self.subprogressesWeights copy] enumerateObjectsUsingBlock:^(NSDictionary *subprogressWeight, NSUInteger idx, BOOL *stop) {
if (subprogressWeight[subprogressKey] == subprogress) {
[self.subprogressesWeights removeObject:subprogressWeight];
}
}];
[subprogress removeObserver:self];
}
- (CGFloat)progressBasedOnSubprogresses{
if ([self allSubprogressesHaveWeight]) {
return [self progressBasedOnWeightedSubprogresses];
}
return [self progressBasedOnEquallyWeightedSubprogresses];
}
- (BOOL)allSubprogressesHaveWeight{
BOOL allSubprogressesHaveWeight = YES;
for (NSDictionary *subprogressWeight in self.subprogressesWeights) {
if (!subprogressWeight[subprogressWeightKey]) {
allSubprogressesHaveWeight = NO;
}
}
return allSubprogressesHaveWeight;
}
- (CGFloat)progressBasedOnWeightedSubprogresses{
CGFloat totalWeight = 0;
for (NSDictionary *subprogressWeight in self.subprogressesWeights) {
totalWeight += [subprogressWeight[subprogressWeightKey] floatValue];
}
CGFloat total = 0;
for (NSDictionary *subprogressWeight in self.subprogressesWeights) {
PDKTProgress *subprogress = subprogressWeight[subprogressKey];
CGFloat subprogressWeightPercentage = (totalWeight * [subprogressWeight[subprogressWeightKey] floatValue])/100;
total += (subprogressWeightPercentage/100)*subprogress.progress;
}
return total;
}
- (CGFloat)progressBasedOnEquallyWeightedSubprogresses{
CGFloat total = 0;
for (NSDictionary *subprogressWeight in self.subprogressesWeights) {
PDKTProgress *subprogress = subprogressWeight[subprogressKey];
total += (1/(CGFloat)self.subprogressesWeights.count)*subprogress.progress;
}
return total;
}
@end