├── .gitignore ├── LICENSE ├── MWKProgressIndicator.h ├── MWKProgressIndicator.m ├── README.md ├── error.png ├── loading.png └── success.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 maxor92 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MWKProgressIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // MWKProgressIndicator.h 3 | // routemev1 4 | // 5 | // Created by Max Kölb on 28/03/14. 6 | // Copyright (c) 2014 Max Kölb. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, MWKProgressMessageUpdateType) 12 | { 13 | MWKProgressMessageUpdateTypeText, 14 | MWKProgressMessageUpdateTypeVoice, 15 | MWKProgressMessageUpdateTypeAll 16 | }; 17 | 18 | @interface MWKProgressIndicator : UIView 19 | 20 | + (void)show; 21 | 22 | + (void)dismiss; 23 | + (void)dismissWithoutAnimation; 24 | 25 | /// Update progress from 0.0 -> 1.0 26 | + (void)updateProgress:(float)progress; 27 | 28 | /// Updates message 29 | + (void)updateMessage:(NSString *)message; 30 | 31 | /// Updates a combination of voice or text, determined by updatetype 32 | + (void)updateMessage:(NSString *)message type:(MWKProgressMessageUpdateType)type; 33 | 34 | /// Display an error for 2 seconds 35 | + (void)showErrorMessage:(NSString *)errorMessage; 36 | 37 | /// Display success for 2 seconds 38 | + (void)showSuccessMessage:(NSString *)successMessage; 39 | 40 | /// Convenience message for speaking 41 | + (void)speakMessage:(NSString *)message; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /MWKProgressIndicator.m: -------------------------------------------------------------------------------- 1 | // 2 | // MWKProgressIndicator.m 3 | // routemev1 4 | // 5 | // Created by Max Kölb on 28/03/14. 6 | // Copyright (c) 2014 Max Kölb. All rights reserved. 7 | // 8 | 9 | #import "MWKProgressIndicator.h" 10 | 11 | #import 12 | 13 | #define MWKProgressIndicatorHeight 64.0f 14 | 15 | @implementation MWKProgressIndicator 16 | { 17 | float _progress; 18 | UIView *_progressTrack; 19 | 20 | UILabel *_titleLabel; 21 | 22 | BOOL _lock; 23 | } 24 | 25 | + (instancetype)sharedIndicator 26 | { 27 | static MWKProgressIndicator *indicator = nil; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken,^ 30 | { 31 | indicator = [MWKProgressIndicator new]; 32 | }); 33 | return indicator; 34 | } 35 | 36 | - (id)init 37 | { 38 | self = [super init]; 39 | if (!self) return nil; 40 | 41 | self.backgroundColor = [UIColor whiteColor]; 42 | self.frame = CGRectMake(0, -MWKProgressIndicatorHeight, [UIScreen mainScreen].bounds.size.width, MWKProgressIndicatorHeight); 43 | [[[[[UIApplication sharedApplication] keyWindow] subviews] firstObject] addSubview:self]; 44 | 45 | _progress = 0.0; 46 | _progressTrack = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, self.bounds.size.height)]; 47 | [_progressTrack setBackgroundColor:[UIColor colorWithRed:0.53 green:0.82 blue:1 alpha:1]]; 48 | [self addSubview:_progressTrack]; 49 | 50 | float statusBarHeight = 14.0f; 51 | 52 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, statusBarHeight, self.bounds.size.width, MWKProgressIndicatorHeight - statusBarHeight)]; 53 | [_titleLabel setBackgroundColor:[UIColor clearColor]]; 54 | [_titleLabel setTextColor:[UIColor blackColor]]; 55 | [_titleLabel setTextAlignment:NSTextAlignmentCenter]; 56 | [_titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue-Thin" size:20]]; 57 | [_titleLabel setText:@"Loading..."]; 58 | [self addSubview:_titleLabel]; 59 | 60 | _lock = NO; 61 | 62 | return self; 63 | } 64 | 65 | + (void)show 66 | { 67 | MWKProgressIndicator *indicator = [MWKProgressIndicator sharedIndicator]; 68 | if (CGRectGetMinY(indicator.frame) == 0.0) return; 69 | [indicator setTopLocationValue:0]; 70 | } 71 | 72 | + (void)dismiss 73 | { 74 | MWKProgressIndicator *indicator = [MWKProgressIndicator sharedIndicator]; 75 | [indicator dismissAnimated:YES]; 76 | } 77 | 78 | + (void)dismissWithoutAnimation 79 | { 80 | MWKProgressIndicator *indicator = [MWKProgressIndicator sharedIndicator]; 81 | [indicator dismissAnimated:NO]; 82 | } 83 | 84 | - (void)dismissAnimated:(BOOL)animated 85 | { 86 | if (CGRectGetMinY(self.frame) == -MWKProgressIndicatorHeight) return; 87 | 88 | if (animated) 89 | { 90 | [self updateProgress:0.0]; 91 | [self setTopLocationValue:-MWKProgressIndicatorHeight]; 92 | } 93 | else 94 | { 95 | _progressTrack.frame = CGRectMake(0, 0, 0, self.frame.size.height); 96 | [self setTopLocationValue:-MWKProgressIndicatorHeight withDuration:0.0]; 97 | } 98 | } 99 | 100 | - (void)setTopLocationValue:(float)value 101 | { 102 | [self setTopLocationValue:value withDuration:0.4]; 103 | } 104 | 105 | - (void)setTopLocationValue:(float)value withDuration:(float)duration 106 | { 107 | [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.7 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseOut animations:^ 108 | { 109 | [self setFrame:CGRectMake(0, value, self.bounds.size.width, self.bounds.size.height)]; 110 | } 111 | completion:nil]; 112 | } 113 | 114 | + (void)updateProgress:(float)progress 115 | { 116 | MWKProgressIndicator *indicator = [MWKProgressIndicator sharedIndicator]; 117 | [indicator updateProgress:progress]; 118 | } 119 | 120 | - (void)updateProgress:(float)progress 121 | { 122 | if (0.0f <= progress && progress < 1.1f) 123 | { 124 | float units = self.frame.size.width / 1; 125 | _progress = progress; 126 | 127 | [UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:0.8 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^ 128 | { 129 | _progressTrack.frame = CGRectMake(0, 0, units * progress, self.bounds.size.height); 130 | } 131 | completion:nil]; 132 | } 133 | } 134 | 135 | + (void)updateMessage:(NSString *)message 136 | { 137 | [MWKProgressIndicator updateMessage:message type:MWKProgressMessageUpdateTypeText]; 138 | } 139 | 140 | + (void)updateMessage:(NSString *)message type:(MWKProgressMessageUpdateType)type 141 | { 142 | dispatch_async(dispatch_get_main_queue(), ^ 143 | { 144 | if (type == MWKProgressMessageUpdateTypeAll || type == MWKProgressMessageUpdateTypeText) 145 | { 146 | MWKProgressIndicator *indicator = [MWKProgressIndicator sharedIndicator]; 147 | [indicator updateMessage:message]; 148 | } 149 | 150 | if (type == MWKProgressMessageUpdateTypeAll || type == MWKProgressMessageUpdateTypeVoice) 151 | { 152 | [MWKProgressIndicator speakMessage:message]; 153 | } 154 | }); 155 | } 156 | 157 | 158 | - (void)updateMessage:(NSString *)message 159 | { 160 | dispatch_async(dispatch_get_main_queue(), ^ 161 | { 162 | _titleLabel.text = message; 163 | }); 164 | } 165 | 166 | + (void)showErrorMessage:(NSString *)errorMessage 167 | { 168 | [[MWKProgressIndicator sharedIndicator] showWithColor:[UIColor redColor] duration:2 message:errorMessage]; 169 | } 170 | 171 | + (void)showSuccessMessage:(NSString *)successMessage 172 | { 173 | [[MWKProgressIndicator sharedIndicator] showWithColor:[UIColor greenColor] duration:2 message:successMessage]; 174 | } 175 | 176 | - (void)showWithColor:(UIColor *)color duration:(float)duration message:(NSString *)message 177 | { 178 | if (_lock) return; 179 | 180 | _lock = YES; 181 | 182 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 183 | float hideDuration = 0.5; 184 | 185 | [self setTopLocationValue:-MWKProgressIndicatorHeight withDuration:hideDuration]; 186 | 187 | 188 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(hideDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^ 189 | { 190 | [self updateProgress:0.0]; 191 | 192 | [self setTopLocationValue:0 withDuration:hideDuration]; 193 | self.backgroundColor = color; 194 | [self updateMessage:message]; 195 | }); 196 | 197 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((duration+hideDuration) * NSEC_PER_SEC)), dispatch_get_main_queue(),^ 198 | { 199 | 200 | [self setTopLocationValue:-MWKProgressIndicatorHeight withDuration:hideDuration]; 201 | 202 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(hideDuration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^ 203 | { 204 | 205 | self.backgroundColor = [UIColor whiteColor]; 206 | _lock = NO; 207 | }); 208 | }); 209 | }); 210 | } 211 | 212 | - (void)updateTrackColor:(UIColor *)color 213 | { 214 | _progressTrack.backgroundColor = color; 215 | } 216 | 217 | + (void)speakMessage:(NSString *)message 218 | { 219 | static AVSpeechSynthesizer *synthesizer = nil; 220 | 221 | if (!synthesizer) 222 | { 223 | synthesizer = [AVSpeechSynthesizer new]; 224 | } 225 | 226 | AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:message]; 227 | utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"]; 228 | utterance.rate = 0.3; 229 | 230 | [synthesizer speakUtterance:utterance]; 231 | } 232 | 233 | @end 234 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MWKProgressIndicator 2 | ==================== 3 | 4 | A minimal progress indicator for iOS with status update support. Displays above UINavigationBar and below the Status Bar. 5 | 6 | #Show: 7 | ```[MWKProgressIndicator show];``` 8 | 9 | #Dismiss: 10 | ```[MWKProgressIndicator dismiss];``` 11 | 12 | #Update progress: 13 | ```[MWKProgressIndicator updateProgress:0.1f];``` 14 | 15 | #Update message: 16 | ```[MWKProgressIndicator updateMessage:@"Downloading OSM Data"];``` 17 | 18 | ![](https://raw.githubusercontent.com/maxor92/MWKProgressIndicator/master/loading.png) 19 | 20 | #Show success message for 2s: 21 | ```[MWKProgressIndicator showSuccessMessage:@"Finished calculating route!"];``` 22 | 23 | ![](https://raw.githubusercontent.com/maxor92/MWKProgressIndicator/master/success.png) 24 | 25 | #Show error message for 2s: 26 | 27 | ```[MWKProgressIndicator showErrorMessage:@"Parsing error :("];``` 28 | 29 | ![](https://raw.githubusercontent.com/maxor92/MWKProgressIndicator/master/error.png) 30 | -------------------------------------------------------------------------------- /error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwkoelb/MWKProgressIndicator/e3cd67da08227c2f48104da528837adbbb2e79de/error.png -------------------------------------------------------------------------------- /loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwkoelb/MWKProgressIndicator/e3cd67da08227c2f48104da528837adbbb2e79de/loading.png -------------------------------------------------------------------------------- /success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwkoelb/MWKProgressIndicator/e3cd67da08227c2f48104da528837adbbb2e79de/success.png --------------------------------------------------------------------------------