├── screen.png ├── Watch Ring Generator ├── assets │ ├── 38mm@2x.png │ ├── 42mm@2x.png │ ├── bezel38mm@2x.png │ ├── bezel42mm@2x.png │ ├── 38mm-paged@2x.png │ └── 42mm-paged@2x.png ├── Images.xcassets │ ├── bezel38mm.imageset │ │ ├── bezel38mm@2x.png │ │ └── Contents.json │ ├── bezel42mm.imageset │ │ ├── bezel42mm@2x.png │ │ └── Contents.json │ ├── paged38mm.imageset │ │ ├── 38mm-paged@2x.png │ │ └── Contents.json │ ├── paged42mm.imageset │ │ ├── 42mm-paged@2x.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── ViewController.h ├── AppDelegate.h ├── main.m ├── Info.plist ├── AppDelegate.m ├── Base.lproj │ └── LaunchScreen.xib └── ViewController.m ├── Watch Ring Generator.xcodeproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── .gitignore ├── Vendor ├── M13ProgressSuite │ ├── M13ProgressViewMetroDotPolygon.h │ ├── M13ProgressViewPie.h │ ├── M13ProgressViewRadiative.h │ ├── M13ProgressViewRing.h │ ├── M13ProgressView.m │ ├── M13ProgressViewImage.h │ ├── M13ProgressViewSegmentedRing.h │ ├── M13ProgressViewStripedBar.h │ ├── M13ProgressViewMetro.h │ ├── M13ProgressViewBorderedBar.h │ ├── M13ProgressViewBar.h │ ├── M13ProgressViewFilteredImage.h │ ├── M13ProgressView.h │ ├── M13ProgressViewSegmentedBar.h │ ├── M13ProgressViewMetroDotPolygon.m │ ├── M13ProgressViewFilteredImage.m │ ├── M13ProgressViewRadiative.m │ ├── M13ProgressViewImage.m │ ├── M13ProgressViewMetro.m │ ├── M13ProgressViewStripedBar.m │ ├── M13ProgressViewPie.m │ ├── M13ProgressViewRing.m │ └── M13ProgressViewBorderedBar.m ├── NSFileManager+Utilities │ ├── NSFileManager+Utilities.h │ └── NSFileManager+Utilities.m └── UIColor+Utilities │ ├── UIColor-Expanded.h │ └── UIColor-Expanded.m ├── LICENSE └── README.md /screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/screen.png -------------------------------------------------------------------------------- /Watch Ring Generator/assets/38mm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/assets/38mm@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/assets/42mm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/assets/42mm@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/assets/bezel38mm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/assets/bezel38mm@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/assets/bezel42mm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/assets/bezel42mm@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/assets/38mm-paged@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/assets/38mm-paged@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/assets/42mm-paged@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/assets/42mm-paged@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/bezel38mm.imageset/bezel38mm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/Images.xcassets/bezel38mm.imageset/bezel38mm@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/bezel42mm.imageset/bezel42mm@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/Images.xcassets/bezel42mm.imageset/bezel42mm@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/paged38mm.imageset/38mm-paged@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/Images.xcassets/paged38mm.imageset/38mm-paged@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/paged42mm.imageset/42mm-paged@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radianttap/WatchRingGenerator/HEAD/Watch Ring Generator/Images.xcassets/paged42mm.imageset/42mm-paged@2x.png -------------------------------------------------------------------------------- /Watch Ring Generator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside -------------------------------------------------------------------------------- /Watch Ring Generator/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Watch Ring Generator 4 | // 5 | // Created by Aleksandar Vacić on 23.2.15.. 6 | // Copyright (c) 2015. Radiant Tap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Watch Ring Generator/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Watch Ring Generator 4 | // 5 | // Created by Aleksandar Vacić on 23.2.15.. 6 | // Copyright (c) 2015. Radiant Tap. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Watch Ring Generator/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Watch Ring Generator 4 | // 5 | // Created by Aleksandar Vacić on 23.2.15.. 6 | // Copyright (c) 2015. Radiant Tap. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/bezel38mm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "bezel38mm@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/bezel42mm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "bezel42mm@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/paged38mm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "38mm-paged@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/paged42mm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "42mm-paged@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewMetroDotPolygon.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewMetroDotShape.h 3 | // M13ProgressSuite 4 | // 5 | // Created by Brandon McQuilkin on 3/9/14. 6 | // Copyright (c) 2014 Brandon McQuilkin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "M13ProgressViewMetro.h" 11 | 12 | /**A subclass of M13ProgressViewMetroDot.*/ 13 | @interface M13ProgressViewMetroDotPolygon : M13ProgressViewMetroDot 14 | 15 | /**The number of sides the polygon has. 16 | @note if set less than 3, the polygon will be a circle.*/ 17 | @property (nonatomic, assign) NSUInteger numberOfSides; 18 | /**The radius of the polygon.*/ 19 | @property (nonatomic, assign) CGFloat radius; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Aleksandar Vacić 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. 22 | 23 | -------------------------------------------------------------------------------- /Watch Ring Generator/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewPie.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewPie.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | /**A progress view that shows progress with a pie chart.*/ 17 | @interface M13ProgressViewPie : M13ProgressView 18 | 19 | /**@name Appearance*/ 20 | /**The thickness of the border around the pie.*/ 21 | @property (nonatomic, assign) CGFloat backgroundRingWidth; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Vendor/NSFileManager+Utilities/NSFileManager+Utilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | Aleksandar Vacić, Radiant Tap 3 | http://radianttap.com/ 4 | 5 | BSD License, Use at your own risk 6 | 7 | */ 8 | 9 | @import UIKit; 10 | 11 | /* 12 | 13 | Provided by Apple 14 | 15 | NSHomeDirectory() = YOUR_APP/ 16 | NSTemporaryDirectory() = YOUR_APP/tmp/ 17 | 18 | */ 19 | 20 | // Path utilities 21 | NSString *NSDocumentsFolder(); 22 | NSString *NSLibraryFolder(); 23 | NSString *NSBundleFolder(); 24 | NSString *NSDCIMFolder(); 25 | NSString *NSApplicationSupportFolder(); 26 | 27 | /* 28 | 29 | Original work by 30 | Erica Sadun, http://ericasadun.com 31 | iPhone Developer's Cookbook, 3.0 Edition 32 | 33 | */ 34 | @interface NSFileManager (Utilities) 35 | + (NSString *) pathForItemNamed: (NSString *) fname inFolder: (NSString *) path; 36 | + (NSString *) pathForDocumentNamed: (NSString *) fname; 37 | + (NSString *) pathForBundleDocumentNamed: (NSString *) fname; 38 | 39 | + (NSArray *) pathsForItemsMatchingExtension: (NSString *) ext inFolder: (NSString *) path; 40 | + (NSArray *) pathsForDocumentsMatchingExtension: (NSString *) ext; 41 | + (NSArray *) pathsForBundleDocumentsMatchingExtension: (NSString *) ext; 42 | 43 | + (NSArray *) filesInFolder: (NSString *) path; 44 | @end 45 | 46 | 47 | 48 | @interface NSFileManager (RadiantTap) 49 | 50 | + (BOOL)findOrCreateDirectoryPath:(NSString *)path; 51 | + (BOOL)findOrCreateDirectoryPath:(NSString *)path backup:(BOOL)shouldBackup dataProtection:(NSString *)dataProtection; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewRadiative.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewRadiative.h 3 | // M13ProgressSuite 4 | // 5 | // Created by Brandon McQuilkin on 3/13/14. 6 | // Copyright (c) 2014 Brandon McQuilkin. All rights reserved. 7 | // 8 | 9 | #import "M13ProgressView.h" 10 | 11 | typedef enum { 12 | M13ProgressViewRadiativeShapeCircle, 13 | M13ProgressViewRadiativeShapeSquare 14 | } M13ProgressViewRadiativeShape; 15 | 16 | /**A progress view that displays progress via "Radiative" rings around a central point.*/ 17 | @interface M13ProgressViewRadiative : M13ProgressView 18 | 19 | /**@name Appearance*/ 20 | /**The point where the wave fronts originate from. The point is defined in percentages, top left being {0, 0}, and the bottom right being {1, 1}.*/ 21 | @property (nonatomic, assign) CGPoint originationPoint; 22 | /**The distance of the last ripple from the origination point.*/ 23 | @property (nonatomic, assign) CGFloat ripplesRadius; 24 | /**The width of the ripples.*/ 25 | @property (nonatomic, assign) CGFloat rippleWidth; 26 | /**The shape of the radiative ripples*/ 27 | @property (nonatomic, assign) M13ProgressViewRadiativeShape shape; 28 | /**The number of ripples the progress view displays.*/ 29 | @property (nonatomic, assign) NSUInteger numberOfRipples; 30 | /**The number of ripples the indeterminate pulse animation is.*/ 31 | @property (nonatomic, assign) NSUInteger pulseWidth; 32 | /**The direction of the progress. If set to yes, the progress will be outward, of set to no, it will be inwards.*/ 33 | @property (nonatomic, assign) BOOL progressOutwards; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Watch Ring Generator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.radianttap.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 45 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewRing.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewRing.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | /**A progress view stylized similarly to the iOS 7 App store progress view.*/ 17 | @interface M13ProgressViewRing : M13ProgressView 18 | 19 | /**@name Appearance*/ 20 | /**The width of the background ring in points.*/ 21 | @property (nonatomic, assign) CGFloat backgroundRingWidth; 22 | /**The width of the progress ring in points.*/ 23 | @property (nonatomic, assign) CGFloat progressRingWidth; 24 | /**@name Percentage*/ 25 | /**Wether or not to display a percentage inside the ring.*/ 26 | @property (nonatomic, assign) BOOL showPercentage; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressView.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressView.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | @implementation M13ProgressView 17 | 18 | - (id)initWithFrame:(CGRect)frame 19 | { 20 | self = [super initWithFrame:frame]; 21 | if (self) { 22 | // Initialization code 23 | } 24 | return self; 25 | } 26 | 27 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 28 | { 29 | _progress = progress; 30 | } 31 | 32 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 33 | { 34 | //To be overriden in subclasses 35 | } 36 | 37 | /* 38 | // Only override drawRect: if you perform custom drawing. 39 | // An empty implementation adversely affects performance during animation. 40 | - (void)drawRect:(CGRect)rect 41 | { 42 | // Drawing code 43 | } 44 | */ 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | > Note: this is abandoned. I don't recommend to use it in new projects. 3 | --- 4 | 5 | 6 | # WatchRingGenerator 1.1 7 | 8 | iOS app to generate series of PNG images, to be used in WatchKit apps. It’s primarily made to be used in iOS Simulator, so you can easily get to the pics. 9 | I recommend to use iPad Air simulator. 10 | 11 | It looks like this (from ver 1.0): 12 | 13 | ![](screen.png) 14 | 15 | ## Ver 1.0 16 | 17 | You can set your color hex codes and tap Return key to have them instantly applied. Same with other text fields – change value and tap Return to apply it. 18 | The color boxes are actually buttons and I first wanted to add color picker there, but gave up for the lack of time. Progress can go from 0.0 to 1.0 and it is there simply as preview. 19 | 20 | This is the format of the file names: `RINGID-RINGSIZE-RINGWIDTH_COUNTER@2x.png`. It generates 100 pics for each ring. 21 | 22 | * `RINGID` is either `outer` or `innner` 23 | * `RINGSIZE` is size in points, by default it's 120 for outer ring and 100 for inner 24 | * `RINGWIDTH` is also in points, defines how fat the ring is (default is 9) 25 | 26 | Images are generated in `img` folder inside app's `Documents` directory. 27 | 28 | ## Ver 1.1 29 | 30 | Added option to generate .xcassets file, automatically for both 38mm and 42mm. The difference between the pics is 20pt, so it's easy to generate both sizes automatically. 31 | 32 | ### How to use 33 | 34 | * Set the watch size to 38mm (it's the default, but just in case you tried 42mm) 35 | * Setup your rings anyway you want (keep in mind that max size is 134pt) 36 | * Tap `Generate .xcassets` button and look for the app's `Documents` folder in iOS Simulator 37 | 38 | Now just add `Ring.xcassets` into Watch app target (not the extension) and use it like this: 39 | 40 | ```objective-c 41 | [self.progressImage setImageNamed:@"inner-124-12-"]; 42 | ``` 43 | 44 | Have fun creating your images. Code is a bit convoluted, but it does the job and I hope you can find your way around in case you want to change the naming. 45 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewImage.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | typedef enum { 17 | M13ProgressViewImageProgressDirectionLeftToRight, 18 | M13ProgressViewImageProgressDirectionBottomToTop, 19 | M13ProgressViewImageProgressDirectionRightToLeft, 20 | M13ProgressViewImageProgressDirectionTopToBottom 21 | } M13ProgressViewImageProgressDirection; 22 | 23 | /**A progress bar where progress is shown by cutting an image. 24 | @note This progress bar does not have in indeterminate mode and does not respond to actions.*/ 25 | @interface M13ProgressViewImage : M13ProgressView 26 | 27 | /**@name Appearance*/ 28 | /**The image to use when showing progress.*/ 29 | @property (nonatomic, retain) UIImage *progressImage; 30 | /**The direction of progress. (What direction the fill proceeds in.)*/ 31 | @property (nonatomic, assign) M13ProgressViewImageProgressDirection progressDirection; 32 | /**Wether or not to draw the greyscale background.*/ 33 | @property (nonatomic, assign) BOOL drawGreyscaleBackground; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /Watch Ring Generator/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Watch Ring Generator 4 | // 5 | // Created by Aleksandar Vacić on 23.2.15.. 6 | // Copyright (c) 2015. Radiant Tap. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | 32 | [[NSUserDefaults standardUserDefaults] synchronize]; 33 | } 34 | 35 | - (void)applicationWillEnterForeground:(UIApplication *)application { 36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | - (void)applicationDidBecomeActive:(UIApplication *)application { 40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 41 | } 42 | 43 | - (void)applicationWillTerminate:(UIApplication *)application { 44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewSegmentedRing.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressVewSegmentedRing.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | typedef enum { 15 | M13ProgressViewSegmentedRingSegmentBoundaryTypeWedge, 16 | M13ProgressViewSegmentedRingSegmentBoundaryTypeRectangle 17 | } M13ProgressViewSegmentedRingSegmentBoundaryType; 18 | 19 | #import "M13ProgressView.h" 20 | 21 | /**Progress is shown by a ring split up into segments.*/ 22 | @interface M13ProgressViewSegmentedRing : M13ProgressView 23 | 24 | /**@name Appearance*/ 25 | /**The width of the progress ring in points.*/ 26 | @property (nonatomic, assign) CGFloat progressRingWidth; 27 | /**The number of segments to display in the progress view.*/ 28 | @property (nonatomic, assign) NSInteger numberOfSegments; 29 | /**The angle of the separation between the segments in radians.*/ 30 | @property (nonatomic, assign) CGFloat segmentSeparationAngle; 31 | /**The type of boundary between segments.*/ 32 | @property (nonatomic, assign) M13ProgressViewSegmentedRingSegmentBoundaryType segmentBoundaryType; 33 | /**@name Percentage*/ 34 | /**Wether or not to display a percentage inside the ring.*/ 35 | @property (nonatomic, assign) BOOL showPercentage; 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewStripedBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewStripedBar.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | typedef enum { 17 | M13ProgressViewStripedBarCornerTypeSquare, 18 | M13ProgressViewStripedBarCornerTypeRounded, 19 | M13ProgressViewStripedBarCornerTypeCircle 20 | } M13ProgressViewStripedBarCornerType; 21 | 22 | /**A progress bar that is striped, and can animate the stripes if desired.*/ 23 | @interface M13ProgressViewStripedBar : M13ProgressView 24 | 25 | /**@name Appearance*/ 26 | /**The type of corner to display on the bar.*/ 27 | @property (nonatomic, assign) M13ProgressViewStripedBarCornerType cornerType; 28 | /**The radius of the corner if the corner type is set to rounded rect.*/ 29 | @property (nonatomic, assign) CGFloat cornerRadius; 30 | /**The width of the stripes if shown.*/ 31 | @property (nonatomic, assign) CGFloat stripeWidth; 32 | /**Wether or not the stripes are animated.*/ 33 | @property (nonatomic, assign) BOOL animateStripes; 34 | /**Wether or not to show the stripes.*/ 35 | @property (nonatomic, assign) BOOL showStripes; 36 | /**The color of the stripes.*/ 37 | @property (nonatomic, retain) UIColor *stripeColor; 38 | /**The width of the border.*/ 39 | @property (nonatomic, assign) CGFloat borderWidth; 40 | 41 | /**The inner color of the progress area .*/ 42 | @property (nonatomic, retain) UIColor *innerFillColor; 43 | @end 44 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewMetro.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewMetro.h 3 | // M13ProgressSuite 4 | // 5 | // Created by Brandon McQuilkin on 3/8/14. 6 | // Copyright (c) 2014 Brandon McQuilkin. All rights reserved. 7 | // 8 | 9 | #import "M13ProgressView.h" 10 | 11 | typedef enum { 12 | M13ProgressViewMetroAnimationShapeEllipse, 13 | M13ProgressViewMetroAnimationShapeRectangle, 14 | M13ProgressViewMetroAnimationShapeLine 15 | } M13ProgressViewMetroAnimationShape; 16 | 17 | /**The layer that the `M13ProgressViewMetro` animates.*/ 18 | @interface M13ProgressViewMetroDot : CALayer 19 | 20 | /**Wether or not the dot is highlighted. The dot becomes highlighted to show progress.*/ 21 | @property (nonatomic, assign) BOOL highlighted; 22 | /**The color to show on success.*/ 23 | @property (nonatomic, retain) UIColor *successColor; 24 | /**The color to show on failure.*/ 25 | @property (nonatomic, retain) UIColor *failureColor; 26 | /**The primary color of the dot.*/ 27 | @property (nonatomic, retain) UIColor *primaryColor; 28 | /**The secondary color of the dot.*/ 29 | @property (nonatomic, retain) UIColor *secondaryColor; 30 | /**Perform the given action if defined. Usually showing success or failure. 31 | @param action The action to perform. 32 | @param animated Wether or not to animate the change*/ 33 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated; 34 | /**All subclasses must respond to NSCopying.*/ 35 | - (id)copy; 36 | 37 | @end 38 | 39 | /**A progress view based off of Windows 8's progress animation.*/ 40 | @interface M13ProgressViewMetro : M13ProgressView 41 | 42 | /**@name Properties*/ 43 | /**The number of dots in the animation.*/ 44 | @property (nonatomic, assign) NSUInteger numberOfDots; 45 | /**The shape of the animation.*/ 46 | @property (nonatomic, assign) M13ProgressViewMetroAnimationShape animationShape; 47 | /**The size of the dots*/ 48 | @property (nonatomic, assign) CGSize dotSize; 49 | /**The dot to display.*/ 50 | @property (nonatomic, retain) M13ProgressViewMetroDot *metroDot; 51 | /**@name Appearance*/ 52 | /**The color to show on success.*/ 53 | @property (nonatomic, retain) UIColor *successColor; 54 | /**The color to show on failure.*/ 55 | @property (nonatomic, retain) UIColor *failureColor; 56 | /**@name Actions*/ 57 | /**Wether or not the progress view animating.*/ 58 | - (BOOL)isAnimating; 59 | /**Begin the animation.*/ 60 | - (void)beginAnimating; 61 | /**End the animation.*/ 62 | - (void)stopAnimating; 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /Vendor/UIColor+Utilities/UIColor-Expanded.h: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | 3 | #define SUPPORTS_UNDOCUMENTED_API 0 4 | 5 | @interface UIColor (UIColor_Expanded) 6 | @property (nonatomic, readonly) CGColorSpaceModel colorSpaceModel; 7 | @property (nonatomic, readonly) BOOL canProvideRGBComponents; 8 | @property (nonatomic, readonly) CGFloat red; // Only valid if canProvideRGBComponents is YES 9 | @property (nonatomic, readonly) CGFloat green; // Only valid if canProvideRGBComponents is YES 10 | @property (nonatomic, readonly) CGFloat blue; // Only valid if canProvideRGBComponents is YES 11 | @property (nonatomic, readonly) CGFloat white; // Only valid if colorSpaceModel == kCGColorSpaceModelMonochrome 12 | @property (nonatomic, readonly) CGFloat alpha; 13 | @property (nonatomic, readonly) UInt32 rgbHex; 14 | 15 | - (NSString *)colorSpaceString; 16 | 17 | - (NSArray *)arrayFromRGBAComponents; 18 | 19 | - (BOOL)red:(CGFloat *)r green:(CGFloat *)g blue:(CGFloat *)b alpha:(CGFloat *)a; 20 | 21 | - (UIColor *)colorByLuminanceMapping; 22 | 23 | - (UIColor *)colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 24 | - (UIColor *) colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 25 | - (UIColor *) colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 26 | - (UIColor *) colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; 27 | 28 | - (UIColor *)colorByMultiplyingBy:(CGFloat)f; 29 | - (UIColor *) colorByAdding:(CGFloat)f; 30 | - (UIColor *) colorByLighteningTo:(CGFloat)f; 31 | - (UIColor *) colorByDarkeningTo:(CGFloat)f; 32 | 33 | - (UIColor *)colorByMultiplyingByColor:(UIColor *)color; 34 | - (UIColor *) colorByAddingColor:(UIColor *)color; 35 | - (UIColor *) colorByLighteningToColor:(UIColor *)color; 36 | - (UIColor *) colorByDarkeningToColor:(UIColor *)color; 37 | 38 | - (NSString *)stringFromColor; 39 | - (NSString *)hexStringFromColor; 40 | 41 | + (UIColor *)randomColor; 42 | + (UIColor *)colorWithString:(NSString *)stringToConvert; 43 | + (UIColor *)colorWithRGBHex:(UInt32)hex; 44 | + (UIColor *)colorWithHexString:(NSString *)stringToConvert; 45 | 46 | + (UIColor *)colorWithName:(NSString *)cssColorName; 47 | 48 | @end 49 | 50 | #if SUPPORTS_UNDOCUMENTED_API 51 | // UIColor_Undocumented_Expanded 52 | // Methods which rely on undocumented methods of UIColor 53 | @interface UIColor (UIColor_Undocumented_Expanded) 54 | - (NSString *)fetchStyleString; 55 | - (UIColor *)rgbColor; // Via Poltras 56 | @end 57 | #endif // SUPPORTS_UNDOCUMENTED_API 58 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewBorderedBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewBorderedBar.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | typedef enum { 17 | M13ProgressViewBorderedBarProgressDirectionLeftToRight, 18 | M13ProgressViewBorderedBarProgressDirectionBottomToTop, 19 | M13ProgressViewBorderedBarProgressDirectionRightToLeft, 20 | M13ProgressViewBorderedBarProgressDirectionTopToBottom 21 | } M13ProgressViewBorderedBarProgressDirection; 22 | 23 | typedef enum { 24 | M13ProgressViewBorderedBarCornerTypeSquare, 25 | M13ProgressViewBorderedBarCornerTypeRounded, 26 | M13ProgressViewBorderedBarCornerTypeCircle 27 | } M13ProgressViewBorderedBarCornerType; 28 | 29 | /**A Progress bar with a thick border.*/ 30 | @interface M13ProgressViewBorderedBar : M13ProgressView 31 | 32 | /**@name Appearance*/ 33 | /**The direction of progress. (What direction the fill proceeds in.)*/ 34 | @property (nonatomic, assign) M13ProgressViewBorderedBarProgressDirection progressDirection; 35 | /**The type of corner to display on the bar.*/ 36 | @property (nonatomic, assign) M13ProgressViewBorderedBarCornerType cornerType; 37 | /**The corner radius of the ber if the corner type is set to Rounded.*/ 38 | @property (nonatomic, assign) CGFloat cornerRadius; 39 | /**The border width of the progress bar.*/ 40 | @property (nonatomic, assign) CGFloat borderWidth; 41 | /**@name Actions*/ 42 | /**The color the bar changes to for the success action.*/ 43 | @property (nonatomic, retain) UIColor *successColor; 44 | /**The color the bar changes to for the failure action.*/ 45 | @property (nonatomic, retain) UIColor *failureColor; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewBar.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | typedef enum { 17 | M13ProgressViewBarPercentagePositionLeft, 18 | M13ProgressViewBarPercentagePositionRight, 19 | M13ProgressViewBarPercentagePositionTop, 20 | M13ProgressViewBarPercentagePositionBottom, 21 | } M13ProgressViewBarPercentagePosition; 22 | 23 | typedef enum { 24 | M13ProgressViewBarProgressDirectionLeftToRight, 25 | M13ProgressViewBarProgressDirectionBottomToTop, 26 | M13ProgressViewBarProgressDirectionRightToLeft, 27 | M13ProgressViewBarProgressDirectionTopToBottom 28 | } M13ProgressViewBarProgressDirection; 29 | 30 | /**A replacement for UIProgressBar.*/ 31 | @interface M13ProgressViewBar : M13ProgressView 32 | 33 | /**@name Appearance*/ 34 | /**The direction of progress. (What direction the fill proceeds in.)*/ 35 | @property (nonatomic, assign) M13ProgressViewBarProgressDirection progressDirection; 36 | /**The thickness of the progress bar.*/ 37 | @property (nonatomic, assign) CGFloat progressBarThickness; 38 | /**@name Actions*/ 39 | /**The color the bar changes to for the success action.*/ 40 | @property (nonatomic, retain) UIColor *successColor; 41 | /**The color the bar changes to for the failure action.*/ 42 | @property (nonatomic, retain) UIColor *failureColor; 43 | /**@name Percentage*/ 44 | /**Wether or not to show percentage text. If shown exterior to the progress bar, the progress bar is shifted to make room for the text.*/ 45 | @property (nonatomic, assign) BOOL showPercentage; 46 | /**The location of the percentage in comparison to the progress bar.*/ 47 | @property (nonatomic, assign) M13ProgressViewBarPercentagePosition percentagePosition; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewFilteredImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewFilteredImage.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | @import CoreImage; 16 | 17 | #define kM13ProgressViewFilteredImageCIFilterStartValuesKey @"StartValues" 18 | #define kM13ProgressViewFilteredImageCIFilterEndValuesKey @"EndValues" 19 | 20 | /**A progress view where progress is shown by changes in CIFilters. 21 | @note This progress bar does not have in indeterminate mode and does not respond to actions.*/ 22 | @interface M13ProgressViewFilteredImage : M13ProgressView 23 | 24 | /**@name Appearance*/ 25 | /**The image to use when showing progress.*/ 26 | @property (nonatomic, retain) UIImage *progressImage; 27 | /**The UIImageView that shows the progress image.*/ 28 | @property (nonatomic, retain) UIImageView *progressView; 29 | /**The array of CIFilters to apply to the image. 30 | @note The filters need to be encased in a M13ProgressViewCIFilterWrapper*/ 31 | @property (nonatomic, retain) NSArray *filters; 32 | /**The dictionaries of dictionaries that coresspond to filter properties to be changed. 33 | NSArray 34 | |------ NSDictionary (Index matches the coresponding CIFilter in filters) 35 | | |---- "Parameter Key" -> NSDictionary 36 | | | |------ "Start Value" -> NSNumber 37 | | | |------ "End Value" -> NSNumber 38 | | |---- "Parameter Key" -> NSDictionary 39 | | |------ "Start Value" -> NSNumber 40 | | |------ "End Value" -> NSNumber 41 | |------ NSDictionary ... */ 42 | @property (nonatomic, retain) NSArray *filterParameters; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressView.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressView.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | @import UIKit; 15 | 16 | typedef enum { 17 | /**Resets the action and returns the progress view to its normal state.*/ 18 | M13ProgressViewActionNone, 19 | /**The progress view shows success.*/ 20 | M13ProgressViewActionSuccess, 21 | /**The progress view shows failure.*/ 22 | M13ProgressViewActionFailure 23 | } M13ProgressViewAction; 24 | 25 | /**A standardized base upon which to build progress views for applications. This allows one to use any subclass progress view in any component that use this standard.*/ 26 | @interface M13ProgressView : UIView 27 | 28 | /**@name Appearance*/ 29 | /**The primary color of the `M13ProgressView`.*/ 30 | @property (nonatomic, retain) UIColor *primaryColor; 31 | /**The secondary color of the `M13ProgressView`.*/ 32 | @property (nonatomic, retain) UIColor *secondaryColor; 33 | 34 | /**@name Properties*/ 35 | /**Wether or not the progress view is indeterminate.*/ 36 | @property (nonatomic, assign) BOOL indeterminate; 37 | /**The durations of animations in seconds.*/ 38 | @property (nonatomic, assign) CGFloat animationDuration; 39 | /**The progress displayed to the user.*/ 40 | @property (nonatomic, readonly) CGFloat progress; 41 | 42 | /**@name Actions*/ 43 | /**Set the progress of the `M13ProgressView`. 44 | @param progress The progress to show on the progress view. 45 | @param animated Wether or not to animate the progress change.*/ 46 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated; 47 | /**Perform the given action if defined. Usually showing success or failure. 48 | @param action The action to perform. 49 | @param animated Wether or not to animate the change*/ 50 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewSegmentedBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewSegmentedBar.h 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressView.h" 15 | 16 | typedef enum { 17 | M13ProgressViewSegmentedBarProgressDirectionLeftToRight, 18 | M13ProgressViewSegmentedBarProgressDirectionBottomToTop, 19 | M13ProgressViewSegmentedBarProgressDirectionRightToLeft, 20 | M13ProgressViewSegmentedBarProgressDirectionTopToBottom 21 | } M13ProgressViewSegmentedBarProgressDirection; 22 | 23 | typedef enum { 24 | M13ProgressViewSegmentedBarSegmentShapeRectangle, 25 | M13ProgressViewSegmentedBarSegmentShapeRoundedRect, 26 | M13ProgressViewSegmentedBarSegmentShapeCircle 27 | } M13ProgressViewSegmentedBarSegmentShape; 28 | 29 | /**A progress bar that shows progress with discrete values.*/ 30 | @interface M13ProgressViewSegmentedBar : M13ProgressView 31 | 32 | /**@name Appearance*/ 33 | /**The direction of progress. (What direction the fill proceeds in.)*/ 34 | @property (nonatomic, assign) M13ProgressViewSegmentedBarProgressDirection progressDirection; 35 | /**The shape of the segments.*/ 36 | @property (nonatomic, assign) M13ProgressViewSegmentedBarSegmentShape segmentShape; 37 | /**The corner radius of the segment shape if the shape is set to M13ProgressViewSegmentedBarSegmentShapeRoundedRect.*/ 38 | @property (nonatomic, assign) CGFloat cornerRadius; 39 | /**The number of segments to display in the progress view.*/ 40 | @property (nonatomic, assign) NSInteger numberOfSegments; 41 | /**The separation between segments in points. Must be less than self.width / numberOfSegments.*/ 42 | @property (nonatomic, assign) CGFloat segmentSeparation; 43 | /**@name Actions*/ 44 | /**The color the bar changes to for the success action.*/ 45 | @property (nonatomic, retain) UIColor *successColor; 46 | /**The color the bar changes to for the failure action.*/ 47 | @property (nonatomic, retain) UIColor *failureColor; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewMetroDotPolygon.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewMetroDotShape.m 3 | // M13ProgressSuite 4 | // 5 | // Created by Brandon McQuilkin on 3/9/14. 6 | // Copyright (c) 2014 Brandon McQuilkin. All rights reserved. 7 | // 8 | 9 | #import "M13ProgressViewMetroDotPolygon.h" 10 | 11 | @implementation M13ProgressViewMetroDotPolygon 12 | { 13 | CAShapeLayer *shapeLayer; 14 | M13ProgressViewAction currentAction; 15 | } 16 | 17 | - (void)setNumberOfSides:(NSUInteger)numberOfSides 18 | { 19 | _numberOfSides = numberOfSides; 20 | [self setNeedsDisplay]; 21 | } 22 | 23 | - (void)setRadius:(CGFloat)radius 24 | { 25 | _radius = radius; 26 | [self setNeedsDisplay]; 27 | } 28 | 29 | - (NSArray *)verticies 30 | { 31 | if (_numberOfSides < 3) { 32 | return nil; 33 | } else { 34 | NSMutableArray *pointsArray = [NSMutableArray array]; 35 | for (int i = 0; i < _numberOfSides; i++) { 36 | CGPoint point = CGPointMake(_radius * cosf((2.0 * M_PI * (float)i) / (float)_numberOfSides), _radius * sinf((2.0 * M_PI * (float)i) / (float)_numberOfSides)); 37 | NSValue *value = [NSValue valueWithCGPoint:point]; 38 | [pointsArray addObject:value]; 39 | } 40 | return pointsArray; 41 | } 42 | } 43 | 44 | - (void)drawInContext:(CGContextRef)ctx 45 | { 46 | //Create and add the polygon layer if it does not exist 47 | if (shapeLayer == nil) { 48 | shapeLayer = [CAShapeLayer layer]; 49 | [self addSublayer:shapeLayer]; 50 | } 51 | //Create the path for the polygon 52 | UIBezierPath *path = [UIBezierPath bezierPath]; 53 | NSArray *verticies = [self verticies]; 54 | if (verticies == nil) { 55 | //Draw circle 56 | path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, _radius * 2, _radius * 2)]; 57 | } else { 58 | [path moveToPoint:((NSValue *)verticies[0]).CGPointValue]; 59 | for (int i = 1; i < verticies.count; i++) { 60 | [path addLineToPoint:((NSValue *)verticies[i]).CGPointValue]; 61 | } 62 | [path closePath]; 63 | } 64 | //Set the shape layer's path 65 | shapeLayer.path = path.CGPath; 66 | 67 | //Set the color of the polygon 68 | shapeLayer.fillColor = self.secondaryColor.CGColor; 69 | if (self.highlighted) { 70 | shapeLayer.fillColor = self.primaryColor.CGColor; 71 | } 72 | if (currentAction == M13ProgressViewActionSuccess) { 73 | shapeLayer.fillColor = self.successColor.CGColor; 74 | } else if (currentAction == M13ProgressViewActionFailure) { 75 | shapeLayer.fillColor = self.failureColor.CGColor; 76 | } 77 | 78 | //Draw 79 | [super drawInContext:ctx]; 80 | } 81 | 82 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 83 | { 84 | currentAction = action; 85 | [self setNeedsDisplay]; 86 | } 87 | 88 | - (id)copy 89 | { 90 | M13ProgressViewMetroDotPolygon *dot = [[M13ProgressViewMetroDotPolygon alloc] init]; 91 | dot.primaryColor = self.primaryColor; 92 | dot.secondaryColor = self.secondaryColor; 93 | dot.successColor = self.successColor; 94 | dot.failureColor = self.failureColor; 95 | dot.numberOfSides = _numberOfSides; 96 | dot.radius = _radius; 97 | return dot; 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /Watch Ring Generator/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Vendor/NSFileManager+Utilities/NSFileManager+Utilities.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Aleksandar Vacić, Radiant Tap 4 | http://radianttap.com/ 5 | 6 | BSD License, Use at your own risk 7 | 8 | */ 9 | 10 | #import "NSFileManager+Utilities.h" 11 | 12 | NSString *NSDocumentsFolder() { 13 | 14 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 15 | NSString *documentsDirectory = paths[0]; 16 | return documentsDirectory; 17 | } 18 | 19 | NSString *NSLibraryFolder() { 20 | 21 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); 22 | NSString *libraryDirectory = paths[0]; 23 | return libraryDirectory; 24 | } 25 | 26 | NSString *NSApplicationSupportFolder() { 27 | 28 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); 29 | NSString *libraryDirectory = paths[0]; 30 | return libraryDirectory; 31 | } 32 | 33 | NSString *NSBundleFolder() { 34 | 35 | return [[NSBundle mainBundle] bundlePath]; 36 | } 37 | 38 | NSString *NSDCIMFolder() { 39 | 40 | return @"/var/mobile/Media/DCIM"; 41 | } 42 | 43 | @implementation NSFileManager (Utilities) 44 | 45 | + (NSString *) pathForItemNamed: (NSString *) fname inFolder: (NSString *) path { 46 | NSString *file; 47 | NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path]; 48 | while (file = [dirEnum nextObject]) 49 | if ([[file lastPathComponent] isEqualToString:fname]) 50 | return [path stringByAppendingPathComponent:file]; 51 | return nil; 52 | } 53 | 54 | + (NSString *) pathForDocumentNamed: (NSString *) fname { 55 | 56 | return [NSFileManager pathForItemNamed:fname inFolder:NSDocumentsFolder()]; 57 | } 58 | 59 | + (NSString *) pathForBundleDocumentNamed: (NSString *) fname { 60 | 61 | return [NSFileManager pathForItemNamed:fname inFolder:NSBundleFolder()]; 62 | } 63 | 64 | + (NSArray *) filesInFolder: (NSString *) path { 65 | 66 | NSString *file; 67 | NSMutableArray *results = [NSMutableArray array]; 68 | NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path]; 69 | while (file = [dirEnum nextObject]) 70 | { 71 | BOOL isDir; 72 | [[NSFileManager defaultManager] fileExistsAtPath:[path stringByAppendingPathComponent:file] isDirectory: &isDir]; 73 | if (!isDir) [results addObject:file]; 74 | } 75 | return results; 76 | } 77 | 78 | // Case insensitive compare, with deep enumeration 79 | + (NSArray *) pathsForItemsMatchingExtension: (NSString *) ext inFolder: (NSString *) path { 80 | 81 | NSString *file; 82 | NSMutableArray *results = [NSMutableArray array]; 83 | NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path]; 84 | while (file = [dirEnum nextObject]) 85 | if ([[file pathExtension] caseInsensitiveCompare:ext] == NSOrderedSame) 86 | [results addObject:[path stringByAppendingPathComponent:file]]; 87 | return results; 88 | } 89 | 90 | + (NSArray *) pathsForDocumentsMatchingExtension: (NSString *) ext { 91 | 92 | return [NSFileManager pathsForItemsMatchingExtension:ext inFolder:NSDocumentsFolder()]; 93 | } 94 | 95 | // Case insensitive compare 96 | + (NSArray *) pathsForBundleDocumentsMatchingExtension: (NSString *) ext { 97 | 98 | return [NSFileManager pathsForItemsMatchingExtension:ext inFolder:NSBundleFolder()]; 99 | } 100 | 101 | 102 | @end 103 | 104 | 105 | 106 | 107 | @implementation NSFileManager (RadiantTap) 108 | 109 | - (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL { 110 | 111 | NSError *error = nil; 112 | BOOL success = [URL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&error]; 113 | if (!success) { 114 | NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); 115 | } 116 | 117 | return success; 118 | } 119 | 120 | + (BOOL)findOrCreateDirectoryPath:(NSString *)path { 121 | 122 | return [NSFileManager findOrCreateDirectoryPath:path backup:YES dataProtection:nil]; 123 | } 124 | 125 | + (BOOL)findOrCreateDirectoryPath:(NSString *)path backup:(BOOL)shouldBackup dataProtection:(NSString *)dataProtection { 126 | 127 | BOOL isDirectory; 128 | BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory]; 129 | if (exists) { 130 | if (isDirectory) return YES; 131 | return NO; 132 | } 133 | 134 | // 135 | // Create the path if it doesn't exist 136 | // 137 | NSError *error; 138 | BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; 139 | 140 | // 141 | if (success && !shouldBackup) { 142 | // try to set no-iCloud-backup folder, but do not fail everything if this particular bit fails 143 | [[NSFileManager defaultManager] addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:path]]; 144 | } 145 | 146 | if (success && dataProtection != nil) { 147 | // also, set protection level as requested 148 | [[NSFileManager defaultManager] setAttributes:@{NSFileProtectionKey:dataProtection} ofItemAtPath:path error:&error]; 149 | } 150 | 151 | return success; 152 | } 153 | 154 | // example how to add your own stuff, for the particular app you work with 155 | //+ (NSString *)RTOrdersFolder { 156 | // 157 | // NSString *f = [NSDocumentsFolder() stringByAppendingPathComponent:@"orders"]; 158 | // if ([NSFileManager findOrCreateDirectoryPath:f shouldBackup:YES dataProtection:NSFileProtectionComplete]) 159 | // return f; 160 | // 161 | // return nil; 162 | //} 163 | 164 | @end -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewFilteredImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewFilteredImage.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressViewFilteredImage.h" 15 | 16 | @interface M13ProgressViewFilteredImage () 17 | 18 | /**The start progress for the progress animation.*/ 19 | @property (nonatomic, assign) CGFloat animationFromValue; 20 | /**The end progress for the progress animation.*/ 21 | @property (nonatomic, assign) CGFloat animationToValue; 22 | /**The start time interval for the animaiton.*/ 23 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 24 | /**Link to the display to keep animations in sync.*/ 25 | @property (nonatomic, strong) CADisplayLink *displayLink; 26 | 27 | @end 28 | 29 | @implementation M13ProgressViewFilteredImage 30 | 31 | #pragma mark Initalization and setup 32 | 33 | - (id)init 34 | { 35 | self = [super init]; 36 | if (self) { 37 | [self setup]; 38 | } 39 | return self; 40 | } 41 | 42 | - (id)initWithFrame:(CGRect)frame 43 | { 44 | self = [super initWithFrame:frame]; 45 | if (self) { 46 | [self setup]; 47 | } 48 | return self; 49 | } 50 | 51 | - (id)initWithCoder:(NSCoder *)aDecoder 52 | { 53 | self = [super initWithCoder:aDecoder]; 54 | if (self) { 55 | [self setup]; 56 | } 57 | return self; 58 | } 59 | 60 | - (void)setup 61 | { 62 | //Set own background color 63 | self.backgroundColor = [UIColor clearColor]; 64 | 65 | //Set defauts 66 | self.animationDuration = .3; 67 | 68 | //Set up the progress view 69 | _progressView = [[UIImageView alloc] init]; 70 | _progressView.contentMode = UIViewContentModeScaleAspectFit; 71 | _progressView.frame = self.bounds; 72 | [self addSubview:_progressView]; 73 | 74 | //Layout 75 | [self layoutSubviews]; 76 | } 77 | 78 | #pragma mark Appearance 79 | 80 | - (void)setProgressImage:(UIImage *)progressImage 81 | { 82 | _progressImage = progressImage; 83 | [_progressView setImage:_progressImage]; 84 | [self setNeedsDisplay]; 85 | } 86 | 87 | - (void)setFilters:(NSArray *)filters 88 | { 89 | _filters = filters; 90 | [self setNeedsDisplay]; 91 | } 92 | 93 | - (void)setFilterParameters:(NSArray *)filterParameters 94 | { 95 | _filterParameters = filterParameters; 96 | } 97 | 98 | #pragma mark Actions 99 | 100 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 101 | { 102 | if (animated == NO) { 103 | if (_displayLink) { 104 | //Kill running animations 105 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 106 | _displayLink = nil; 107 | } 108 | [super setProgress:progress animated:NO]; 109 | [self setNeedsDisplay]; 110 | } else { 111 | _animationStartTime = CACurrentMediaTime(); 112 | _animationFromValue = self.progress; 113 | _animationToValue = progress; 114 | if (!_displayLink) { 115 | //Create and setup the display link 116 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 117 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 118 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 119 | } /*else { 120 | //Reuse the current display link 121 | }*/ 122 | } 123 | } 124 | 125 | - (void)animateProgress:(CADisplayLink *)displayLink 126 | { 127 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 128 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 129 | if (dt >= 1.0) { 130 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 131 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 132 | self.displayLink = nil; 133 | dispatch_async(dispatch_get_main_queue(), ^{ 134 | [super setProgress:_animationToValue animated:NO]; 135 | [self setNeedsDisplay]; 136 | }); 137 | return; 138 | } 139 | 140 | dispatch_async(dispatch_get_main_queue(), ^{ 141 | //Set progress 142 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 143 | [self setNeedsDisplay]; 144 | }); 145 | 146 | }); 147 | } 148 | 149 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 150 | { 151 | //Do Nothing 152 | } 153 | 154 | - (void)setIndeterminate:(BOOL)indeterminate 155 | { 156 | [super setIndeterminate:indeterminate]; 157 | //Do Nothing 158 | } 159 | 160 | #pragma mark Layout 161 | 162 | - (void)layoutSubviews 163 | { 164 | [super layoutSubviews]; 165 | _progressView.frame = self.bounds; 166 | } 167 | 168 | #pragma mark Drawing 169 | 170 | - (void)drawRect:(CGRect)rect 171 | { 172 | //Create the base CIImage 173 | CIImage *image = [CIImage imageWithCGImage:_progressImage.CGImage]; 174 | //Change the values of the CIFilters before drawing 175 | for (int i = 0; i < _filters.count; i++) { 176 | CIFilter *filter = _filters[i]; 177 | //For each filter 178 | NSDictionary *parameters = _filterParameters[i]; 179 | //For each parameter 180 | for (NSString *parameterKey in parameters.allKeys) { 181 | //Retreive the values 182 | NSDictionary *parameterDict = parameters[parameterKey]; 183 | CGFloat startValue = [parameterDict[kM13ProgressViewFilteredImageCIFilterStartValuesKey] floatValue]; 184 | CGFloat endValue = [parameterDict[kM13ProgressViewFilteredImageCIFilterEndValuesKey] floatValue]; 185 | //Calculate the current value 186 | CGFloat value = startValue + ((endValue - startValue) * self.progress); 187 | //Set the value 188 | [filter setValue:[NSNumber numberWithFloat:value] forKey:parameterKey]; 189 | } 190 | //Set the input image 191 | [filter setValue:image forKey:kCIInputImageKey]; 192 | image = filter.outputImage; 193 | } 194 | //Set the image of the image view. 195 | [_progressView setImage:[UIImage imageWithCIImage:image]]; 196 | 197 | [super drawRect:rect]; 198 | } 199 | 200 | @end 201 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewRadiative.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewRadiative.m 3 | // M13ProgressSuite 4 | // 5 | // Created by Brandon McQuilkin on 3/13/14. 6 | // Copyright (c) 2014 Brandon McQuilkin. All rights reserved. 7 | // 8 | 9 | #import "M13ProgressViewRadiative.h" 10 | @import QuartzCore; 11 | 12 | @interface M13ProgressViewRadiative () 13 | 14 | /**The start progress for the progress animation.*/ 15 | @property (nonatomic, assign) CGFloat animationFromValue; 16 | /**The end progress for the progress animation.*/ 17 | @property (nonatomic, assign) CGFloat animationToValue; 18 | /**The start time interval for the animaiton.*/ 19 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 20 | /**Link to the display to keep animations in sync.*/ 21 | @property (nonatomic, strong) CADisplayLink *displayLink; 22 | 23 | @end 24 | 25 | @implementation M13ProgressViewRadiative 26 | { 27 | NSMutableArray *ripplePaths; 28 | } 29 | 30 | - (id)init 31 | { 32 | self = [super init]; 33 | if (self) { 34 | [self setup]; 35 | } 36 | return self; 37 | } 38 | 39 | - (id)initWithFrame:(CGRect)frame 40 | { 41 | self = [super initWithFrame:frame]; 42 | if (self) { 43 | [self setup]; 44 | } 45 | return self; 46 | } 47 | 48 | - (id)initWithCoder:(NSCoder *)aDecoder 49 | { 50 | self = [super initWithCoder:aDecoder]; 51 | if (self) { 52 | [self setup]; 53 | } 54 | return self; 55 | } 56 | 57 | - (void)setup 58 | { 59 | //Set own background color 60 | self.backgroundColor = [UIColor clearColor]; 61 | self.clipsToBounds = YES; 62 | 63 | //Set defauts 64 | self.animationDuration = 1.0; 65 | _originationPoint = CGPointMake(0.5, 0.5); 66 | self.numberOfRipples = 10; 67 | self.shape = M13ProgressViewRadiativeShapeCircle; 68 | _rippleWidth = 1.0; 69 | _ripplesRadius = 20; 70 | _pulseWidth = 5; 71 | _progressOutwards = YES; 72 | 73 | //Set default colors 74 | self.primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 75 | self.secondaryColor = [UIColor colorWithRed:181/255.0 green:182/255.0 blue:183/255.0 alpha:1.0]; 76 | } 77 | 78 | #pragma mark Setters 79 | 80 | - (void)setOriginationPoint:(CGPoint)originationPoint 81 | { 82 | _originationPoint = originationPoint; 83 | [self setNeedsLayout]; 84 | [self setNeedsDisplay]; 85 | } 86 | 87 | - (void)setRipplesRadius:(CGFloat)ripplesRadius 88 | { 89 | _ripplesRadius = ripplesRadius; 90 | [self setNeedsLayout]; 91 | [self setNeedsDisplay]; 92 | } 93 | 94 | - (void)setNumberOfRipples:(NSUInteger)numberOfRipples 95 | { 96 | _numberOfRipples = numberOfRipples; 97 | [self setNeedsLayout]; 98 | [self setNeedsDisplay]; 99 | } 100 | 101 | - (void)setRippleWidth:(CGFloat)rippleWidth 102 | { 103 | _rippleWidth = rippleWidth; 104 | for (UIBezierPath *path in ripplePaths) { 105 | path.lineWidth = _rippleWidth; 106 | } 107 | [self setIndeterminate:self.indeterminate]; 108 | } 109 | 110 | - (void)setShape:(M13ProgressViewRadiativeShape)shape 111 | { 112 | _shape = shape; 113 | [self setNeedsLayout]; 114 | [self setNeedsDisplay]; 115 | } 116 | 117 | - (void)setPulseWidth:(NSUInteger)pulseWidth 118 | { 119 | _pulseWidth = pulseWidth; 120 | self.indeterminate = self.indeterminate; 121 | } 122 | 123 | - (void)setProgressOutwards:(BOOL)progressOutwards 124 | { 125 | _progressOutwards = progressOutwards; 126 | [self setNeedsDisplay]; 127 | } 128 | 129 | - (void)setPrimaryColor:(UIColor *)primaryColor 130 | { 131 | [super setPrimaryColor:primaryColor]; 132 | [self setNeedsDisplay]; 133 | } 134 | 135 | - (void)setSecondaryColor:(UIColor *)secondaryColor 136 | { 137 | [super setSecondaryColor:secondaryColor]; 138 | [self setNeedsDisplay]; 139 | } 140 | 141 | #pragma mark animations 142 | 143 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 144 | { 145 | if (animated == NO) { 146 | if (_displayLink) { 147 | //Kill running animations 148 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 149 | _displayLink = nil; 150 | } 151 | [super setProgress:progress animated:NO]; 152 | [self setNeedsDisplay]; 153 | } else { 154 | _animationStartTime = CACurrentMediaTime(); 155 | _animationFromValue = self.progress; 156 | _animationToValue = progress; 157 | if (!_displayLink) { 158 | //Create and setup the display link 159 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 160 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 161 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 162 | } /*else { 163 | //Reuse the current display link 164 | }*/ 165 | } 166 | } 167 | 168 | - (void)animateProgress:(CADisplayLink *)displayLink 169 | { 170 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 171 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 172 | if (dt >= 1.0) { 173 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 174 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 175 | self.displayLink = nil; 176 | dispatch_async(dispatch_get_main_queue(), ^{ 177 | [super setProgress:_animationToValue animated:NO]; 178 | [self setNeedsDisplay]; 179 | }); 180 | return; 181 | } 182 | 183 | dispatch_async(dispatch_get_main_queue(), ^{ 184 | //Set progress 185 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 186 | [self setNeedsDisplay]; 187 | }); 188 | 189 | }); 190 | } 191 | 192 | - (void)setIndeterminate:(BOOL)indeterminate 193 | { 194 | [super setIndeterminate:indeterminate]; 195 | //Need animation 196 | } 197 | 198 | #pragma mark Layout 199 | 200 | - (void)layoutSubviews 201 | { 202 | [super layoutSubviews]; 203 | //Create the paths to draw the ripples 204 | ripplePaths = [NSMutableArray array]; 205 | for (int i = 0; i < _numberOfRipples - 1; i++) { 206 | if (_shape == M13ProgressViewRadiativeShapeCircle) { 207 | //If circular 208 | UIBezierPath *path = [UIBezierPath bezierPath]; 209 | //Calculate the radius 210 | CGFloat radius = _ripplesRadius * ((float)i / (float)(_numberOfRipples - 1)); 211 | //Draw the arc 212 | [path moveToPoint:CGPointMake((_originationPoint.x * self.bounds.size.width)+ radius, _originationPoint.y * self.bounds.size.height)]; 213 | [path addArcWithCenter:CGPointMake(self.bounds.size.width * _originationPoint.x, self.bounds.size.height * _originationPoint.y) radius:radius startAngle:0.0 endAngle:(2 * M_PI) clockwise:YES]; 214 | //Set the width 215 | path.lineWidth = _rippleWidth; 216 | [ripplePaths addObject:path]; 217 | } else if (_shape == M13ProgressViewRadiativeShapeSquare) { 218 | //If square 219 | CGFloat radius = _ripplesRadius * ((float)i / (float)(_numberOfRipples - 1)); 220 | CGFloat delta = radius * (1 / sqrtf(2)); 221 | UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake((_originationPoint.x * self.bounds.size.width) - delta, (_originationPoint.y * self.bounds.size.height) - delta, delta * 2, delta * 2)]; 222 | path.lineWidth = _rippleWidth; 223 | [ripplePaths addObject:path]; 224 | } 225 | } 226 | } 227 | 228 | #pragma mark Drawing 229 | 230 | - (void)drawRect:(CGRect)rect 231 | { 232 | [super drawRect:rect]; 233 | //Get the current context 234 | CGContextRef context = UIGraphicsGetCurrentContext(); 235 | //For each of the paths draw it in the view. 236 | NSEnumerator *enumerator; 237 | if (_progressOutwards) { 238 | enumerator = [ripplePaths objectEnumerator]; 239 | } else { 240 | enumerator = [ripplePaths reverseObjectEnumerator]; 241 | } 242 | 243 | UIBezierPath *path; 244 | int i = 0; 245 | int indexOfLastFilledPath = ceilf(self.progress * (float)_numberOfRipples); 246 | while ((path = [enumerator nextObject])) { 247 | //Set the path's color 248 | if (!self.indeterminate) { 249 | //Show progress 250 | if (i <= indexOfLastFilledPath && self.progress != 0) { 251 | //Highlighted 252 | CGContextSetStrokeColorWithColor(context, self.primaryColor.CGColor); 253 | CGContextAddPath(context, path.CGPath); 254 | CGContextStrokePath(context); 255 | } else { 256 | //Not highlighted 257 | CGContextSetStrokeColorWithColor(context, self.secondaryColor.CGColor); 258 | CGContextAddPath(context, path.CGPath); 259 | CGContextStrokePath(context); 260 | } 261 | i++; 262 | } else { 263 | //Indeterminate 264 | 265 | } 266 | } 267 | } 268 | 269 | @end 270 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewImage.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressViewImage.h" 15 | 16 | @interface M13ProgressViewImage () 17 | 18 | /**The start progress for the progress animation.*/ 19 | @property (nonatomic, assign) CGFloat animationFromValue; 20 | /**The end progress for the progress animation.*/ 21 | @property (nonatomic, assign) CGFloat animationToValue; 22 | /**The start time interval for the animaiton.*/ 23 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 24 | /**Link to the display to keep animations in sync.*/ 25 | @property (nonatomic, strong) CADisplayLink *displayLink; 26 | /**Allow us to write to the progress.*/ 27 | @property (nonatomic, readwrite) CGFloat progress; 28 | /**The UIImageView that shows the progress image.*/ 29 | @property (nonatomic, retain) UIImageView *progressView; 30 | 31 | @end 32 | 33 | @implementation M13ProgressViewImage 34 | @dynamic progress; 35 | 36 | #pragma mark Initalization and setup 37 | 38 | - (id)init 39 | { 40 | self = [super init]; 41 | if (self) { 42 | [self setup]; 43 | } 44 | return self; 45 | } 46 | 47 | - (id)initWithFrame:(CGRect)frame 48 | { 49 | self = [super initWithFrame:frame]; 50 | if (self) { 51 | [self setup]; 52 | } 53 | return self; 54 | } 55 | 56 | - (id)initWithCoder:(NSCoder *)aDecoder 57 | { 58 | self = [super initWithCoder:aDecoder]; 59 | if (self) { 60 | [self setup]; 61 | } 62 | return self; 63 | } 64 | 65 | - (void)setup 66 | { 67 | //Set own background color 68 | self.backgroundColor = [UIColor clearColor]; 69 | 70 | //Set defauts 71 | self.animationDuration = .3; 72 | _progressDirection = M13ProgressViewImageProgressDirectionLeftToRight; 73 | _drawGreyscaleBackground = YES; 74 | 75 | //Set the progress view 76 | _progressView = [[UIImageView alloc] init]; 77 | _progressView.frame = self.bounds; 78 | _progressView.contentMode = UIViewContentModeScaleAspectFit; 79 | [self addSubview:_progressView]; 80 | 81 | //Layout 82 | [self layoutSubviews]; 83 | } 84 | 85 | #pragma mark Appearance 86 | 87 | - (void)setProgressDirection:(M13ProgressViewImageProgressDirection)progressDirection 88 | { 89 | _progressDirection = progressDirection; 90 | [self setNeedsDisplay]; 91 | } 92 | 93 | - (void)setDrawGreyscaleBackground:(BOOL)drawGreyscaleBackground 94 | { 95 | _drawGreyscaleBackground = drawGreyscaleBackground; 96 | [self setNeedsDisplay]; 97 | } 98 | 99 | - (void)setProgressImage:(UIImage *)progressImage 100 | { 101 | _progressImage = progressImage; 102 | _progressView.image = _progressImage; 103 | [self setNeedsDisplay]; 104 | } 105 | 106 | #pragma mark Actions 107 | 108 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 109 | { 110 | if (animated == NO) { 111 | if (_displayLink) { 112 | //Kill running animations 113 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 114 | _displayLink = nil; 115 | } 116 | [super setProgress:progress animated:NO]; 117 | [self setNeedsDisplay]; 118 | } else { 119 | _animationStartTime = CACurrentMediaTime(); 120 | _animationFromValue = self.progress; 121 | _animationToValue = progress; 122 | if (!_displayLink) { 123 | //Create and setup the display link 124 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 125 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 126 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 127 | } /*else { 128 | //Reuse the current display link 129 | }*/ 130 | } 131 | } 132 | 133 | - (void)animateProgress:(CADisplayLink *)displayLink 134 | { 135 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 136 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 137 | if (dt >= 1.0) { 138 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 139 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 140 | self.displayLink = nil; 141 | dispatch_async(dispatch_get_main_queue(), ^{ 142 | [super setProgress:_animationToValue animated:NO]; 143 | [self setNeedsDisplay]; 144 | }); 145 | return; 146 | } 147 | 148 | dispatch_async(dispatch_get_main_queue(), ^{ 149 | //Set progress 150 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 151 | [self setNeedsDisplay]; 152 | }); 153 | 154 | }); 155 | } 156 | 157 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 158 | { 159 | //Do Nothing 160 | } 161 | 162 | - (void)setIndeterminate:(BOOL)indeterminate 163 | { 164 | [super setIndeterminate:indeterminate]; 165 | //Do Nothing 166 | } 167 | 168 | #pragma mark Layout 169 | 170 | - (void)layoutSubviews 171 | { 172 | [super layoutSubviews]; 173 | _progressView.frame = self.bounds; 174 | } 175 | 176 | #pragma mark Drawing 177 | 178 | - (void)drawRect:(CGRect)rect 179 | { 180 | [super drawRect:rect]; 181 | 182 | [_progressView setImage:[self createImageForCurrentProgress]]; 183 | } 184 | 185 | - (UIImage *)createImageForCurrentProgress 186 | { 187 | const int ALPHA = 0; 188 | const int RED = 1; 189 | const int GREEN = 2; 190 | const int BLUE = 3; 191 | 192 | //Create image rectangle with current image width/height 193 | CGRect imageRect = CGRectMake(0, 0, _progressImage.size.width * _progressImage.scale, _progressImage.size.height * _progressImage.scale); 194 | 195 | int width = imageRect.size.width; 196 | int height = imageRect.size.height; 197 | 198 | //The pixels will be painted to this array 199 | uint32_t *pixels = (uint32_t *) malloc(width * height * sizeof(uint32_t)); 200 | 201 | //Clear the pixels so any transparency is preserved 202 | memset(pixels, 0, width * height * sizeof(uint32_t)); 203 | 204 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 205 | 206 | //Create a context with RGBA pixels 207 | CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast); 208 | 209 | //Paint the bitmap to our context which will fill in the pixels array 210 | CGContextDrawImage(context, CGRectMake(0, 0, width, height), _progressImage.CGImage); 211 | 212 | //Calculate the ranges to make greyscale or transparent. 213 | int xFrom = 0; 214 | int xTo = width; 215 | int yFrom = 0; 216 | int yTo = height; 217 | 218 | if (_progressDirection == M13ProgressViewImageProgressDirectionBottomToTop) { 219 | yTo = height * (1 - self.progress); 220 | } else if (_progressDirection == M13ProgressViewImageProgressDirectionTopToBottom) { 221 | yFrom = height * self.progress; 222 | } else if (_progressDirection == M13ProgressViewImageProgressDirectionLeftToRight) { 223 | xFrom = width * self.progress; 224 | } else if (_progressDirection == M13ProgressViewImageProgressDirectionRightToLeft) { 225 | xTo = width * (1 - self.progress); 226 | } 227 | 228 | for (int x = xFrom; x < xTo; x++) { 229 | for (int y = yFrom; y < yTo; y++) { 230 | //Get the pixel 231 | uint8_t *rgbaPixel = (uint8_t *) &pixels[y * width + x]; 232 | //Convert 233 | if (_drawGreyscaleBackground) { 234 | //Convert to grayscale using luma coding: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale 235 | uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE]; 236 | // set the pixels to gray 237 | rgbaPixel[RED] = gray; 238 | rgbaPixel[GREEN] = gray; 239 | rgbaPixel[BLUE] = gray; 240 | } else { 241 | //Convert the pixels to transparant 242 | rgbaPixel[RED] = 0; 243 | rgbaPixel[GREEN] = 0; 244 | rgbaPixel[BLUE] = 0; 245 | rgbaPixel[ALPHA] = 0; 246 | } 247 | } 248 | } 249 | 250 | // create a new CGImageRef from our context with the modified pixels 251 | CGImageRef image = CGBitmapContextCreateImage(context); 252 | 253 | // we're done with the context, color space, and pixels 254 | CGContextRelease(context); 255 | CGColorSpaceRelease(colorSpace); 256 | free(pixels); 257 | 258 | // make a new UIImage to return 259 | UIImage *resultUIImage = [UIImage imageWithCGImage:image scale:_progressImage.scale orientation:UIImageOrientationUp]; 260 | 261 | // we're done with image now too 262 | CGImageRelease(image); 263 | 264 | return resultUIImage; 265 | } 266 | 267 | @end 268 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewMetro.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewMetro.m 3 | // M13ProgressSuite 4 | // 5 | // Created by Brandon McQuilkin on 3/8/14. 6 | // Copyright (c) 2014 Brandon McQuilkin. All rights reserved. 7 | // 8 | 9 | #import "M13ProgressViewMetro.h" 10 | #import "M13ProgressViewMetroDotPolygon.h" 11 | 12 | @interface M13ProgressViewMetro () 13 | 14 | /**The start progress for the progress animation.*/ 15 | @property (nonatomic, assign) CGFloat animationFromValue; 16 | /**The end progress for the progress animation.*/ 17 | @property (nonatomic, assign) CGFloat animationToValue; 18 | /**The start time interval for the animaiton.*/ 19 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 20 | /**Link to the display to keep animations in sync.*/ 21 | @property (nonatomic, strong) CADisplayLink *displayLink; 22 | 23 | @end 24 | 25 | @implementation M13ProgressViewMetroDot 26 | { 27 | M13ProgressViewAction currentAction; 28 | } 29 | 30 | #pragma mark Initalization 31 | 32 | - (id)init 33 | { 34 | self = [super init]; 35 | if (self) { 36 | [self setup]; 37 | } 38 | return self; 39 | } 40 | 41 | - (id)initWithCoder:(NSCoder *)aDecoder 42 | { 43 | self = [super initWithCoder:aDecoder]; 44 | if (self) { 45 | [self setup]; 46 | } 47 | return self; 48 | } 49 | 50 | - (id)initWithLayer:(id)layer 51 | { 52 | self = [super initWithLayer:layer]; 53 | if (self) { 54 | [self setup]; 55 | } 56 | return self; 57 | } 58 | 59 | + (id)layer 60 | { 61 | return [[M13ProgressViewMetroDot alloc] init]; 62 | } 63 | 64 | - (void)setup 65 | { 66 | _highlighted = NO; 67 | currentAction = M13ProgressViewActionNone; 68 | _primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 69 | _secondaryColor = [UIColor colorWithRed:181/255.0 green:182/255.0 blue:183/255.0 alpha:1.0]; 70 | _successColor = [UIColor colorWithRed:63.0f/255.0f green:226.0f/255.0f blue:80.0f/255.0f alpha:1]; 71 | _failureColor = [UIColor colorWithRed:249.0f/255.0f green:37.0f/255.0f blue:0 alpha:1]; 72 | } 73 | 74 | #pragma mark Setters 75 | 76 | - (void)setHighlighted:(BOOL)highlighted 77 | { 78 | _highlighted = highlighted; 79 | [self setNeedsDisplay]; 80 | } 81 | 82 | - (void)setSuccessColor:(UIColor *)successColor 83 | { 84 | _successColor = successColor; 85 | [self setNeedsDisplay]; 86 | } 87 | 88 | - (void)setFailureColor:(UIColor *)failureColor 89 | { 90 | _failureColor = failureColor; 91 | [self setNeedsDisplay]; 92 | } 93 | 94 | - (void)setPrimaryColor:(UIColor *)primaryColor 95 | { 96 | _primaryColor = primaryColor; 97 | [self setNeedsDisplay]; 98 | } 99 | 100 | - (void)setSecondaryColor:(UIColor *)secondaryColor 101 | { 102 | _secondaryColor = secondaryColor; 103 | [self setNeedsDisplay]; 104 | } 105 | 106 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 107 | { 108 | currentAction = action; 109 | [self setNeedsDisplay]; 110 | } 111 | 112 | @end 113 | 114 | @implementation M13ProgressViewMetro 115 | { 116 | BOOL animating; 117 | NSUInteger currentNumberOfDots; 118 | NSTimer *animationTimer; 119 | UIBezierPath *animationPath; 120 | NSMutableArray *dots; 121 | 122 | CGFloat circleSize; 123 | } 124 | 125 | - (id)init 126 | { 127 | self = [super init]; 128 | if (self) { 129 | [self setup]; 130 | } 131 | return self; 132 | } 133 | 134 | - (id)initWithFrame:(CGRect)frame 135 | { 136 | self = [super initWithFrame:frame]; 137 | if (self) { 138 | [self setup]; 139 | } 140 | return self; 141 | } 142 | 143 | - (id)initWithCoder:(NSCoder *)aDecoder 144 | { 145 | self = [super initWithCoder:aDecoder]; 146 | if (self) { 147 | [self setup]; 148 | } 149 | return self; 150 | } 151 | 152 | - (void)setup 153 | { 154 | //Set defaults 155 | self.clipsToBounds = YES; 156 | animating = NO; 157 | _numberOfDots = 6; 158 | _dotSize = CGSizeMake(20, 20); 159 | self.animationDuration = 1.5; 160 | self.animationShape = M13ProgressViewMetroAnimationShapeEllipse; 161 | self.primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 162 | self.secondaryColor = [UIColor colorWithRed:181/255.0 green:182/255.0 blue:183/255.0 alpha:1.0]; 163 | _successColor = [UIColor colorWithRed:63.0f/255.0f green:226.0f/255.0f blue:80.0f/255.0f alpha:1]; 164 | _failureColor = [UIColor colorWithRed:249.0f/255.0f green:37.0f/255.0f blue:0 alpha:1]; 165 | _metroDot = [[M13ProgressViewMetroDotPolygon alloc] init]; 166 | } 167 | 168 | #pragma mark Properties 169 | 170 | - (void)setFrame:(CGRect)frame 171 | { 172 | [super setFrame:frame]; 173 | if (animating) { 174 | [self stopAnimating]; 175 | [self beginAnimating]; 176 | } 177 | } 178 | 179 | - (BOOL)isAnimating 180 | { 181 | return animating; 182 | } 183 | 184 | - (void)setAnimationShape:(M13ProgressViewMetroAnimationShape)animationShape 185 | { 186 | _animationShape = animationShape; 187 | if (_animationShape == M13ProgressViewMetroAnimationShapeEllipse) { 188 | //Inset the size of the dot 189 | CGFloat radius = MIN((self.bounds.size.width - _dotSize.width) / 2, (self.bounds.size.height - _dotSize.height) / 2); 190 | //Create the path 191 | animationPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2) radius:radius startAngle:M_PI_2 endAngle:(-M_PI_2 * 3) clockwise:YES]; 192 | } else if (_animationShape == M13ProgressViewMetroAnimationShapeRectangle) { 193 | //Inset the size of the dot 194 | CGRect pathRect = CGRectInset(self.bounds, _dotSize.width, _dotSize.height); 195 | //Create the path 196 | animationPath = [UIBezierPath bezierPath]; 197 | [animationPath moveToPoint:CGPointMake((pathRect.size.width / 2) + pathRect.origin.x, pathRect.size.height + pathRect.origin.y)]; 198 | [animationPath addLineToPoint:CGPointMake(pathRect.origin.x, pathRect.size.height + pathRect.origin.y)]; 199 | [animationPath addLineToPoint:CGPointMake(pathRect.origin.x, pathRect.origin.y)]; 200 | [animationPath addLineToPoint:CGPointMake(pathRect.origin.x + pathRect.size.width, pathRect.origin.y)]; 201 | [animationPath addLineToPoint:CGPointMake(pathRect.origin.x + pathRect.size.width, pathRect.origin.y + pathRect.size.height)]; 202 | [animationPath moveToPoint:CGPointMake((pathRect.size.width / 2) + pathRect.origin.x, pathRect.size.height + pathRect.origin.y)]; 203 | } else if (animationShape == M13ProgressViewMetroAnimationShapeLine) { 204 | //Create the path 205 | animationPath = [UIBezierPath bezierPath]; 206 | [animationPath moveToPoint:CGPointMake(-_dotSize.width, self.bounds.size.height / 2)]; 207 | [animationPath addLineToPoint:CGPointMake(self.bounds.size.width + _dotSize.width, self.bounds.size.width / 2)]; 208 | } 209 | [self stopAnimating]; 210 | [self beginAnimating]; 211 | } 212 | 213 | - (void)setNumberOfDots:(NSUInteger)numberOfDots 214 | { 215 | _numberOfDots = numberOfDots; 216 | [self stopAnimating]; 217 | [self beginAnimating]; 218 | } 219 | 220 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 221 | { 222 | for (M13ProgressViewMetroDot *dot in dots) { 223 | [dot performAction:action animated:animated]; 224 | } 225 | } 226 | 227 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 228 | { 229 | if (animated == NO) { 230 | if (_displayLink) { 231 | //Kill running animations 232 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 233 | _displayLink = nil; 234 | } 235 | [super setProgress:progress animated:NO]; 236 | [self showProgress]; 237 | } else { 238 | _animationStartTime = CACurrentMediaTime(); 239 | _animationFromValue = self.progress; 240 | _animationToValue = progress; 241 | if (!_displayLink) { 242 | //Create and setup the display link 243 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 244 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 245 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 246 | } /*else { 247 | //Reuse the current display link 248 | }*/ 249 | } 250 | } 251 | 252 | - (void)animateProgress:(CADisplayLink *)displayLink 253 | { 254 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 255 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 256 | if (dt >= 1.0) { 257 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 258 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 259 | self.displayLink = nil; 260 | dispatch_async(dispatch_get_main_queue(), ^{ 261 | [super setProgress:_animationToValue animated:NO]; 262 | [self showProgress]; 263 | }); 264 | return; 265 | } 266 | 267 | dispatch_async(dispatch_get_main_queue(), ^{ 268 | //Set progress 269 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 270 | [self showProgress]; 271 | }); 272 | 273 | }); 274 | } 275 | 276 | - (void)showProgress 277 | { 278 | static int pastIndexToHighlightTo = 0; 279 | int indexToHighlightTo = ceilf(_numberOfDots * self.progress); 280 | //Only perform the animation if necessary. 281 | if (pastIndexToHighlightTo != indexToHighlightTo) { 282 | for (int i = 0; i < _numberOfDots; i++) { 283 | M13ProgressViewMetroDot *dot = dots[i]; 284 | if (i <= indexToHighlightTo && self.progress != 0) { 285 | dot.highlighted = YES; 286 | } else { 287 | dot.highlighted = NO; 288 | } 289 | } 290 | pastIndexToHighlightTo = indexToHighlightTo; 291 | } 292 | 293 | } 294 | 295 | #pragma mark Animation 296 | 297 | -(void)beginAnimating 298 | { 299 | if (!animating){ 300 | 301 | animating = YES; 302 | 303 | currentNumberOfDots = 0; 304 | dots = [[NSMutableArray alloc] init]; 305 | 306 | //add circles 307 | animationTimer = [NSTimer scheduledTimerWithTimeInterval: 0.20 target: self 308 | selector: @selector(createCircle) userInfo: nil repeats: YES]; 309 | } 310 | } 311 | 312 | -(void)createCircle 313 | { 314 | if (currentNumberOfDots<_numberOfDots){ 315 | 316 | currentNumberOfDots ++; 317 | CGRect f; 318 | if (_animationShape != M13ProgressViewMetroAnimationShapeLine) { 319 | f = CGRectMake((self.frame.size.width - _dotSize.width) / 2 - 1, self.frame.size.height - _dotSize.height - 1, _dotSize.width, _dotSize.height); 320 | } else { 321 | f = CGRectMake(- _dotSize.width, self.bounds.size.height / 2, _dotSize.width, _dotSize.height); 322 | } 323 | M13ProgressViewMetroDot *dotLayer = [_metroDot copy]; 324 | dotLayer.frame = f; 325 | [self.layer addSublayer:dotLayer]; 326 | [dots addObject:dotLayer]; 327 | 328 | CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 329 | animation.duration = self.animationDuration; 330 | animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.15f :0.60f :0.85f :0.4f]; 331 | [animation setCalculationMode:kCAAnimationPaced]; 332 | animation.path = animationPath.CGPath; 333 | animation.repeatCount = HUGE_VALF; 334 | 335 | if (_animationShape != M13ProgressViewMetroAnimationShapeLine) { 336 | //No delay needed 337 | [dotLayer addAnimation:animation forKey:@"metroAnimation"]; 338 | } else { 339 | //Delay repeat 340 | animation.repeatCount = 1; 341 | CAAnimationGroup *group = [CAAnimationGroup animation]; 342 | group.duration = self.animationDuration * 2; 343 | group.animations = @[animation]; 344 | group.repeatCount = HUGE_VALF; 345 | [dotLayer addAnimation:group forKey:@"metroAnimation"]; 346 | } 347 | 348 | 349 | } else { 350 | [animationTimer invalidate]; 351 | } 352 | } 353 | 354 | -(void)stopAnimating 355 | { 356 | animating = NO; 357 | [animationTimer invalidate]; 358 | for (CALayer *layer in dots) { 359 | [layer removeAllAnimations]; 360 | [layer removeFromSuperlayer]; 361 | } 362 | } 363 | 364 | @end 365 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewStripedBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewStripedBar.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressViewStripedBar.h" 15 | 16 | @interface M13ProgressViewStripedBar () 17 | /**The start progress for the progress animation.*/ 18 | @property (nonatomic, assign) CGFloat animationFromValue; 19 | /**The end progress for the progress animation.*/ 20 | @property (nonatomic, assign) CGFloat animationToValue; 21 | /**The start time interval for the animaiton.*/ 22 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 23 | /**Link to the display to keep animations in sync.*/ 24 | @property (nonatomic, strong) CADisplayLink *displayLink; 25 | /**Allow us to write to the progress.*/ 26 | @property (nonatomic, readwrite) CGFloat progress; 27 | /**The layer that contains the progress layer. That also masks the progress layer.*/ 28 | @property (nonatomic, retain) CALayer *progressSuperLayer; 29 | /**The layer that displays progress in the progress bar.*/ 30 | @property (nonatomic, retain) CALayer *progressLayer; 31 | /**The layer that masks the stripes of the progress layer.*/ 32 | @property (nonatomic, retain) CAShapeLayer *progressMaskLayer; 33 | /**The mask layer for the progress layer.*/ 34 | @property (nonatomic, retain) CAShapeLayer *maskLayer; 35 | /**The background layer that displays the border.*/ 36 | @property (nonatomic, retain) CAShapeLayer *backgroundLayer; 37 | /**The layer that is used to animate indeterminate progress.*/ 38 | @property (nonatomic, retain) CALayer *indeterminateLayer; 39 | /**The action currently being performed.*/ 40 | @property (nonatomic, assign) M13ProgressViewAction currentAction; 41 | /**The stripes layer.*/ 42 | @property (nonatomic, retain) CAShapeLayer *stripesLayer; 43 | @end 44 | 45 | @implementation M13ProgressViewStripedBar 46 | { 47 | UIColor *_currentColor; 48 | } 49 | @dynamic progress; 50 | 51 | 52 | #pragma mark Initalization and setup 53 | 54 | - (id)init 55 | { 56 | self = [super init]; 57 | if (self) { 58 | [self setup]; 59 | } 60 | return self; 61 | } 62 | 63 | - (id)initWithFrame:(CGRect)frame 64 | { 65 | self = [super initWithFrame:frame]; 66 | if (self) { 67 | [self setup]; 68 | } 69 | return self; 70 | } 71 | 72 | - (id)initWithCoder:(NSCoder *)aDecoder 73 | { 74 | self = [super initWithCoder:aDecoder]; 75 | if (self) { 76 | [self setup]; 77 | } 78 | return self; 79 | } 80 | 81 | - (void)setup 82 | { 83 | //Set own background color 84 | self.backgroundColor = [UIColor clearColor]; 85 | 86 | //Set defauts 87 | self.animationDuration = .3; 88 | _cornerType = M13ProgressViewStripedBarCornerTypeSquare; 89 | _cornerRadius = 3.0; 90 | _stripeWidth = 7.0; 91 | _borderWidth = 1.0; 92 | _showStripes = YES; 93 | 94 | //Set default colors 95 | self.primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 96 | self.secondaryColor = [UIColor colorWithRed:181/255.0 green:182/255.0 blue:183/255.0 alpha:1.0]; 97 | self.stripeColor = [UIColor whiteColor]; 98 | _currentColor = self.primaryColor; 99 | 100 | //BackgroundLayer 101 | _backgroundLayer = [CAShapeLayer layer]; 102 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 103 | _backgroundLayer.fillColor = nil; 104 | _backgroundLayer.lineWidth = _borderWidth; 105 | [self.layer addSublayer:_backgroundLayer]; 106 | 107 | //Main mask layer 108 | _progressSuperLayer = [CALayer layer]; 109 | _maskLayer = [CAShapeLayer layer]; 110 | _maskLayer.fillColor = [UIColor blackColor].CGColor; 111 | _maskLayer.backgroundColor = [UIColor clearColor].CGColor; 112 | _progressSuperLayer.mask = _maskLayer; 113 | [self.layer addSublayer:_progressSuperLayer]; 114 | 115 | //ProgressLayer 116 | _progressLayer = [CALayer layer]; 117 | _progressMaskLayer = [CAShapeLayer layer]; 118 | _progressMaskLayer.fillColor = [UIColor whiteColor].CGColor; 119 | _progressMaskLayer.backgroundColor = [UIColor clearColor].CGColor; 120 | _progressLayer.mask = _progressMaskLayer; 121 | [_progressSuperLayer addSublayer:_progressLayer]; 122 | _stripesLayer = [CAShapeLayer layer]; 123 | [_progressLayer addSublayer:_stripesLayer]; 124 | 125 | //Layout 126 | [self layoutSubviews]; 127 | 128 | //Start stripes animation 129 | [self setAnimateStripes:YES]; 130 | } 131 | 132 | #pragma mark Appearance 133 | 134 | - (void)setPrimaryColor:(UIColor *)primaryColor 135 | { 136 | [super setPrimaryColor:primaryColor]; 137 | if (_currentAction == M13ProgressViewActionNone) { 138 | _currentColor = self.primaryColor; 139 | } 140 | [self setNeedsDisplay]; 141 | } 142 | 143 | - (void)setSecondaryColor:(UIColor *)secondaryColor 144 | { 145 | [super setSecondaryColor:secondaryColor]; 146 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 147 | [self setNeedsDisplay]; 148 | } 149 | 150 | - (void)setInnerFillColor:(UIColor *)innerFillColor { 151 | 152 | _innerFillColor = innerFillColor; 153 | _backgroundLayer.fillColor = self.innerFillColor.CGColor; 154 | [self setNeedsDisplay]; 155 | } 156 | 157 | - (void)setStripeColor:(UIColor *)stripeColor 158 | { 159 | _stripeColor = stripeColor; 160 | } 161 | 162 | - (void)setCornerType:(M13ProgressViewStripedBarCornerType)cornerType 163 | { 164 | _cornerType = cornerType; 165 | [self setNeedsDisplay]; 166 | } 167 | 168 | - (void)setCornerRadius:(CGFloat)cornerRadius 169 | { 170 | _cornerRadius = cornerRadius; 171 | [self setNeedsDisplay]; 172 | } 173 | 174 | - (void)setStripeWidth:(CGFloat)stripeWidth 175 | { 176 | _stripeWidth = stripeWidth; 177 | [self setNeedsDisplay]; 178 | } 179 | 180 | - (void)setBorderWidth:(CGFloat)borderWidth 181 | { 182 | _borderWidth = borderWidth; 183 | [self setNeedsDisplay]; 184 | } 185 | 186 | - (void)setShowStripes:(BOOL)showStripes 187 | { 188 | _showStripes = showStripes; 189 | [self drawStripes]; 190 | } 191 | 192 | - (void)setAnimateStripes:(BOOL)animateStripes 193 | { 194 | _animateStripes = animateStripes; 195 | 196 | //reset the animations 197 | [_stripesLayer removeAllAnimations]; 198 | if (_animateStripes) { 199 | //Set the stripes frame 200 | _stripesLayer.frame = CGRectMake(0, 0, self.bounds.size.width + (4 * _stripeWidth), self.bounds.size.height); 201 | //Add the animation 202 | //Create the animation 203 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 204 | animation.duration = 2 * self.animationDuration; 205 | animation.repeatCount = HUGE_VALF; 206 | animation.removedOnCompletion = YES; 207 | animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(- (2 *_stripeWidth) + (self.bounds.size.width / 2), self.bounds.size.height / 2.0)]; 208 | animation.toValue = [NSValue valueWithCGPoint:CGPointMake(0 + (self.bounds.size.width / 2.0), self.bounds.size.height / 2.0)]; 209 | [_stripesLayer addAnimation:animation forKey:@"position"]; 210 | } 211 | } 212 | 213 | #pragma mark Actions 214 | 215 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 216 | { 217 | if (animated == NO) { 218 | if (_displayLink) { 219 | //Kill running animations 220 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 221 | _displayLink = nil; 222 | } 223 | [super setProgress:progress animated:NO]; 224 | [self setNeedsDisplay]; 225 | } else { 226 | _animationStartTime = CACurrentMediaTime(); 227 | _animationFromValue = self.progress; 228 | _animationToValue = progress; 229 | if (!_displayLink) { 230 | //Create and setup the display link 231 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 232 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 233 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 234 | } /*else { 235 | //Reuse the current display link 236 | }*/ 237 | } 238 | } 239 | 240 | - (void)animateProgress:(CADisplayLink *)displayLink 241 | { 242 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 243 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 244 | if (dt >= 1.0) { 245 | dispatch_async(dispatch_get_main_queue(), ^{ 246 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 247 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 248 | self.displayLink = nil; 249 | 250 | [super setProgress:_animationToValue animated:NO]; 251 | [self setNeedsDisplay]; 252 | }); 253 | return; 254 | } 255 | 256 | dispatch_async(dispatch_get_main_queue(), ^{ 257 | //Set progress 258 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 259 | [self setNeedsDisplay]; 260 | }); 261 | 262 | }); 263 | } 264 | 265 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 266 | { 267 | if (action == M13ProgressViewActionNone && _currentAction != M13ProgressViewActionNone) { 268 | _currentColor = self.primaryColor; 269 | } else if (action == M13ProgressViewActionSuccess && _currentAction != M13ProgressViewActionSuccess) { 270 | _currentColor = [UIColor colorWithRed:63.0f/255.0f green:226.0f/255.0f blue:80.0f/255.0f alpha:1]; 271 | } else if (action == M13ProgressViewActionFailure && _currentAction != M13ProgressViewActionFailure) { 272 | _currentColor = [UIColor colorWithRed:249.0f/255.0f green:37.0f/255.0f blue:0 alpha:1]; 273 | } 274 | _currentAction = action; 275 | [self drawStripes]; 276 | } 277 | 278 | - (void)setIndeterminate:(BOOL)indeterminate 279 | { 280 | [super setIndeterminate:indeterminate]; 281 | [self drawProgress]; 282 | } 283 | 284 | #pragma mark Layout 285 | 286 | - (void)layoutSubviews 287 | { 288 | //Set the proper location and size of the text. 289 | _backgroundLayer.frame = self.bounds; 290 | _progressSuperLayer.frame = self.bounds; 291 | _progressMaskLayer.frame = self.bounds; 292 | [self setAnimateStripes:YES]; 293 | [self calculateMask]; 294 | [self drawStripes]; 295 | } 296 | 297 | #pragma mark Drawing 298 | 299 | - (void)drawRect:(CGRect)rect 300 | { 301 | [self drawProgress]; 302 | [self drawBackground]; 303 | } 304 | 305 | - (void)drawProgress 306 | { 307 | //Calculate the corner radius 308 | CGFloat cornerRadius = 0; 309 | if (_cornerType == M13ProgressViewStripedBarCornerTypeRounded) { 310 | cornerRadius = _cornerRadius; 311 | } else if (_cornerType == M13ProgressViewStripedBarCornerTypeCircle) { 312 | cornerRadius = self.bounds.size.height - (2 * _borderWidth); 313 | } 314 | 315 | //Draw the path 316 | CGRect rect; 317 | if (!self.indeterminate) { 318 | rect = CGRectMake(_borderWidth * 2, _borderWidth * 2, (self.bounds.size.width - (4 * _borderWidth)) * self.progress, self.bounds.size.height - (4 * _borderWidth)); 319 | } else { 320 | rect = CGRectMake(_borderWidth * 2, _borderWidth * 2, (self.bounds.size.width - (4 * _borderWidth)), self.bounds.size.height - (4 * _borderWidth)); 321 | } 322 | 323 | UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius]; 324 | [_progressMaskLayer setPath:path.CGPath]; 325 | } 326 | 327 | - (void)drawBackground 328 | { 329 | //Create the path to stroke 330 | //Calculate the corner radius 331 | CGFloat cornerRadius = 0; 332 | if (_cornerType == M13ProgressViewStripedBarCornerTypeRounded) { 333 | cornerRadius = _cornerRadius; 334 | } else if (_cornerType == M13ProgressViewStripedBarCornerTypeCircle) { 335 | cornerRadius = (self.bounds.size.height - _borderWidth) / 2.0; 336 | } 337 | 338 | //Draw the path 339 | CGRect rect = CGRectMake(_borderWidth / 2.0, _borderWidth / 2.0, self.bounds.size.width - _borderWidth, self.bounds.size.height - _borderWidth); 340 | UIBezierPath *path; 341 | if (_cornerType == M13ProgressViewStripedBarCornerTypeSquare) { 342 | //Having a 0 corner radius does not display properly since the rect displays like it is not closed. 343 | path = [UIBezierPath bezierPathWithRect:rect]; 344 | } else { 345 | path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];; 346 | } 347 | _backgroundLayer.path = path.CGPath; 348 | } 349 | 350 | - (void)drawStripes 351 | { 352 | if (!_showStripes && !self.indeterminate) { 353 | //Fill with a solid color 354 | _stripesLayer.backgroundColor = _currentColor.CGColor; 355 | } else { 356 | //Start the image context 357 | UIGraphicsBeginImageContextWithOptions(CGSizeMake(_stripeWidth * 4.0, _stripeWidth * 4.0), NO, [UIScreen mainScreen].scale); 358 | 359 | //Fill the background 360 | [_currentColor setFill]; 361 | UIBezierPath *fillPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, _stripeWidth * 4.0, _stripeWidth * 4.0)]; 362 | [fillPath fill]; 363 | 364 | //Draw the stripes 365 | [_stripeColor setFill]; 366 | for (int i = 0; i < 4; i++) { 367 | //Create the four inital points of the fill shape 368 | CGPoint bottomLeft = CGPointMake(-(_stripeWidth * 4.0), _stripeWidth * 4.0); 369 | CGPoint topLeft = CGPointMake(0, 0); 370 | CGPoint topRight = CGPointMake(_stripeWidth, 0); 371 | CGPoint bottomRight = CGPointMake(-(_stripeWidth * 4.0) + _stripeWidth, _stripeWidth * 4.0); 372 | //Shift all four points as needed to draw all four stripes 373 | bottomLeft.x += i * (2 * _stripeWidth); 374 | topLeft.x += i * (2 * _stripeWidth); 375 | topRight.x += i * (2 * _stripeWidth); 376 | bottomRight.x += i * (2 * _stripeWidth); 377 | //Create the fill path 378 | UIBezierPath *path = [UIBezierPath bezierPath]; 379 | [path moveToPoint:bottomLeft]; 380 | [path addLineToPoint:topLeft]; 381 | [path addLineToPoint:topRight]; 382 | [path addLineToPoint:bottomRight]; 383 | [path closePath]; 384 | [path fill]; 385 | } 386 | 387 | //Retreive the image 388 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 389 | UIGraphicsEndImageContext(); 390 | 391 | //Set the background of the progress layer 392 | _stripesLayer.backgroundColor = [UIColor colorWithPatternImage:image].CGColor; 393 | } 394 | } 395 | 396 | - (void)calculateMask 397 | { 398 | //Calculate the corner radius 399 | CGFloat cornerRadius = 0; 400 | if (_cornerType == M13ProgressViewStripedBarCornerTypeRounded) { 401 | cornerRadius = _cornerRadius; 402 | } else if (_cornerType == M13ProgressViewStripedBarCornerTypeCircle) { 403 | cornerRadius = self.bounds.size.height - (2 * _borderWidth); 404 | } 405 | 406 | //Draw the path 407 | CGRect rect = CGRectMake(_borderWidth * 2, _borderWidth * 2, self.bounds.size.width - (4 * _borderWidth), self.bounds.size.height - (4 * _borderWidth)); 408 | 409 | UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius]; 410 | 411 | //Create the mask 412 | _maskLayer.path = path.CGPath; 413 | 414 | //Set the frame 415 | _maskLayer.frame = self.bounds; 416 | _progressSuperLayer.frame = self.bounds; 417 | } 418 | 419 | @end 420 | -------------------------------------------------------------------------------- /Vendor/UIColor+Utilities/UIColor-Expanded.m: -------------------------------------------------------------------------------- 1 | #import "UIColor-Expanded.h" 2 | 3 | /* 4 | 5 | Thanks to Poltras, Millenomi, Eridius, Nownot, WhatAHam, jberry, 6 | and everyone else who helped out but whose name is inadvertantly omitted 7 | 8 | */ 9 | 10 | /* 11 | Current outstanding request list: 12 | 13 | - PolarBearFarm - color descriptions ([UIColor warmGrayWithHintOfBlueTouchOfRedAndSplashOfYellowColor]) 14 | - Crayola color set 15 | - Eridius - UIColor needs a method that takes 2 colors and gives a third complementary one 16 | - Consider UIMutableColor that can be adjusted (brighter, cooler, warmer, thicker-alpha, etc) 17 | */ 18 | 19 | /* 20 | FOR REFERENCE: Color Space Models: enum CGColorSpaceModel { 21 | kCGColorSpaceModelUnknown = -1, 22 | kCGColorSpaceModelMonochrome, 23 | kCGColorSpaceModelRGB, 24 | kCGColorSpaceModelCMYK, 25 | kCGColorSpaceModelLab, 26 | kCGColorSpaceModelDeviceN, 27 | kCGColorSpaceModelIndexed, 28 | kCGColorSpaceModelPattern 29 | }; 30 | */ 31 | 32 | // Static cache of looked up color names. Used with +colorWithName: 33 | static NSMutableDictionary *colorNameCache = nil; 34 | 35 | #if SUPPORTS_UNDOCUMENTED_API 36 | // UIColor_Undocumented 37 | // Undocumented methods of UIColor 38 | @interface UIColor (UIColor_Undocumented) 39 | - (NSString *)styleString; 40 | @end 41 | #endif // SUPPORTS_UNDOCUMENTED_API 42 | 43 | @interface UIColor (UIColor_Expanded_Support) 44 | + (UIColor *)searchForColorByName:(NSString *)cssColorName; 45 | @end 46 | 47 | #pragma mark - 48 | 49 | @implementation UIColor (UIColor_Expanded) 50 | 51 | - (CGColorSpaceModel)colorSpaceModel { 52 | return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor)); 53 | } 54 | 55 | - (NSString *)colorSpaceString { 56 | switch (self.colorSpaceModel) { 57 | case kCGColorSpaceModelUnknown: 58 | return @"kCGColorSpaceModelUnknown"; 59 | case kCGColorSpaceModelMonochrome: 60 | return @"kCGColorSpaceModelMonochrome"; 61 | case kCGColorSpaceModelRGB: 62 | return @"kCGColorSpaceModelRGB"; 63 | case kCGColorSpaceModelCMYK: 64 | return @"kCGColorSpaceModelCMYK"; 65 | case kCGColorSpaceModelLab: 66 | return @"kCGColorSpaceModelLab"; 67 | case kCGColorSpaceModelDeviceN: 68 | return @"kCGColorSpaceModelDeviceN"; 69 | case kCGColorSpaceModelIndexed: 70 | return @"kCGColorSpaceModelIndexed"; 71 | case kCGColorSpaceModelPattern: 72 | return @"kCGColorSpaceModelPattern"; 73 | default: 74 | return @"Not a valid color space"; 75 | } 76 | } 77 | 78 | - (BOOL)canProvideRGBComponents { 79 | switch (self.colorSpaceModel) { 80 | case kCGColorSpaceModelRGB: 81 | case kCGColorSpaceModelMonochrome: 82 | return YES; 83 | default: 84 | return NO; 85 | } 86 | } 87 | 88 | - (NSArray *)arrayFromRGBAComponents { 89 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -arrayFromRGBAComponents"); 90 | 91 | CGFloat r,g,b,a; 92 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 93 | 94 | return @[@(r), 95 | @(g), 96 | @(b), 97 | @(a)]; 98 | } 99 | 100 | - (BOOL)red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha { 101 | const CGFloat *components = CGColorGetComponents(self.CGColor); 102 | 103 | CGFloat r,g,b,a; 104 | 105 | switch (self.colorSpaceModel) { 106 | case kCGColorSpaceModelMonochrome: 107 | r = g = b = components[0]; 108 | a = components[1]; 109 | break; 110 | case kCGColorSpaceModelRGB: 111 | r = components[0]; 112 | g = components[1]; 113 | b = components[2]; 114 | a = components[3]; 115 | break; 116 | default: // We don't know how to handle this model 117 | return NO; 118 | } 119 | 120 | if (red) *red = r; 121 | if (green) *green = g; 122 | if (blue) *blue = b; 123 | if (alpha) *alpha = a; 124 | 125 | return YES; 126 | } 127 | 128 | - (CGFloat)red { 129 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -red"); 130 | const CGFloat *c = CGColorGetComponents(self.CGColor); 131 | return c[0]; 132 | } 133 | 134 | - (CGFloat)green { 135 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -green"); 136 | const CGFloat *c = CGColorGetComponents(self.CGColor); 137 | if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0]; 138 | return c[1]; 139 | } 140 | 141 | - (CGFloat)blue { 142 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -blue"); 143 | const CGFloat *c = CGColorGetComponents(self.CGColor); 144 | if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0]; 145 | return c[2]; 146 | } 147 | 148 | - (CGFloat)white { 149 | NSAssert(self.colorSpaceModel == kCGColorSpaceModelMonochrome, @"Must be a Monochrome color to use -white"); 150 | const CGFloat *c = CGColorGetComponents(self.CGColor); 151 | return c[0]; 152 | } 153 | 154 | - (CGFloat)alpha { 155 | return CGColorGetAlpha(self.CGColor); 156 | } 157 | 158 | - (UInt32)rgbHex { 159 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbHex"); 160 | 161 | CGFloat r,g,b,a; 162 | if (![self red:&r green:&g blue:&b alpha:&a]) return 0; 163 | 164 | r = MIN(MAX(self.red, 0.0f), 1.0f); 165 | g = MIN(MAX(self.green, 0.0f), 1.0f); 166 | b = MIN(MAX(self.blue, 0.0f), 1.0f); 167 | 168 | return (((int)roundf(r * 255)) << 16) 169 | | (((int)roundf(g * 255)) << 8) 170 | | (((int)roundf(b * 255))); 171 | } 172 | 173 | #pragma mark Arithmetic operations 174 | 175 | - (UIColor *)colorByLuminanceMapping { 176 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 177 | 178 | CGFloat r,g,b,a; 179 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 180 | 181 | // http://en.wikipedia.org/wiki/Luma_(video) 182 | // Y = 0.2126 R + 0.7152 G + 0.0722 B 183 | return [UIColor colorWithWhite:r*0.2126f + g*0.7152f + b*0.0722f 184 | alpha:a]; 185 | 186 | } 187 | 188 | - (UIColor *)colorByMultiplyingByRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 189 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 190 | 191 | CGFloat r,g,b,a; 192 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 193 | 194 | return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r * red)) 195 | green:MAX(0.0, MIN(1.0, g * green)) 196 | blue:MAX(0.0, MIN(1.0, b * blue)) 197 | alpha:MAX(0.0, MIN(1.0, a * alpha))]; 198 | } 199 | 200 | - (UIColor *)colorByAddingRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 201 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 202 | 203 | CGFloat r,g,b,a; 204 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 205 | 206 | return [UIColor colorWithRed:MAX(0.0, MIN(1.0, r + red)) 207 | green:MAX(0.0, MIN(1.0, g + green)) 208 | blue:MAX(0.0, MIN(1.0, b + blue)) 209 | alpha:MAX(0.0, MIN(1.0, a + alpha))]; 210 | } 211 | 212 | - (UIColor *)colorByLighteningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 213 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 214 | 215 | CGFloat r,g,b,a; 216 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 217 | 218 | return [UIColor colorWithRed:MAX(r, red) 219 | green:MAX(g, green) 220 | blue:MAX(b, blue) 221 | alpha:MAX(a, alpha)]; 222 | } 223 | 224 | - (UIColor *)colorByDarkeningToRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha { 225 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 226 | 227 | CGFloat r,g,b,a; 228 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 229 | 230 | return [UIColor colorWithRed:MIN(r, red) 231 | green:MIN(g, green) 232 | blue:MIN(b, blue) 233 | alpha:MIN(a, alpha)]; 234 | } 235 | 236 | - (UIColor *)colorByMultiplyingBy:(CGFloat)f { 237 | return [self colorByMultiplyingByRed:f green:f blue:f alpha:1.0f]; 238 | } 239 | 240 | - (UIColor *)colorByAdding:(CGFloat)f { 241 | return [self colorByMultiplyingByRed:f green:f blue:f alpha:0.0f]; 242 | } 243 | 244 | - (UIColor *)colorByLighteningTo:(CGFloat)f { 245 | return [self colorByLighteningToRed:f green:f blue:f alpha:0.0f]; 246 | } 247 | 248 | - (UIColor *)colorByDarkeningTo:(CGFloat)f { 249 | return [self colorByDarkeningToRed:f green:f blue:f alpha:1.0f]; 250 | } 251 | 252 | - (UIColor *)colorByMultiplyingByColor:(UIColor *)color { 253 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 254 | 255 | CGFloat r,g,b,a; 256 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 257 | 258 | return [self colorByMultiplyingByRed:r green:g blue:b alpha:1.0f]; 259 | } 260 | 261 | - (UIColor *)colorByAddingColor:(UIColor *)color { 262 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 263 | 264 | CGFloat r,g,b,a; 265 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 266 | 267 | return [self colorByAddingRed:r green:g blue:b alpha:0.0f]; 268 | } 269 | 270 | - (UIColor *)colorByLighteningToColor:(UIColor *)color { 271 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 272 | 273 | CGFloat r,g,b,a; 274 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 275 | 276 | return [self colorByLighteningToRed:r green:g blue:b alpha:0.0f]; 277 | } 278 | 279 | - (UIColor *)colorByDarkeningToColor:(UIColor *)color { 280 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use arithmatic operations"); 281 | 282 | CGFloat r,g,b,a; 283 | if (![self red:&r green:&g blue:&b alpha:&a]) return nil; 284 | 285 | return [self colorByDarkeningToRed:r green:g blue:b alpha:1.0f]; 286 | } 287 | 288 | #pragma mark String utilities 289 | 290 | - (NSString *)stringFromColor { 291 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -stringFromColor"); 292 | NSString *result; 293 | switch (self.colorSpaceModel) { 294 | case kCGColorSpaceModelRGB: 295 | result = [NSString stringWithFormat:@"{%0.3f, %0.3f, %0.3f, %0.3f}", self.red, self.green, self.blue, self.alpha]; 296 | break; 297 | case kCGColorSpaceModelMonochrome: 298 | result = [NSString stringWithFormat:@"{%0.3f, %0.3f}", self.white, self.alpha]; 299 | break; 300 | default: 301 | result = nil; 302 | } 303 | return result; 304 | } 305 | 306 | - (NSString *)hexStringFromColor { 307 | return [NSString stringWithFormat:@"%0.6X", (unsigned int)self.rgbHex]; 308 | } 309 | 310 | + (UIColor *)colorWithString:(NSString *)stringToConvert { 311 | NSScanner *scanner = [NSScanner scannerWithString:stringToConvert]; 312 | if (![scanner scanString:@"{" intoString:NULL]) return nil; 313 | const NSUInteger kMaxComponents = 4; 314 | float c[kMaxComponents]; 315 | NSUInteger i = 0; 316 | if (![scanner scanFloat:&c[i++]]) return nil; 317 | while (1) { 318 | if ([scanner scanString:@"}" intoString:NULL]) break; 319 | if (i >= kMaxComponents) return nil; 320 | if ([scanner scanString:@"," intoString:NULL]) { 321 | if (![scanner scanFloat:&c[i++]]) return nil; 322 | } else { 323 | // either we're at the end of there's an unexpected character here 324 | // both cases are error conditions 325 | return nil; 326 | } 327 | } 328 | if (![scanner isAtEnd]) return nil; 329 | UIColor *color; 330 | switch (i) { 331 | case 2: // monochrome 332 | color = [UIColor colorWithWhite:c[0] alpha:c[1]]; 333 | break; 334 | case 4: // RGB 335 | color = [UIColor colorWithRed:c[0] green:c[1] blue:c[2] alpha:c[3]]; 336 | break; 337 | default: 338 | color = nil; 339 | } 340 | return color; 341 | } 342 | 343 | #pragma mark Class methods 344 | 345 | + (UIColor *)randomColor { 346 | return [UIColor colorWithRed:(CGFloat)RAND_MAX / random() 347 | green:(CGFloat)RAND_MAX / random() 348 | blue:(CGFloat)RAND_MAX / random() 349 | alpha:1.0f]; 350 | } 351 | 352 | + (UIColor *)colorWithRGBHex:(UInt32)hex { 353 | int r = (hex >> 16) & 0xFF; 354 | int g = (hex >> 8) & 0xFF; 355 | int b = (hex) & 0xFF; 356 | 357 | return [UIColor colorWithRed:r / 255.0f 358 | green:g / 255.0f 359 | blue:b / 255.0f 360 | alpha:1.0f]; 361 | } 362 | 363 | // Returns a UIColor by scanning the string for a hex number and passing that to +[UIColor colorWithRGBHex:] 364 | // Skips any leading whitespace and ignores any trailing characters 365 | + (UIColor *)colorWithHexString:(NSString *)stringToConvert { 366 | NSScanner *scanner = [NSScanner scannerWithString:stringToConvert]; 367 | unsigned hexNum; 368 | if (![scanner scanHexInt:&hexNum]) return nil; 369 | return [UIColor colorWithRGBHex:hexNum]; 370 | } 371 | 372 | // Lookup a color using css 3/svg color name 373 | + (UIColor *)colorWithName:(NSString *)cssColorName { 374 | UIColor *color; 375 | @synchronized(colorNameCache) { 376 | // Look for the color in the cache 377 | color = colorNameCache[cssColorName]; 378 | 379 | if ((id)color == [NSNull null]) { 380 | // If it wasn't there previously, it's still not there now 381 | color = nil; 382 | } else if (!color) { 383 | // Color not in cache, so search for it now 384 | color = [self searchForColorByName:cssColorName]; 385 | 386 | // Set the value in cache, storing NSNull on failure 387 | colorNameCache[cssColorName] = (color ?: (id)[NSNull null]); 388 | } 389 | } 390 | 391 | return color; 392 | } 393 | 394 | #pragma mark UIColor_Expanded initialization 395 | 396 | + (void)load { 397 | colorNameCache = [[NSMutableDictionary alloc] init]; 398 | } 399 | 400 | @end 401 | 402 | #pragma mark - 403 | 404 | #if SUPPORTS_UNDOCUMENTED_API 405 | @implementation UIColor (UIColor_Undocumented_Expanded) 406 | - (NSString *)fetchStyleString { 407 | return [self styleString]; 408 | } 409 | 410 | // Convert a color into RGB Color space, courtesy of Poltras 411 | // via http://ofcodeandmen.poltras.com/2009/01/22/convert-a-cgcolorref-to-another-cgcolorspaceref/ 412 | // 413 | - (UIColor *)rgbColor { 414 | // Call to undocumented method "styleString". 415 | NSString *style = [self styleString]; 416 | NSScanner *scanner = [NSScanner scannerWithString:style]; 417 | CGFloat red, green, blue; 418 | if (![scanner scanString:@"rgb(" intoString:NULL]) return nil; 419 | if (![scanner scanFloat:&red]) return nil; 420 | if (![scanner scanString:@"," intoString:NULL]) return nil; 421 | if (![scanner scanFloat:&green]) return nil; 422 | if (![scanner scanString:@"," intoString:NULL]) return nil; 423 | if (![scanner scanFloat:&blue]) return nil; 424 | if (![scanner scanString:@")" intoString:NULL]) return nil; 425 | if (![scanner isAtEnd]) return nil; 426 | 427 | return [UIColor colorWithRed:red green:green blue:blue alpha:self.alpha]; 428 | } 429 | @end 430 | #endif // SUPPORTS_UNDOCUMENTED_API 431 | 432 | @implementation UIColor (UIColor_Expanded_Support) 433 | /* 434 | * Database of color names and hex rgb values, derived 435 | * from the css 3 color spec: 436 | * http://www.w3.org/TR/css3-color/ 437 | * 438 | * We think this is a very compact way of storing 439 | * this information, and relatively cheap to lookup. 440 | * 441 | * Note that we search for color names starting with ',' 442 | * and terminated by '#', so that we don't get false matches. 443 | * For this reason, the database begins with ','. 444 | */ 445 | static const char *colorNameDB = "," 446 | "aliceblue#f0f8ff,antiquewhite#faebd7,aqua#00ffff,aquamarine#7fffd4,azure#f0ffff," 447 | "beige#f5f5dc,bisque#ffe4c4,black#000000,blanchedalmond#ffebcd,blue#0000ff," 448 | "blueviolet#8a2be2,brown#a52a2a,burlywood#deb887,cadetblue#5f9ea0,chartreuse#7fff00," 449 | "chocolate#d2691e,coral#ff7f50,cornflowerblue#6495ed,cornsilk#fff8dc,crimson#dc143c," 450 | "cyan#00ffff,darkblue#00008b,darkcyan#008b8b,darkgoldenrod#b8860b,darkgray#a9a9a9," 451 | "darkgreen#006400,darkgrey#a9a9a9,darkkhaki#bdb76b,darkmagenta#8b008b," 452 | "darkolivegreen#556b2f,darkorange#ff8c00,darkorchid#9932cc,darkred#8b0000," 453 | "darksalmon#e9967a,darkseagreen#8fbc8f,darkslateblue#483d8b,darkslategray#2f4f4f," 454 | "darkslategrey#2f4f4f,darkturquoise#00ced1,darkviolet#9400d3,deeppink#ff1493," 455 | "deepskyblue#00bfff,dimgray#696969,dimgrey#696969,dodgerblue#1e90ff," 456 | "firebrick#b22222,floralwhite#fffaf0,forestgreen#228b22,fuchsia#ff00ff," 457 | "gainsboro#dcdcdc,ghostwhite#f8f8ff,gold#ffd700,goldenrod#daa520,gray#808080," 458 | "green#008000,greenyellow#adff2f,grey#808080,honeydew#f0fff0,hotpink#ff69b4," 459 | "indianred#cd5c5c,indigo#4b0082,ivory#fffff0,khaki#f0e68c,lavender#e6e6fa," 460 | "lavenderblush#fff0f5,lawngreen#7cfc00,lemonchiffon#fffacd,lightblue#add8e6," 461 | "lightcoral#f08080,lightcyan#e0ffff,lightgoldenrodyellow#fafad2,lightgray#d3d3d3," 462 | "lightgreen#90ee90,lightgrey#d3d3d3,lightpink#ffb6c1,lightsalmon#ffa07a," 463 | "lightseagreen#20b2aa,lightskyblue#87cefa,lightslategray#778899," 464 | "lightslategrey#778899,lightsteelblue#b0c4de,lightyellow#ffffe0,lime#00ff00," 465 | "limegreen#32cd32,linen#faf0e6,magenta#ff00ff,maroon#800000,mediumaquamarine#66cdaa," 466 | "mediumblue#0000cd,mediumorchid#ba55d3,mediumpurple#9370db,mediumseagreen#3cb371," 467 | "mediumslateblue#7b68ee,mediumspringgreen#00fa9a,mediumturquoise#48d1cc," 468 | "mediumvioletred#c71585,midnightblue#191970,mintcream#f5fffa,mistyrose#ffe4e1," 469 | "moccasin#ffe4b5,navajowhite#ffdead,navy#000080,oldlace#fdf5e6,olive#808000," 470 | "olivedrab#6b8e23,orange#ffa500,orangered#ff4500,orchid#da70d6,palegoldenrod#eee8aa," 471 | "palegreen#98fb98,paleturquoise#afeeee,palevioletred#db7093,papayawhip#ffefd5," 472 | "peachpuff#ffdab9,peru#cd853f,pink#ffc0cb,plum#dda0dd,powderblue#b0e0e6," 473 | "purple#800080,red#ff0000,rosybrown#bc8f8f,royalblue#4169e1,saddlebrown#8b4513," 474 | "salmon#fa8072,sandybrown#f4a460,seagreen#2e8b57,seashell#fff5ee,sienna#a0522d," 475 | "silver#c0c0c0,skyblue#87ceeb,slateblue#6a5acd,slategray#708090,slategrey#708090," 476 | "snow#fffafa,springgreen#00ff7f,steelblue#4682b4,tan#d2b48c,teal#008080," 477 | "thistle#d8bfd8,tomato#ff6347,turquoise#40e0d0,violet#ee82ee,wheat#f5deb3," 478 | "white#ffffff,whitesmoke#f5f5f5,yellow#ffff00,yellowgreen#9acd32"; 479 | 480 | + (UIColor *)searchForColorByName:(NSString *)cssColorName { 481 | UIColor *result = nil; 482 | 483 | // Compile the string we'll use to search against the database 484 | // We search for ",#" to avoid false matches 485 | const char *searchString = [[NSString stringWithFormat:@",%@#", cssColorName] UTF8String]; 486 | 487 | // Search for the color name 488 | const char *found = strstr(colorNameDB, searchString); 489 | 490 | // If found, step past the search string and grab the hex representation 491 | if (found) { 492 | const char *after = found + strlen(searchString); 493 | int hex; 494 | if (sscanf(after, "%x", &hex) == 1) { 495 | result = [self colorWithRGBHex:hex]; 496 | } 497 | } 498 | 499 | return result; 500 | } 501 | @end 502 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewPie.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewPie.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressViewPie.h" 15 | 16 | @interface M13ProgressViewPie () 17 | 18 | /**The start progress for the progress animation.*/ 19 | @property (nonatomic, assign) CGFloat animationFromValue; 20 | /**The end progress for the progress animation.*/ 21 | @property (nonatomic, assign) CGFloat animationToValue; 22 | /**The start time interval for the animaiton.*/ 23 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 24 | /**Link to the display to keep animations in sync.*/ 25 | @property (nonatomic, strong) CADisplayLink *displayLink; 26 | /**Allow us to write to the progress.*/ 27 | @property (nonatomic, readwrite) CGFloat progress; 28 | /**The layer that progress is shown on.*/ 29 | @property (nonatomic, retain) CAShapeLayer *progressLayer; 30 | /**The layer that the background and indeterminate progress is shown on.*/ 31 | @property (nonatomic, retain) CAShapeLayer *backgroundLayer; 32 | /**The layer that is used to render icons for success or failure.*/ 33 | @property (nonatomic, retain) CAShapeLayer *iconLayer; 34 | /**The layer that is used to display the indeterminate view.*/ 35 | @property (nonatomic, retain) CAShapeLayer *indeterminateLayer; 36 | /**The action currently being performed.*/ 37 | @property (nonatomic, assign) M13ProgressViewAction currentAction; 38 | 39 | @end 40 | 41 | #define kM13ProgressViewPieHideKey @"Hide" 42 | #define kM13ProgressViewPieShowKey @"Show" 43 | 44 | @implementation M13ProgressViewPie 45 | { 46 | //Wether or not the corresponding values have been overriden by the user 47 | BOOL _backgroundRingWidthOverriden; 48 | } 49 | @dynamic progress; 50 | 51 | #pragma mark Initalization and setup 52 | 53 | - (id)init 54 | { 55 | self = [super init]; 56 | if (self) { 57 | [self setup]; 58 | } 59 | return self; 60 | } 61 | 62 | - (id)initWithFrame:(CGRect)frame 63 | { 64 | self = [super initWithFrame:frame]; 65 | if (self) { 66 | [self setup]; 67 | } 68 | return self; 69 | } 70 | 71 | - (id)initWithCoder:(NSCoder *)aDecoder 72 | { 73 | self = [super initWithCoder:aDecoder]; 74 | if (self) { 75 | [self setup]; 76 | } 77 | return self; 78 | } 79 | 80 | - (void)setup 81 | { 82 | //Set own background color 83 | self.backgroundColor = [UIColor clearColor]; 84 | 85 | //Set defaut sizes 86 | _backgroundRingWidthOverriden = NO; 87 | _backgroundRingWidth = fmaxf(self.bounds.size.width * .025, 1.0); 88 | 89 | self.animationDuration = .3; 90 | 91 | //Set default colors 92 | self.primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 93 | self.secondaryColor = self.primaryColor; 94 | 95 | //Set up the background layer 96 | _backgroundLayer = [CAShapeLayer layer]; 97 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 98 | _backgroundLayer.fillColor = [UIColor clearColor].CGColor; 99 | _backgroundLayer.lineCap = kCALineCapRound; 100 | _backgroundLayer.lineWidth = _backgroundRingWidth; 101 | [self.layer addSublayer:_backgroundLayer]; 102 | 103 | //Set up the progress layer 104 | _progressLayer = [CAShapeLayer layer]; 105 | _progressLayer.fillColor = self.primaryColor.CGColor; 106 | _progressLayer.backgroundColor = [UIColor clearColor].CGColor; 107 | [self.layer addSublayer:_progressLayer]; 108 | 109 | //Set the indeterminate layer 110 | _indeterminateLayer = [CAShapeLayer layer]; 111 | _indeterminateLayer.fillColor = self.primaryColor.CGColor; 112 | _indeterminateLayer.opacity = 0; 113 | [self.layer addSublayer:_indeterminateLayer]; 114 | 115 | //Set up the icon layer 116 | _iconLayer = [CAShapeLayer layer]; 117 | _iconLayer.fillColor = self.primaryColor.CGColor; 118 | _iconLayer.fillRule = kCAFillRuleNonZero; 119 | [self.layer addSublayer:_iconLayer]; 120 | } 121 | 122 | #pragma mark Appearance 123 | 124 | - (void)setPrimaryColor:(UIColor *)primaryColor 125 | { 126 | [super setPrimaryColor:primaryColor]; 127 | _progressLayer.strokeColor = self.primaryColor.CGColor; 128 | _progressLayer.fillColor = self.primaryColor.CGColor; 129 | _iconLayer.fillColor = self.primaryColor.CGColor; 130 | _indeterminateLayer.fillColor = self.primaryColor.CGColor; 131 | [self setNeedsDisplay]; 132 | } 133 | 134 | - (void)setSecondaryColor:(UIColor *)secondaryColor 135 | { 136 | [super setSecondaryColor:secondaryColor]; 137 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 138 | [self setNeedsDisplay]; 139 | } 140 | 141 | - (void)setBackgroundRingWidth:(CGFloat)backgroundRingWidth 142 | { 143 | _backgroundRingWidth = backgroundRingWidth; 144 | _backgroundLayer.lineWidth = _backgroundRingWidth; 145 | _backgroundRingWidthOverriden = YES; 146 | [self setNeedsDisplay]; 147 | } 148 | 149 | #pragma mark Actions 150 | 151 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 152 | { 153 | if (self.progress == progress) { 154 | return; 155 | } 156 | if (animated == NO) { 157 | if (_displayLink) { 158 | //Kill running animations 159 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 160 | _displayLink = nil; 161 | } 162 | [super setProgress:progress animated:animated]; 163 | [self setNeedsDisplay]; 164 | } else { 165 | _animationStartTime = CACurrentMediaTime(); 166 | _animationFromValue = self.progress; 167 | _animationToValue = progress; 168 | if (!_displayLink) { 169 | //Create and setup the display link 170 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 171 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 172 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 173 | } /*else { 174 | //Reuse the current display link 175 | }*/ 176 | } 177 | } 178 | 179 | - (void)animateProgress:(CADisplayLink *)displayLink 180 | { 181 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 182 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 183 | if (dt >= 1.0) { 184 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 185 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 186 | self.displayLink = nil; 187 | dispatch_async(dispatch_get_main_queue(), ^{ 188 | [super setProgress:_animationToValue animated:NO]; 189 | [self setNeedsDisplay]; 190 | }); 191 | return; 192 | } 193 | 194 | dispatch_async(dispatch_get_main_queue(), ^{ 195 | //Set progress 196 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 197 | [self setNeedsDisplay]; 198 | }); 199 | 200 | }); 201 | } 202 | 203 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 204 | { 205 | if (action == M13ProgressViewActionNone && _currentAction != M13ProgressViewActionNone) { 206 | //Animate 207 | [CATransaction begin]; 208 | [_iconLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 209 | if (self.indeterminate) { 210 | [_indeterminateLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 211 | } else { 212 | [_progressLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 213 | } 214 | [CATransaction commit]; 215 | _currentAction = action; 216 | } else if (action == M13ProgressViewActionSuccess && _currentAction != M13ProgressViewActionSuccess) { 217 | if (_currentAction == M13ProgressViewActionNone) { 218 | _currentAction = action; 219 | //Just show the icon layer 220 | [self drawIcon]; 221 | //Animate 222 | [CATransaction begin]; 223 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 224 | if (self.indeterminate) { 225 | [_indeterminateLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 226 | } else { 227 | [_progressLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 228 | } 229 | [CATransaction commit]; 230 | } else if (_currentAction == M13ProgressViewActionFailure) { 231 | //Hide the icon layer before showing 232 | [CATransaction begin]; 233 | [_iconLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 234 | [CATransaction setCompletionBlock:^{ 235 | _currentAction = action; 236 | [self drawIcon]; 237 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 238 | }]; 239 | [CATransaction commit]; 240 | } 241 | } else if (action == M13ProgressViewActionFailure && _currentAction != M13ProgressViewActionFailure) { 242 | if (_currentAction == M13ProgressViewActionNone) { 243 | //Just show the icon layer 244 | _currentAction = action; 245 | [self drawIcon]; 246 | [CATransaction begin]; 247 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 248 | if (self.indeterminate) { 249 | [_indeterminateLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 250 | } else { 251 | [_progressLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 252 | } 253 | [CATransaction commit]; 254 | } else if (_currentAction == M13ProgressViewActionSuccess) { 255 | //Hide the icon layer before showing 256 | [CATransaction begin]; 257 | [_iconLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 258 | [CATransaction setCompletionBlock:^{ 259 | _currentAction = action; 260 | [self drawIcon]; 261 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 262 | }]; 263 | [CATransaction commit]; 264 | } 265 | } 266 | } 267 | 268 | - (void)setIndeterminate:(BOOL)indeterminate 269 | { 270 | [super setIndeterminate:indeterminate]; 271 | if (self.indeterminate == YES) { 272 | //Draw the indeterminate circle 273 | [self drawIndeterminate]; 274 | 275 | //Create the rotation animation 276 | CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; 277 | rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0]; 278 | rotationAnimation.duration = 1; 279 | rotationAnimation.cumulative = YES; 280 | rotationAnimation.repeatCount = HUGE_VALF; 281 | 282 | //Set the animations 283 | [_indeterminateLayer addAnimation:rotationAnimation forKey:@"rotationAnimation"]; 284 | [CATransaction begin]; 285 | [_progressLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 286 | [_indeterminateLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 287 | [CATransaction commit]; 288 | } else { 289 | //Animate 290 | [CATransaction begin]; 291 | [_progressLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewPieShowKey]; 292 | [_indeterminateLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewPieHideKey]; 293 | [CATransaction setCompletionBlock:^{ 294 | //Remove the rotation animation and reset the background 295 | [_backgroundLayer removeAnimationForKey:@"rotationAnimation"]; 296 | [self drawBackground]; 297 | }]; 298 | [CATransaction commit]; 299 | } 300 | } 301 | 302 | - (CABasicAnimation *)showAnimation 303 | { 304 | //Show the progress layer and percentage 305 | CABasicAnimation *showAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 306 | showAnimation.fromValue = [NSNumber numberWithFloat:0.0]; 307 | showAnimation.toValue = [NSNumber numberWithFloat:1.0]; 308 | showAnimation.duration = self.animationDuration; 309 | showAnimation.repeatCount = 1.0; 310 | //Prevent the animation from resetting 311 | showAnimation.fillMode = kCAFillModeForwards; 312 | showAnimation.removedOnCompletion = NO; 313 | return showAnimation; 314 | } 315 | 316 | - (CABasicAnimation *)hideAnimation 317 | { 318 | //Hide the progress layer and percentage 319 | CABasicAnimation *hideAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 320 | hideAnimation.fromValue = [NSNumber numberWithFloat:1.0]; 321 | hideAnimation.toValue = [NSNumber numberWithFloat:0.0]; 322 | hideAnimation.duration = self.animationDuration; 323 | hideAnimation.repeatCount = 1.0; 324 | //Prevent the animation from resetting 325 | hideAnimation.fillMode = kCAFillModeForwards; 326 | hideAnimation.removedOnCompletion = NO; 327 | return hideAnimation; 328 | } 329 | 330 | #pragma mark Layout 331 | 332 | - (void)layoutSubviews 333 | { 334 | //Update frames of layers 335 | _backgroundLayer.frame = self.bounds; 336 | _progressLayer.frame = self.bounds; 337 | _iconLayer.frame = self.bounds; 338 | _indeterminateLayer.frame = self.bounds; 339 | 340 | //Update line widths if not overriden 341 | if (!_backgroundRingWidthOverriden) { 342 | _backgroundRingWidth = fmaxf(self.frame.size.width * .025, 1.0); 343 | } 344 | 345 | //Redraw 346 | [self setNeedsDisplay]; 347 | } 348 | 349 | - (void)setFrame:(CGRect)frame 350 | { 351 | //Keep the progress view square. 352 | if (frame.size.width != frame.size.height) { 353 | frame.size.height = frame.size.width; 354 | } 355 | [super setFrame:frame]; 356 | } 357 | 358 | 359 | #pragma mark Drawing 360 | 361 | - (void)drawRect:(CGRect)rect 362 | { 363 | [super drawRect:rect]; 364 | 365 | //Draw the background 366 | [self drawBackground]; 367 | 368 | //Draw Icons 369 | [self drawIcon]; 370 | 371 | //Draw Progress 372 | [self drawProgress]; 373 | } 374 | 375 | - (void)drawSuccess 376 | { 377 | //Draw relative to a base size and percentage, that way the check can be drawn for any size.*/ 378 | CGFloat radius = (self.frame.size.width / 2.0); 379 | CGFloat size = radius * .3; 380 | 381 | //Create the path 382 | UIBezierPath *path = [UIBezierPath bezierPath]; 383 | [path moveToPoint:CGPointMake(0, 0)]; 384 | [path addLineToPoint:CGPointMake(0, size * 2)]; 385 | [path addLineToPoint:CGPointMake(size * 3, size * 2)]; 386 | [path addLineToPoint:CGPointMake(size * 3, size)]; 387 | [path addLineToPoint:CGPointMake(size, size)]; 388 | [path addLineToPoint:CGPointMake(size, 0)]; 389 | [path closePath]; 390 | 391 | //Rotate it through -45 degrees... 392 | [path applyTransform:CGAffineTransformMakeRotation(-M_PI_4)]; 393 | 394 | //Center it 395 | [path applyTransform:CGAffineTransformMakeTranslation(radius * .46, 1.02 * radius)]; 396 | 397 | //Set path 398 | [_iconLayer setPath:path.CGPath]; 399 | [_iconLayer setFillColor:self.primaryColor.CGColor]; 400 | } 401 | 402 | - (void)drawFailure 403 | { 404 | //Calculate the size of the X 405 | CGFloat radius = self.frame.size.width / 2.0; 406 | CGFloat size = radius * .3; 407 | 408 | //Create the path for the X 409 | UIBezierPath *xPath = [UIBezierPath bezierPath]; 410 | [xPath moveToPoint:CGPointMake(size, 0)]; 411 | [xPath addLineToPoint:CGPointMake(2 * size, 0)]; 412 | [xPath addLineToPoint:CGPointMake(2 * size, size)]; 413 | [xPath addLineToPoint:CGPointMake(3 * size, size)]; 414 | [xPath addLineToPoint:CGPointMake(3 * size, 2 * size)]; 415 | [xPath addLineToPoint:CGPointMake(2 * size, 2 * size)]; 416 | [xPath addLineToPoint:CGPointMake(2 * size, 3 * size)]; 417 | [xPath addLineToPoint:CGPointMake(size, 3 * size)]; 418 | [xPath addLineToPoint:CGPointMake(size, 2 * size)]; 419 | [xPath addLineToPoint:CGPointMake(0, 2 * size)]; 420 | [xPath addLineToPoint:CGPointMake(0, size)]; 421 | [xPath addLineToPoint:CGPointMake(size, size)]; 422 | [xPath closePath]; 423 | 424 | //Center it 425 | [xPath applyTransform:CGAffineTransformMakeTranslation(radius - (1.5 * size), radius - (1.5 * size))]; 426 | 427 | //Rotate path 428 | [xPath applyTransform:CGAffineTransformMake(cos(M_PI_4),sin(M_PI_4),-sin(M_PI_4),cos(M_PI_4),radius * (1 - cos(M_PI_4)+ sin(M_PI_4)),radius * (1 - sin(M_PI_4)- cos(M_PI_4)))]; 429 | 430 | //Set path and fill color 431 | [_iconLayer setPath:xPath.CGPath]; 432 | [_iconLayer setFillColor:self.primaryColor.CGColor]; 433 | } 434 | 435 | - (void)drawBackground 436 | { 437 | //Create parameters to draw background 438 | float startAngle = - M_PI_2; 439 | float endAngle = startAngle + (2.0 * M_PI); 440 | CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.width / 2.0); 441 | CGFloat radius = (self.bounds.size.width - _backgroundRingWidth) / 2.0; 442 | 443 | //Draw path 444 | UIBezierPath *path = [UIBezierPath bezierPath]; 445 | path.lineWidth = _backgroundRingWidth; 446 | path.lineCapStyle = kCGLineCapRound; 447 | [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 448 | 449 | //Set the path 450 | _backgroundLayer.path = path.CGPath; 451 | } 452 | 453 | - (void)drawProgress 454 | { 455 | //Create parameters to draw progress 456 | float startAngle = - M_PI_2; 457 | float endAngle = startAngle + (2.0 * M_PI * self.progress); 458 | CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.width / 2.0); 459 | CGFloat radius = (self.bounds.size.width - _backgroundRingWidth) / 2.0; 460 | 461 | //Draw path 462 | UIBezierPath *path = [UIBezierPath bezierPath]; 463 | [path moveToPoint:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0)]; 464 | [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 465 | [path closePath]; 466 | 467 | //Set the path 468 | [_progressLayer setPath:path.CGPath]; 469 | 470 | } 471 | 472 | - (void)drawIcon 473 | { 474 | if (_currentAction == M13ProgressViewActionSuccess) { 475 | [self drawSuccess]; 476 | } else if (_currentAction == M13ProgressViewActionFailure) { 477 | [self drawFailure]; 478 | } else if (_currentAction == M13ProgressViewActionNone) { 479 | //Clear layer 480 | _iconLayer.path = nil; 481 | } 482 | } 483 | 484 | - (void)drawIndeterminate 485 | { 486 | //Create parameters to draw progress 487 | float startAngle = - M_PI_2; 488 | float endAngle = startAngle + (2.0 * M_PI * .2); 489 | CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.width / 2.0); 490 | CGFloat radius = (self.bounds.size.width - _backgroundRingWidth) / 2.0; 491 | 492 | //Draw path 493 | UIBezierPath *path = [UIBezierPath bezierPath]; 494 | [path moveToPoint:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0)]; 495 | [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 496 | [path closePath]; 497 | 498 | //Set the path 499 | [_indeterminateLayer setPath:path.CGPath]; 500 | } 501 | 502 | @end 503 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewRing.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewRing.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressViewRing.h" 15 | @import CoreGraphics; 16 | 17 | @interface M13ProgressViewRing () 18 | /**The number formatter to display the progress percentage.*/ 19 | @property (nonatomic, retain) NSNumberFormatter *percentageFormatter; 20 | /**The label that shows the percentage.*/ 21 | @property (nonatomic, retain) UILabel *percentageLabel; 22 | /**The start progress for the progress animation.*/ 23 | @property (nonatomic, assign) CGFloat animationFromValue; 24 | /**The end progress for the progress animation.*/ 25 | @property (nonatomic, assign) CGFloat animationToValue; 26 | /**The start time interval for the animaiton.*/ 27 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 28 | /**Link to the display to keep animations in sync.*/ 29 | @property (nonatomic, strong) CADisplayLink *displayLink; 30 | /**The layer that progress is shown on.*/ 31 | @property (nonatomic, retain) CAShapeLayer *progressLayer; 32 | /**The layer that the background and indeterminate progress is shown on.*/ 33 | @property (nonatomic, retain) CAShapeLayer *backgroundLayer; 34 | /**The layer that is used to render icons for success or failure.*/ 35 | @property (nonatomic, retain) CAShapeLayer *iconLayer; 36 | /**The action currently being performed.*/ 37 | @property (nonatomic, assign) M13ProgressViewAction currentAction; 38 | @end 39 | 40 | #define kM13ProgressViewRingHideKey @"Hide" 41 | #define kM13ProgressViewRingShowKey @"Show" 42 | 43 | @implementation M13ProgressViewRing 44 | { 45 | //Wether or not the corresponding values have been overriden by the user 46 | BOOL _backgroundRingWidthOverriden; 47 | BOOL _progressRingWidthOverriden; 48 | } 49 | 50 | #pragma mark Initalization and setup 51 | 52 | - (id)init 53 | { 54 | self = [super init]; 55 | if (self) { 56 | [self setup]; 57 | } 58 | return self; 59 | } 60 | 61 | - (id)initWithFrame:(CGRect)frame 62 | { 63 | self = [super initWithFrame:frame]; 64 | if (self) { 65 | [self setup]; 66 | } 67 | return self; 68 | } 69 | 70 | - (id)initWithCoder:(NSCoder *)aDecoder 71 | { 72 | self = [super initWithCoder:aDecoder]; 73 | if (self) { 74 | [self setup]; 75 | } 76 | return self; 77 | } 78 | 79 | - (void)setup 80 | { 81 | //Set own background color 82 | self.backgroundColor = [UIColor clearColor]; 83 | 84 | //Set defaut sizes 85 | _backgroundRingWidth = fmaxf(self.bounds.size.width * .025, 1.0); 86 | _progressRingWidth = 3 * _backgroundRingWidth; 87 | _progressRingWidthOverriden = NO; 88 | _backgroundRingWidthOverriden = NO; 89 | self.animationDuration = .3; 90 | 91 | //Set default colors 92 | self.primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 93 | self.secondaryColor = self.primaryColor; 94 | 95 | //Set up the number formatter 96 | _percentageFormatter = [[NSNumberFormatter alloc] init]; 97 | _percentageFormatter.numberStyle = NSNumberFormatterPercentStyle; 98 | 99 | //Set up the background layer 100 | _backgroundLayer = [CAShapeLayer layer]; 101 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 102 | _backgroundLayer.fillColor = [UIColor clearColor].CGColor; 103 | _backgroundLayer.lineCap = kCALineCapRound; 104 | _backgroundLayer.lineWidth = _backgroundRingWidth; 105 | [self.layer addSublayer:_backgroundLayer]; 106 | 107 | //Set up the progress layer 108 | _progressLayer = [CAShapeLayer layer]; 109 | _progressLayer.strokeColor = self.primaryColor.CGColor; 110 | _progressLayer.fillColor = nil; 111 | _progressLayer.lineCap = kCALineCapRound; 112 | _progressLayer.lineWidth = _progressRingWidth; 113 | [self.layer addSublayer:_progressLayer]; 114 | 115 | //Set up the icon layer 116 | _iconLayer = [CAShapeLayer layer]; 117 | _iconLayer.fillColor = self.primaryColor.CGColor; 118 | _iconLayer.fillRule = kCAFillRuleNonZero; 119 | [self.layer addSublayer:_iconLayer]; 120 | 121 | //Set the label 122 | _percentageLabel = [[UILabel alloc] init]; 123 | _percentageLabel.textAlignment = NSTextAlignmentCenter; 124 | _percentageLabel.contentMode = UIViewContentModeCenter; 125 | _percentageLabel.frame = self.bounds; 126 | [self addSubview:_percentageLabel]; 127 | } 128 | 129 | #pragma mark Appearance 130 | 131 | - (void)setPrimaryColor:(UIColor *)primaryColor 132 | { 133 | [super setPrimaryColor:primaryColor]; 134 | _progressLayer.strokeColor = self.primaryColor.CGColor; 135 | _iconLayer.fillColor = self.primaryColor.CGColor; 136 | [self setNeedsDisplay]; 137 | } 138 | 139 | - (void)setSecondaryColor:(UIColor *)secondaryColor 140 | { 141 | [super setSecondaryColor:secondaryColor]; 142 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 143 | [self setNeedsDisplay]; 144 | } 145 | 146 | - (void)setBackgroundRingWidth:(CGFloat)backgroundRingWidth 147 | { 148 | _backgroundRingWidth = backgroundRingWidth; 149 | _backgroundLayer.lineWidth = _backgroundRingWidth; 150 | _progressRingWidthOverriden = YES; 151 | [self setNeedsDisplay]; 152 | } 153 | 154 | - (void)setProgressRingWidth:(CGFloat)progressRingWidth 155 | { 156 | _progressRingWidth = progressRingWidth; 157 | _progressLayer.lineWidth = _progressRingWidth; 158 | _progressRingWidthOverriden = YES; 159 | [self setNeedsDisplay]; 160 | } 161 | 162 | - (void)setShowPercentage:(BOOL)showPercentage 163 | { 164 | _showPercentage = showPercentage; 165 | if (_showPercentage == YES) { 166 | if (_percentageLabel.superview == nil) { 167 | //Show the label if not already 168 | [self addSubview:_percentageLabel]; 169 | [self setNeedsLayout]; 170 | } 171 | } else { 172 | if (_percentageLabel.superview != nil) { 173 | //Hide the label if not already 174 | [_percentageLabel removeFromSuperview]; 175 | } 176 | } 177 | } 178 | 179 | #pragma mark Actions 180 | 181 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 182 | { 183 | if (self.progress == progress) { 184 | return; 185 | } 186 | if (animated == NO) { 187 | if (_displayLink) { 188 | //Kill running animations 189 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 190 | _displayLink = nil; 191 | } 192 | [super setProgress:progress animated:animated]; 193 | [self setNeedsDisplay]; 194 | } else { 195 | _animationStartTime = CACurrentMediaTime(); 196 | _animationFromValue = self.progress; 197 | _animationToValue = progress; 198 | if (!_displayLink) { 199 | //Create and setup the display link 200 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 201 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 202 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 203 | } /*else { 204 | //Reuse the current display link 205 | }*/ 206 | } 207 | } 208 | 209 | - (void)animateProgress:(CADisplayLink *)displayLink 210 | { 211 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 212 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 213 | if (dt >= 1.0) { 214 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 215 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 216 | self.displayLink = nil; 217 | dispatch_async(dispatch_get_main_queue(), ^{ 218 | [super setProgress:_animationToValue animated:NO]; 219 | [self setNeedsDisplay]; 220 | }); 221 | return; 222 | } 223 | 224 | dispatch_async(dispatch_get_main_queue(), ^{ 225 | //Set progress 226 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 227 | [self setNeedsDisplay]; 228 | }); 229 | 230 | }); 231 | } 232 | 233 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 234 | { 235 | if (action == M13ProgressViewActionNone && _currentAction != M13ProgressViewActionNone) { 236 | //Animate 237 | [CATransaction begin]; 238 | [_iconLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 239 | [_percentageLabel.layer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 240 | [CATransaction commit]; 241 | _currentAction = action; 242 | } else if (action == M13ProgressViewActionSuccess && _currentAction != M13ProgressViewActionSuccess) { 243 | if (_currentAction == M13ProgressViewActionNone) { 244 | _currentAction = action; 245 | //Just show the icon layer 246 | [self drawIcon]; 247 | //Animate 248 | [CATransaction begin]; 249 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 250 | [_percentageLabel.layer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 251 | [CATransaction commit]; 252 | } else if (_currentAction == M13ProgressViewActionFailure) { 253 | //Hide the icon layer before showing 254 | [CATransaction begin]; 255 | [_iconLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 256 | [CATransaction setCompletionBlock:^{ 257 | _currentAction = action; 258 | [self drawIcon]; 259 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 260 | }]; 261 | [CATransaction commit]; 262 | } 263 | } else if (action == M13ProgressViewActionFailure && _currentAction != M13ProgressViewActionFailure) { 264 | if (_currentAction == M13ProgressViewActionNone) { 265 | //Just show the icon layer 266 | _currentAction = action; 267 | [self drawIcon]; 268 | [CATransaction begin]; 269 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 270 | [_percentageLabel.layer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 271 | [CATransaction commit]; 272 | } else if (_currentAction == M13ProgressViewActionSuccess) { 273 | //Hide the icon layer before showing 274 | [CATransaction begin]; 275 | [_iconLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 276 | [CATransaction setCompletionBlock:^{ 277 | _currentAction = action; 278 | [self drawIcon]; 279 | [_iconLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 280 | }]; 281 | [CATransaction commit]; 282 | } 283 | } 284 | } 285 | 286 | - (void)setIndeterminate:(BOOL)indeterminate 287 | { 288 | [super setIndeterminate:indeterminate]; 289 | if (self.indeterminate == YES) { 290 | //Draw the indeterminate circle 291 | [self drawBackground]; 292 | 293 | //Create the rotation animation 294 | CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; 295 | rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0]; 296 | rotationAnimation.duration = 1; 297 | rotationAnimation.cumulative = YES; 298 | rotationAnimation.repeatCount = HUGE_VALF; 299 | 300 | //Set the animations 301 | [_backgroundLayer addAnimation:rotationAnimation forKey:@"rotationAnimation"]; 302 | [CATransaction begin]; 303 | [_progressLayer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 304 | [_percentageLabel.layer addAnimation:[self hideAnimation] forKey:kM13ProgressViewRingHideKey]; 305 | [CATransaction commit]; 306 | } else { 307 | //Animate 308 | [CATransaction begin]; 309 | [_progressLayer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 310 | [_percentageLabel.layer addAnimation:[self showAnimation] forKey:kM13ProgressViewRingShowKey]; 311 | [CATransaction setCompletionBlock:^{ 312 | //Remove the rotation animation and reset the background 313 | [_backgroundLayer removeAnimationForKey:@"rotationAnimation"]; 314 | [self drawBackground]; 315 | }]; 316 | [CATransaction commit]; 317 | } 318 | } 319 | 320 | - (CABasicAnimation *)showAnimation 321 | { 322 | //Show the progress layer and percentage 323 | CABasicAnimation *showAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 324 | showAnimation.fromValue = [NSNumber numberWithFloat:0.0]; 325 | showAnimation.toValue = [NSNumber numberWithFloat:1.0]; 326 | showAnimation.duration = self.animationDuration; 327 | showAnimation.repeatCount = 1.0; 328 | //Prevent the animation from resetting 329 | showAnimation.fillMode = kCAFillModeForwards; 330 | showAnimation.removedOnCompletion = NO; 331 | return showAnimation; 332 | } 333 | 334 | - (CABasicAnimation *)hideAnimation 335 | { 336 | //Hide the progress layer and percentage 337 | CABasicAnimation *hideAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 338 | hideAnimation.fromValue = [NSNumber numberWithFloat:1.0]; 339 | hideAnimation.toValue = [NSNumber numberWithFloat:0.0]; 340 | hideAnimation.duration = self.animationDuration; 341 | hideAnimation.repeatCount = 1.0; 342 | //Prevent the animation from resetting 343 | hideAnimation.fillMode = kCAFillModeForwards; 344 | hideAnimation.removedOnCompletion = NO; 345 | return hideAnimation; 346 | } 347 | 348 | #pragma mark Layout 349 | 350 | - (void)layoutSubviews 351 | { 352 | //Update frames of layers 353 | _backgroundLayer.frame = self.bounds; 354 | _progressLayer.frame = self.bounds; 355 | _iconLayer.frame = self.bounds; 356 | _percentageLabel.frame = self.bounds; 357 | 358 | //Update font size 359 | _percentageLabel.font = [UIFont systemFontOfSize:(self.bounds.size.width / 5)]; 360 | _percentageLabel.textColor = self.primaryColor; 361 | 362 | //Update line widths if not overriden 363 | if (!_backgroundRingWidthOverriden) { 364 | _backgroundRingWidth = fmaxf(self.frame.size.width * .025, 1.0); 365 | } 366 | if (!_progressRingWidthOverriden) { 367 | _progressRingWidth = _backgroundRingWidth * 3; 368 | } 369 | 370 | //Redraw 371 | [self setNeedsDisplay]; 372 | } 373 | 374 | - (void)setFrame:(CGRect)frame 375 | { 376 | //Keep the progress view square. 377 | if (frame.size.width != frame.size.height) { 378 | frame.size.height = frame.size.width; 379 | } 380 | [super setFrame:frame]; 381 | } 382 | 383 | #pragma mark Drawing 384 | 385 | - (void)drawRect:(CGRect)rect 386 | { 387 | [super drawRect:rect]; 388 | 389 | //Draw the background 390 | [self drawBackground]; 391 | 392 | //Draw Icons 393 | [self drawIcon]; 394 | 395 | //Draw Progress 396 | [self drawProgress]; 397 | } 398 | 399 | - (void)drawSuccess 400 | { 401 | //Draw relative to a base size and percentage, that way the check can be drawn for any size.*/ 402 | CGFloat radius = (self.frame.size.width / 2.0); 403 | CGFloat size = radius * .3; 404 | 405 | //Create the path 406 | UIBezierPath *path = [UIBezierPath bezierPath]; 407 | [path moveToPoint:CGPointMake(0, 0)]; 408 | [path addLineToPoint:CGPointMake(0, size * 2)]; 409 | [path addLineToPoint:CGPointMake(size * 3, size * 2)]; 410 | [path addLineToPoint:CGPointMake(size * 3, size)]; 411 | [path addLineToPoint:CGPointMake(size, size)]; 412 | [path addLineToPoint:CGPointMake(size, 0)]; 413 | [path closePath]; 414 | 415 | //Rotate it through -45 degrees... 416 | [path applyTransform:CGAffineTransformMakeRotation(-M_PI_4)]; 417 | 418 | //Center it 419 | [path applyTransform:CGAffineTransformMakeTranslation(radius * .46, 1.02 * radius)]; 420 | 421 | //Set path 422 | [_iconLayer setPath:path.CGPath]; 423 | [_iconLayer setFillColor:self.primaryColor.CGColor]; 424 | } 425 | 426 | - (void)drawFailure 427 | { 428 | //Calculate the size of the X 429 | CGFloat radius = self.frame.size.width / 2.0; 430 | CGFloat size = radius * .3; 431 | 432 | //Create the path for the X 433 | UIBezierPath *xPath = [UIBezierPath bezierPath]; 434 | [xPath moveToPoint:CGPointMake(size, 0)]; 435 | [xPath addLineToPoint:CGPointMake(2 * size, 0)]; 436 | [xPath addLineToPoint:CGPointMake(2 * size, size)]; 437 | [xPath addLineToPoint:CGPointMake(3 * size, size)]; 438 | [xPath addLineToPoint:CGPointMake(3 * size, 2 * size)]; 439 | [xPath addLineToPoint:CGPointMake(2 * size, 2 * size)]; 440 | [xPath addLineToPoint:CGPointMake(2 * size, 3 * size)]; 441 | [xPath addLineToPoint:CGPointMake(size, 3 * size)]; 442 | [xPath addLineToPoint:CGPointMake(size, 2 * size)]; 443 | [xPath addLineToPoint:CGPointMake(0, 2 * size)]; 444 | [xPath addLineToPoint:CGPointMake(0, size)]; 445 | [xPath addLineToPoint:CGPointMake(size, size)]; 446 | [xPath closePath]; 447 | 448 | 449 | //Center it 450 | [xPath applyTransform:CGAffineTransformMakeTranslation(radius - (1.5 * size), radius - (1.5 * size))]; 451 | 452 | //Rotate path 453 | [xPath applyTransform:CGAffineTransformMake(cos(M_PI_4),sin(M_PI_4),-sin(M_PI_4),cos(M_PI_4),radius * (1 - cos(M_PI_4)+ sin(M_PI_4)),radius * (1 - sin(M_PI_4)- cos(M_PI_4)))]; 454 | 455 | //Set path and fill color 456 | [_iconLayer setPath:xPath.CGPath]; 457 | [_iconLayer setFillColor:self.primaryColor.CGColor]; 458 | } 459 | 460 | - (void)drawBackground 461 | { 462 | //Create parameters to draw background 463 | float startAngle = - M_PI_2; 464 | float endAngle = startAngle + (2.0 * M_PI); 465 | CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.width / 2.0); 466 | CGFloat radius = (self.bounds.size.width - _backgroundRingWidth) / 2.0; 467 | 468 | //If indeterminate, recalculate the end angle 469 | if (self.indeterminate) { 470 | endAngle = .8 * endAngle; 471 | } 472 | 473 | //Draw path 474 | UIBezierPath *path = [UIBezierPath bezierPath]; 475 | path.lineWidth = _progressRingWidth; 476 | path.lineCapStyle = kCGLineCapRound; 477 | [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 478 | 479 | //Set the path 480 | _backgroundLayer.path = path.CGPath; 481 | } 482 | 483 | - (void)drawProgress 484 | { 485 | //Create parameters to draw progress 486 | float startAngle = - M_PI_2; 487 | float endAngle = startAngle + (2.0 * M_PI * self.progress); 488 | CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.width / 2.0); 489 | CGFloat radius = (self.bounds.size.width - _progressRingWidth) / 2.0; 490 | 491 | //Draw path 492 | UIBezierPath *path = [UIBezierPath bezierPath]; 493 | path.lineCapStyle = kCGLineCapButt; 494 | path.lineWidth = _progressRingWidth; 495 | [path addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 496 | 497 | //Set the path 498 | [_progressLayer setPath:path.CGPath]; 499 | 500 | //Update label 501 | _percentageLabel.text = [_percentageFormatter stringFromNumber:[NSNumber numberWithFloat:self.progress]]; 502 | } 503 | 504 | - (void)drawIcon 505 | { 506 | if (_currentAction == M13ProgressViewActionSuccess) { 507 | [self drawSuccess]; 508 | } else if (_currentAction == M13ProgressViewActionFailure) { 509 | [self drawFailure]; 510 | } else if (_currentAction == M13ProgressViewActionNone) { 511 | //Clear layer 512 | _iconLayer.path = nil; 513 | } 514 | } 515 | 516 | @end 517 | -------------------------------------------------------------------------------- /Vendor/M13ProgressSuite/M13ProgressViewBorderedBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // M13ProgressViewBorderedBar.m 3 | // M13ProgressView 4 | // 5 | /*Copyright (c) 2013 Brandon McQuilkin 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | */ 13 | 14 | #import "M13ProgressViewBorderedBar.h" 15 | 16 | @interface M13ProgressViewBorderedBar () 17 | /**The start progress for the progress animation.*/ 18 | @property (nonatomic, assign) CGFloat animationFromValue; 19 | /**The end progress for the progress animation.*/ 20 | @property (nonatomic, assign) CGFloat animationToValue; 21 | /**The start time interval for the animaiton.*/ 22 | @property (nonatomic, assign) CFTimeInterval animationStartTime; 23 | /**Link to the display to keep animations in sync.*/ 24 | @property (nonatomic, strong) CADisplayLink *displayLink; 25 | /**The layer that contains the progress layer. That also masks the progress layer.*/ 26 | @property (nonatomic, retain) CALayer *progressSuperLayer; 27 | /**The layer that displays progress in the progress bar.*/ 28 | @property (nonatomic, retain) CAShapeLayer *progressLayer; 29 | /**The mask layer for the progress layer.*/ 30 | @property (nonatomic, retain) CAShapeLayer *maskLayer; 31 | /**The background layer that displays the border.*/ 32 | @property (nonatomic, retain) CAShapeLayer *backgroundLayer; 33 | /**The layer that is used to animate indeterminate progress.*/ 34 | @property (nonatomic, retain) CALayer *indeterminateLayer; 35 | /**The action currently being performed.*/ 36 | @property (nonatomic, assign) M13ProgressViewAction currentAction; 37 | 38 | @end 39 | 40 | @implementation M13ProgressViewBorderedBar 41 | 42 | #pragma mark Initalization and setup 43 | 44 | - (id)init 45 | { 46 | self = [super init]; 47 | if (self) { 48 | [self setup]; 49 | } 50 | return self; 51 | } 52 | 53 | - (id)initWithFrame:(CGRect)frame 54 | { 55 | self = [super initWithFrame:frame]; 56 | if (self) { 57 | [self setup]; 58 | } 59 | return self; 60 | } 61 | 62 | - (id)initWithCoder:(NSCoder *)aDecoder 63 | { 64 | self = [super initWithCoder:aDecoder]; 65 | if (self) { 66 | [self setup]; 67 | } 68 | return self; 69 | } 70 | 71 | - (void)setup 72 | { 73 | //Set own background color 74 | self.backgroundColor = [UIColor clearColor]; 75 | 76 | //Set defauts 77 | self.animationDuration = .3; 78 | _borderWidth = 1.0; 79 | _cornerRadius = 3.0; 80 | _progressDirection = M13ProgressViewBorderedBarProgressDirectionLeftToRight; 81 | _cornerType = M13ProgressViewBorderedBarCornerTypeSquare; 82 | 83 | //Set default colors 84 | self.primaryColor = [UIColor colorWithRed:0 green:122/255.0 blue:1.0 alpha:1.0]; 85 | self.secondaryColor = self.primaryColor; 86 | _successColor = [UIColor colorWithRed:63.0f/255.0f green:226.0f/255.0f blue:80.0f/255.0f alpha:1]; 87 | _failureColor = [UIColor colorWithRed:249.0f/255.0f green:37.0f/255.0f blue:0 alpha:1]; 88 | 89 | //BackgroundLayer 90 | _backgroundLayer = [CAShapeLayer layer]; 91 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 92 | _backgroundLayer.fillColor = nil; 93 | _backgroundLayer.lineWidth = _borderWidth; 94 | [self.layer addSublayer:_backgroundLayer]; 95 | 96 | //ProgressLayer 97 | _progressLayer = [CAShapeLayer layer]; 98 | _progressLayer.fillColor = self.primaryColor.CGColor; 99 | _maskLayer = [CAShapeLayer layer]; 100 | _maskLayer.fillColor = [UIColor blackColor].CGColor; 101 | _maskLayer.backgroundColor = [UIColor clearColor].CGColor; 102 | _progressSuperLayer = [CALayer layer]; 103 | _progressSuperLayer.mask = _maskLayer; 104 | [_progressSuperLayer addSublayer:_progressLayer]; 105 | [self.layer addSublayer:_progressSuperLayer]; 106 | 107 | //IndeterminateLayer 108 | _indeterminateLayer = [CALayer layer]; 109 | _indeterminateLayer.backgroundColor = self.primaryColor.CGColor; 110 | _indeterminateLayer.opacity = 0; 111 | [_progressSuperLayer addSublayer:_indeterminateLayer]; 112 | 113 | //Layout 114 | [self layoutSubviews]; 115 | } 116 | 117 | #pragma mark Appearance 118 | 119 | - (void)setPrimaryColor:(UIColor *)primaryColor 120 | { 121 | [super setPrimaryColor:primaryColor]; 122 | _progressLayer.fillColor = self.primaryColor.CGColor; 123 | _indeterminateLayer.backgroundColor = self.primaryColor.CGColor; 124 | [self setNeedsDisplay]; 125 | } 126 | 127 | - (void)setSecondaryColor:(UIColor *)secondaryColor 128 | { 129 | [super setSecondaryColor:secondaryColor]; 130 | _backgroundLayer.strokeColor = self.secondaryColor.CGColor; 131 | [self setNeedsDisplay]; 132 | } 133 | 134 | - (void)setSuccessColor:(UIColor *)successColor 135 | { 136 | _successColor = successColor; 137 | [self setNeedsDisplay]; 138 | } 139 | 140 | - (void)setFailureColor:(UIColor *)failureColor 141 | { 142 | _failureColor = failureColor; 143 | [self setNeedsDisplay]; 144 | } 145 | 146 | - (void)setProgressDirection:(M13ProgressViewBorderedBarProgressDirection)progressDirection 147 | { 148 | _progressDirection = progressDirection; 149 | [self setNeedsDisplay]; 150 | } 151 | 152 | - (void)setCornerType:(M13ProgressViewBorderedBarCornerType)cornerType 153 | { 154 | _cornerType = cornerType; 155 | [self calculateMask]; 156 | [self setNeedsDisplay]; 157 | } 158 | 159 | - (void)setCornerRadius:(CGFloat)cornerRadius 160 | { 161 | _cornerRadius = cornerRadius; 162 | [self calculateMask]; 163 | 164 | [self setNeedsDisplay]; 165 | } 166 | 167 | - (void)setBorderWidth:(CGFloat)borderWidth 168 | { 169 | _borderWidth = borderWidth; 170 | _backgroundLayer.lineWidth = borderWidth; 171 | [self calculateMask]; 172 | [self setNeedsDisplay]; 173 | } 174 | 175 | #pragma mark Actions 176 | 177 | - (void)setProgress:(CGFloat)progress animated:(BOOL)animated 178 | { 179 | if (animated == NO) { 180 | if (_displayLink) { 181 | //Kill running animations 182 | [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 183 | _displayLink = nil; 184 | } 185 | [super setProgress:progress animated:NO]; 186 | [self setNeedsDisplay]; 187 | } else { 188 | _animationStartTime = CACurrentMediaTime(); 189 | _animationFromValue = self.progress; 190 | _animationToValue = progress; 191 | if (!_displayLink) { 192 | //Create and setup the display link 193 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 194 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animateProgress:)]; 195 | [self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 196 | } /*else { 197 | //Reuse the current display link 198 | }*/ 199 | } 200 | } 201 | 202 | - (void)animateProgress:(CADisplayLink *)displayLink 203 | { 204 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 205 | CGFloat dt = (displayLink.timestamp - _animationStartTime) / self.animationDuration; 206 | if (dt >= 1.0) { 207 | //Order is important! Otherwise concurrency will cause errors, because setProgress: will detect an animation in progress and try to stop it by itself. Once over one, set to actual progress amount. Animation is over. 208 | [self.displayLink removeFromRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes]; 209 | self.displayLink = nil; 210 | dispatch_async(dispatch_get_main_queue(), ^{ 211 | [super setProgress:_animationToValue animated:NO]; 212 | [self setNeedsDisplay]; 213 | }); 214 | return; 215 | } 216 | 217 | dispatch_async(dispatch_get_main_queue(), ^{ 218 | //Set progress 219 | [super setProgress:_animationFromValue + dt * (_animationToValue - _animationFromValue) animated:YES]; 220 | [self setNeedsDisplay]; 221 | }); 222 | 223 | }); 224 | } 225 | 226 | - (void)performAction:(M13ProgressViewAction)action animated:(BOOL)animated 227 | { 228 | if (action == M13ProgressViewActionNone && _currentAction != M13ProgressViewActionNone) { 229 | _currentAction = action; 230 | [self setNeedsDisplay]; 231 | [CATransaction begin]; 232 | CABasicAnimation *barAnimation = [self barColorAnimation]; 233 | CABasicAnimation *indeterminateAnimation = [self indeterminateColorAnimation]; 234 | barAnimation.fromValue = (id)_progressLayer.strokeColor; 235 | barAnimation.toValue = (id)self.primaryColor.CGColor; 236 | indeterminateAnimation.fromValue = (id)_indeterminateLayer.backgroundColor; 237 | indeterminateAnimation.toValue = (id)self.primaryColor.CGColor; 238 | [_progressLayer addAnimation:barAnimation forKey:@"fillColor"]; 239 | [_indeterminateLayer addAnimation:indeterminateAnimation forKey:@"backgroundColor"]; 240 | [CATransaction commit]; 241 | } else if (action == M13ProgressViewActionSuccess && _currentAction != M13ProgressViewActionSuccess) { 242 | _currentAction = action; 243 | [self setNeedsDisplay]; 244 | [CATransaction begin]; 245 | CABasicAnimation *barAnimation = [self barColorAnimation]; 246 | CABasicAnimation *indeterminateAnimation = [self indeterminateColorAnimation]; 247 | barAnimation.fromValue = (id)_progressLayer.strokeColor; 248 | barAnimation.toValue = (id)_successColor.CGColor; 249 | indeterminateAnimation.fromValue = (id)_indeterminateLayer.backgroundColor; 250 | indeterminateAnimation.toValue = (id)_successColor.CGColor; 251 | [_progressLayer addAnimation:barAnimation forKey:@"fillColor"]; 252 | [_indeterminateLayer addAnimation:indeterminateAnimation forKey:@"backgroundColor"]; 253 | [CATransaction commit]; 254 | } else if (action == M13ProgressViewActionFailure && _currentAction != M13ProgressViewActionFailure) { 255 | _currentAction = action; 256 | [self setNeedsDisplay]; 257 | [CATransaction begin]; 258 | CABasicAnimation *barAnimation = [self barColorAnimation]; 259 | CABasicAnimation *indeterminateAnimation = [self indeterminateColorAnimation]; 260 | barAnimation.fromValue = (id)_progressLayer.strokeColor; 261 | barAnimation.toValue = (id)_failureColor.CGColor; 262 | indeterminateAnimation.fromValue = (id)_indeterminateLayer.backgroundColor; 263 | indeterminateAnimation.toValue = (id)_failureColor.CGColor; 264 | [_progressLayer addAnimation:barAnimation forKey:@"fillColor"]; 265 | [_indeterminateLayer addAnimation:indeterminateAnimation forKey:@"backgroundColor"]; 266 | [CATransaction commit]; 267 | } 268 | } 269 | 270 | - (CABasicAnimation *)barColorAnimation 271 | { 272 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"fillColor"]; 273 | animation.duration = 2 * self.animationDuration; 274 | animation.repeatCount = 1; 275 | //Prevent the animation from resetting 276 | animation.fillMode = kCAFillModeForwards; 277 | animation.removedOnCompletion = NO; 278 | return animation; 279 | } 280 | 281 | - (CABasicAnimation *)indeterminateColorAnimation 282 | { 283 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; 284 | animation.duration = 2 * self.animationDuration; 285 | animation.repeatCount = 1; 286 | //Prevent the animation from resetting 287 | animation.fillMode = kCAFillModeForwards; 288 | animation.removedOnCompletion = NO; 289 | return animation; 290 | } 291 | 292 | - (void)setIndeterminate:(BOOL)indeterminate 293 | { 294 | [super setIndeterminate:indeterminate]; 295 | 296 | if (self.indeterminate == YES) { 297 | //show the indeterminate view 298 | _indeterminateLayer.opacity = 1; 299 | _progressLayer.opacity = 0; 300 | //Create the animation 301 | [_indeterminateLayer removeAnimationForKey:@"position"]; 302 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 303 | animation.duration = 5 * self.animationDuration; 304 | animation.repeatCount = HUGE_VALF; 305 | animation.removedOnCompletion = YES; 306 | //Set the animation control points 307 | if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionLeftToRight) { 308 | animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(-_indeterminateLayer.frame.size.width, _indeterminateLayer.frame.size.height / 2)]; 309 | animation.toValue = [NSValue valueWithCGPoint:CGPointMake(_indeterminateLayer.frame.size.width + self.bounds.size.width, _indeterminateLayer.frame.size.height / 2)]; 310 | } else if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionRightToLeft) { 311 | animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_indeterminateLayer.frame.size.width + self.bounds.size.width, _indeterminateLayer.frame.size.height / 2)]; 312 | animation.toValue = [NSValue valueWithCGPoint:CGPointMake(-_indeterminateLayer.frame.size.width, _indeterminateLayer.frame.size.height / 2)]; 313 | } else if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionBottomToTop) { 314 | animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_indeterminateLayer.frame.size.width / 2, self.bounds.size.height + _indeterminateLayer.frame.size.height)]; 315 | animation.toValue = [NSValue valueWithCGPoint:CGPointMake(_indeterminateLayer.frame.size.width / 2, -_indeterminateLayer.frame.size.height)]; 316 | } else if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionTopToBottom) { 317 | animation.toValue = [NSValue valueWithCGPoint:CGPointMake(_indeterminateLayer.frame.size.width / 2, self.bounds.size.height + _indeterminateLayer.frame.size.height)]; 318 | animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(_indeterminateLayer.frame.size.width / 2, -_indeterminateLayer.frame.size.height)]; 319 | } 320 | [_indeterminateLayer addAnimation:animation forKey:@"position"]; 321 | } else { 322 | //Hide the indeterminate view 323 | _indeterminateLayer.opacity = 0; 324 | _progressLayer.opacity = 1; 325 | //Remove all animations 326 | [_indeterminateLayer removeAnimationForKey:@"position"]; 327 | //Reset progress text 328 | } 329 | } 330 | 331 | - (void)layoutSubviews 332 | { 333 | [super layoutSubviews]; 334 | 335 | [self calculateMask]; 336 | //Set the background layer size 337 | _backgroundLayer.frame = self.bounds; 338 | _progressLayer.frame = self.bounds; 339 | 340 | //Set the indeterminate layer frame 341 | if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionLeftToRight || _progressDirection == M13ProgressViewBorderedBarProgressDirectionRightToLeft) { 342 | //Set the indeterminate layer frame (reset the animation so the animation starts and ends at the right points) 343 | [_indeterminateLayer removeAllAnimations]; 344 | _indeterminateLayer.frame = CGRectMake(0, 0, self.frame.size.width * .4, self.frame.size.height); 345 | [self setIndeterminate:self.indeterminate]; 346 | } else { 347 | //Set the indeterminate layer frame (reset the animation so the animation starts and ends at the right points) 348 | [_indeterminateLayer removeAllAnimations]; 349 | _indeterminateLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height * .4); 350 | [self setIndeterminate:self.indeterminate]; 351 | } 352 | } 353 | 354 | #pragma mark Drawing 355 | 356 | - (void)drawRect:(CGRect)rect 357 | { 358 | if (!self.indeterminate) { 359 | [self drawProgress]; 360 | } 361 | [self drawBackground]; 362 | } 363 | 364 | - (void)drawBackground 365 | { 366 | //Create the path to stroke 367 | //Calculate the corner radius 368 | CGFloat cornerRadius = 0; 369 | if (_cornerType == M13ProgressViewBorderedBarCornerTypeRounded) { 370 | cornerRadius = _cornerRadius; 371 | } else if (_cornerType == M13ProgressViewBorderedBarCornerTypeCircle) { 372 | if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionLeftToRight || _progressDirection == M13ProgressViewBorderedBarProgressDirectionRightToLeft) { 373 | cornerRadius = self.bounds.size.height - _borderWidth; 374 | } else { 375 | cornerRadius = self.bounds.size.width - _borderWidth; 376 | } 377 | } 378 | 379 | //Draw the path 380 | CGRect rect = CGRectMake(_borderWidth / 2.0, _borderWidth / 2.0, self.bounds.size.width - _borderWidth, self.bounds.size.height - _borderWidth); 381 | UIBezierPath *path; 382 | if (_cornerType == M13ProgressViewBorderedBarCornerTypeSquare) { 383 | //Having a 0 corner radius does not display properly since the rect displays like it is not closed. 384 | path = [UIBezierPath bezierPathWithRect:rect]; 385 | } else { 386 | path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];; 387 | } 388 | _backgroundLayer.path = path.CGPath; 389 | } 390 | 391 | - (void)drawProgress 392 | { 393 | 394 | //Calculate the corner radius 395 | CGFloat cornerRadius = 0; 396 | if (_cornerType == M13ProgressViewBorderedBarCornerTypeRounded) { 397 | cornerRadius = _cornerRadius; 398 | } else if (_cornerType == M13ProgressViewBorderedBarCornerTypeCircle) { 399 | if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionLeftToRight || _progressDirection == M13ProgressViewBorderedBarProgressDirectionRightToLeft) { 400 | cornerRadius = self.bounds.size.height - (2 * _borderWidth); 401 | } else { 402 | cornerRadius = self.bounds.size.width - (2 * _borderWidth); 403 | } 404 | } 405 | 406 | //Draw the path 407 | CGRect rect = CGRectZero; 408 | if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionLeftToRight) { 409 | rect = CGRectMake(_borderWidth * 2, _borderWidth * 2, (self.bounds.size.width - (4 * _borderWidth)) * self.progress, self.bounds.size.height - (4 * _borderWidth)); 410 | } else if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionRightToLeft) { 411 | rect = CGRectMake((_borderWidth * 2) + ((self.bounds.size.width - (4 * _borderWidth)) * (1 - self.progress)), _borderWidth * 2, (self.bounds.size.width - (4 * _borderWidth)) * self.progress, self.bounds.size.height - (4 * _borderWidth)); 412 | } else if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionBottomToTop) { 413 | rect = CGRectMake(_borderWidth * 2, ((self.bounds.size.height - (4 * _borderWidth)) * (1 - self.progress)) + (2 * _borderWidth), self.bounds.size.width - (4 * _borderWidth), (self.bounds.size.height - (4 * _borderWidth)) * self.progress); 414 | } else if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionTopToBottom) { 415 | rect = CGRectMake(_borderWidth * 2, _borderWidth * 2, self.bounds.size.width - (4 * _borderWidth), (self.bounds.size.height - (4 * _borderWidth)) * self.progress); 416 | } 417 | UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius]; 418 | [_progressLayer setPath:path.CGPath]; 419 | } 420 | 421 | - (void)calculateMask 422 | { 423 | //Calculate the corner radius 424 | CGFloat cornerRadius = 0; 425 | if (_cornerType == M13ProgressViewBorderedBarCornerTypeRounded) { 426 | cornerRadius = _cornerRadius; 427 | } else if (_cornerType == M13ProgressViewBorderedBarCornerTypeCircle) { 428 | if (_progressDirection == M13ProgressViewBorderedBarProgressDirectionLeftToRight || _progressDirection == M13ProgressViewBorderedBarProgressDirectionRightToLeft) { 429 | cornerRadius = self.bounds.size.height - (2 * _borderWidth); 430 | } else { 431 | cornerRadius = self.bounds.size.width - (2 * _borderWidth); 432 | } 433 | } 434 | 435 | //Draw the path 436 | CGRect rect = CGRectMake(_borderWidth * 2, _borderWidth * 2, self.bounds.size.width - (4 * _borderWidth), self.bounds.size.height - (4 * _borderWidth)); 437 | 438 | UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius]; 439 | 440 | //Create the mask 441 | _maskLayer.path = path.CGPath; 442 | 443 | //Set the frame 444 | _maskLayer.frame = self.bounds; 445 | _progressSuperLayer.frame = self.bounds; 446 | } 447 | 448 | @end 449 | -------------------------------------------------------------------------------- /Watch Ring Generator/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Watch Ring Generator 4 | // 5 | // Created by Aleksandar Vacić on 23.2.15.. 6 | // Copyright (c) 2015. Radiant Tap. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | //#import "M13ProgressViewSegmentedRing.h" 11 | #import "M13ProgressViewRing.h" 12 | #import "UIColor-Expanded.h" 13 | #import "NSFileManager+Utilities.h" 14 | @import QuartzCore; 15 | @import CoreGraphics; 16 | 17 | 18 | 19 | NSString *const DefaultsKeyOuterForegroundColor = @"DefaultsKeyOuterForegroundColor"; 20 | NSString *const DefaultsKeyOuterBackgroundColor = @"DefaultsKeyOuterBackgroundColor"; 21 | NSString *const DefaultsKeyOuterSize = @"DefaultsKeyOuterSize"; 22 | NSString *const DefaultsKeyOuterWidth = @"DefaultsKeyOuterWidth"; 23 | NSString *const DefaultsKeyOuterProgress = @"DefaultsKeyOuterProgress"; 24 | 25 | NSString *const DefaultsKeyInnerForegroundColor = @"DefaultsKeyInnerForegroundColor"; 26 | NSString *const DefaultsKeyInnerBackgroundColor = @"DefaultsKeyInnerBackgroundColor"; 27 | NSString *const DefaultsKeyInnerSize = @"DefaultsKeyInnerSize"; 28 | NSString *const DefaultsKeyInnerWidth = @"DefaultsKeyInnerWidth"; 29 | NSString *const DefaultsKeyInnerProgress = @"DefaultsKeyInnerProgress"; 30 | 31 | NSString *const DefaultsKeyWatchSize = @"DefaultsKeyWatchSize"; 32 | 33 | @interface ViewController () < UITextFieldDelegate > 34 | 35 | @property (weak, nonatomic) IBOutlet UIButton *mm38button; 36 | @property (weak, nonatomic) IBOutlet UIButton *mm42button; 37 | @property (weak, nonatomic) IBOutlet UIImageView *watchFace; 38 | @property (weak, nonatomic) IBOutlet UIImageView *watchApp; 39 | @property (weak, nonatomic) IBOutlet UIView *watchScreen; 40 | 41 | @property (nonatomic) NSInteger kMaxNumberOfImages; 42 | @property (nonatomic) NSInteger kProgressOvershoot; 43 | 44 | @property (weak, nonatomic) IBOutlet M13ProgressViewRing *innerRing; 45 | @property (weak, nonatomic) IBOutlet M13ProgressViewRing *outerRing; 46 | 47 | @property (weak, nonatomic) IBOutlet UIButton *outerBackgroundButton; 48 | @property (weak, nonatomic) IBOutlet UIButton *outerForegroundButton; 49 | @property (weak, nonatomic) IBOutlet UITextField *outerRingSize; 50 | @property (weak, nonatomic) IBOutlet UITextField *outerRingWidth; 51 | @property (weak, nonatomic) IBOutlet UITextField *outerRingProgress; 52 | @property (weak, nonatomic) IBOutlet UITextField *outerBackground; 53 | @property (weak, nonatomic) IBOutlet UITextField *outerForeground; 54 | 55 | @property (weak, nonatomic) IBOutlet UIButton *innerBackgroundButton; 56 | @property (weak, nonatomic) IBOutlet UIButton *innerForegroundButton; 57 | @property (weak, nonatomic) IBOutlet UITextField *innerRingSize; 58 | @property (weak, nonatomic) IBOutlet UITextField *innerRingWidth; 59 | @property (weak, nonatomic) IBOutlet UITextField *innerRingProgress; 60 | @property (weak, nonatomic) IBOutlet UITextField *innerBackground; 61 | @property (weak, nonatomic) IBOutlet UITextField *innerForeground; 62 | 63 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *watchScreenWidthConstraint; 64 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *watchScreenHeightConstraint; 65 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *watchFaceYOffsetConstraint; 66 | 67 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *outerRingWidthConstraint; 68 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *innerRingWidthConstraint; 69 | 70 | @end 71 | 72 | @implementation ViewController 73 | 74 | - (void)viewDidLoad { 75 | [super viewDidLoad]; 76 | 77 | // load saved values 78 | NSUserDefaults *def = [NSUserDefaults standardUserDefaults]; 79 | if ([def stringForKey:DefaultsKeyOuterForegroundColor]) 80 | self.outerForeground.text = [def stringForKey:DefaultsKeyOuterForegroundColor]; 81 | if ([def stringForKey:DefaultsKeyOuterBackgroundColor]) 82 | self.outerBackground.text = [def stringForKey:DefaultsKeyOuterBackgroundColor]; 83 | if ([def stringForKey:DefaultsKeyInnerForegroundColor]) 84 | self.innerForeground.text = [def stringForKey:DefaultsKeyInnerForegroundColor]; 85 | if ([def stringForKey:DefaultsKeyInnerBackgroundColor]) 86 | self.innerBackground.text = [def stringForKey:DefaultsKeyInnerBackgroundColor]; 87 | 88 | if ([def objectForKey:DefaultsKeyOuterSize]) 89 | self.outerRingSize.text = [def stringForKey:DefaultsKeyOuterSize]; 90 | if ([def objectForKey:DefaultsKeyOuterWidth]) 91 | self.outerRingWidth.text = [def stringForKey:DefaultsKeyOuterWidth]; 92 | if ([def objectForKey:DefaultsKeyOuterProgress]) 93 | self.outerRingProgress.text = [def stringForKey:DefaultsKeyOuterProgress]; 94 | 95 | if ([def objectForKey:DefaultsKeyInnerSize]) 96 | self.innerRingSize.text = [def stringForKey:DefaultsKeyInnerSize]; 97 | if ([def objectForKey:DefaultsKeyInnerWidth]) 98 | self.innerRingWidth.text = [def stringForKey:DefaultsKeyInnerWidth]; 99 | if ([def objectForKey:DefaultsKeyInnerProgress]) 100 | self.innerRingProgress.text = [def stringForKey:DefaultsKeyInnerProgress]; 101 | 102 | if ([def integerForKey:DefaultsKeyWatchSize] == 42) { 103 | [self switchTo42mm:nil]; 104 | } 105 | 106 | self.outerRing.showPercentage = NO; 107 | self.innerRing.showPercentage = NO; 108 | 109 | self.kMaxNumberOfImages = 30; 110 | self.kProgressOvershoot = 1; 111 | 112 | [self setupColors]; 113 | } 114 | 115 | - (void)viewDidAppear:(BOOL)animated { 116 | [super viewDidAppear:animated]; 117 | 118 | [self setupSizes:nil]; 119 | 120 | CGFloat g = [self.outerRingProgress.text doubleValue]; 121 | CGFloat s = [self.innerRingProgress.text doubleValue]; 122 | [self setGroupProgress:g sessionProgress:s]; 123 | } 124 | 125 | - (void)setGroupProgress:(CGFloat)groupProgress sessionProgress:(CGFloat)sessionProgress { 126 | 127 | [self.outerRing setProgress:groupProgress animated:YES]; 128 | [self.innerRing setProgress:sessionProgress animated:YES]; 129 | } 130 | 131 | - (void)setupSizes:(void (^)())block { 132 | 133 | self.outerRingWidthConstraint.constant = [self.outerRingSize.text doubleValue]; 134 | self.innerRingWidthConstraint.constant = [self.innerRingSize.text doubleValue]; 135 | [self.watchScreen layoutIfNeeded]; 136 | 137 | dispatch_async(dispatch_get_main_queue(), ^{ 138 | self.outerRing.backgroundRingWidth = [self.outerRingWidth.text doubleValue]; 139 | self.outerRing.progressRingWidth = [self.outerRingWidth.text doubleValue]; 140 | 141 | self.innerRing.backgroundRingWidth = [self.innerRingWidth.text doubleValue]; 142 | self.innerRing.progressRingWidth = [self.innerRingWidth.text doubleValue]; 143 | 144 | [self.outerRing setNeedsDisplay]; 145 | [self.innerRing setNeedsDisplay]; 146 | 147 | if (block) 148 | block(); 149 | }); 150 | } 151 | 152 | - (void)setupColors { 153 | 154 | self.outerBackgroundButton.backgroundColor = [UIColor colorWithHexString:self.outerBackground.text]; 155 | self.outerForegroundButton.backgroundColor = [UIColor colorWithHexString:self.outerForeground.text]; 156 | self.innerBackgroundButton.backgroundColor = [UIColor colorWithHexString:self.innerBackground.text]; 157 | self.innerForegroundButton.backgroundColor = [UIColor colorWithHexString:self.innerForeground.text]; 158 | 159 | self.outerRing.primaryColor = self.outerForegroundButton.backgroundColor; 160 | self.outerRing.secondaryColor = self.outerBackgroundButton.backgroundColor; 161 | self.innerRing.primaryColor = self.innerForegroundButton.backgroundColor; 162 | self.innerRing.secondaryColor = self.innerBackgroundButton.backgroundColor; 163 | 164 | [self.watchScreen setNeedsDisplay]; 165 | } 166 | 167 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 168 | 169 | [self saveToDefaults:textField]; 170 | 171 | if ([textField isEqual:self.outerForeground] || 172 | [textField isEqual:self.outerBackground] || 173 | [textField isEqual:self.innerForeground] || 174 | [textField isEqual:self.innerBackground]) { 175 | 176 | [self setupColors]; 177 | return YES; 178 | 179 | } else if ([textField isEqual:self.outerRingProgress] || 180 | [textField isEqual:self.innerRingProgress]) { 181 | 182 | CGFloat g = [self.outerRingProgress.text doubleValue]; 183 | CGFloat s = [self.innerRingProgress.text doubleValue]; 184 | [self setGroupProgress:g sessionProgress:s]; 185 | 186 | return YES; 187 | } 188 | 189 | [self setupSizes:nil]; 190 | return YES; 191 | } 192 | 193 | - (IBAction)switchTo38mm:(id)sender { 194 | 195 | if (self.watchScreenWidthConstraint.constant == 134) return; 196 | 197 | self.watchScreenWidthConstraint.constant = 134; 198 | self.watchScreenHeightConstraint.constant = 168; 199 | [self.view layoutIfNeeded]; 200 | 201 | self.watchFace.image = [UIImage imageNamed:@"bezel38mm"]; 202 | self.watchApp.image = [UIImage imageNamed:@"paged38mm"]; 203 | 204 | [[NSUserDefaults standardUserDefaults] setInteger:38 forKey:DefaultsKeyWatchSize]; 205 | } 206 | 207 | - (IBAction)switchTo42mm:(id)sender { 208 | 209 | if (self.watchScreenWidthConstraint.constant == 154) return; 210 | 211 | self.watchScreenWidthConstraint.constant = 154; 212 | self.watchScreenHeightConstraint.constant = 193; 213 | [self.view layoutIfNeeded]; 214 | 215 | self.watchFace.image = [UIImage imageNamed:@"bezel42mm"]; 216 | self.watchApp.image = [UIImage imageNamed:@"paged42mm"]; 217 | 218 | [[NSUserDefaults standardUserDefaults] setInteger:42 forKey:DefaultsKeyWatchSize]; 219 | } 220 | 221 | - (IBAction)generateImages:(id)sender { 222 | 223 | NSString *folder = [NSDocumentsFolder() stringByAppendingPathComponent:@"img/"]; 224 | NSLog(@"Destination folder: %@", folder); 225 | [NSFileManager findOrCreateDirectoryPath:folder]; 226 | 227 | CGFloat op = [self.outerRingProgress.text doubleValue]; 228 | CGFloat ip = [self.innerRingProgress.text doubleValue]; 229 | 230 | BOOL (^imagegen)(M13ProgressViewRing *, NSString *, CGSize) = ^BOOL(M13ProgressViewRing *iv, NSString *filePath, CGSize size) { 231 | UIGraphicsBeginImageContextWithOptions(size, NO, 2.0); 232 | CGContextRef context = UIGraphicsGetCurrentContext(); 233 | [iv.layer renderInContext:context]; 234 | UIImage *stepImage = UIGraphicsGetImageFromCurrentImageContext(); 235 | UIGraphicsEndImageContext(); 236 | 237 | NSData *pngData = UIImagePNGRepresentation(stepImage); 238 | NSError *error = nil; 239 | [pngData writeToFile:filePath options:NSDataWritingFileProtectionNone error:&error]; 240 | if (error) { 241 | NSLog(@"Error writing to file path = %@", filePath); 242 | UIAlertController *ac = [UIAlertController alertControllerWithTitle:@"Oops, error" message:[error description] preferredStyle:UIAlertControllerStyleAlert]; 243 | [ac addAction:[UIAlertAction actionWithTitle:@"Grumpf" style:UIAlertActionStyleCancel handler:nil]]; 244 | [self presentViewController:ac 245 | animated:YES 246 | completion:nil]; 247 | return NO; 248 | } 249 | 250 | return YES; 251 | }; 252 | 253 | BOOL success = YES; 254 | 255 | // outer ring 256 | CGSize size = self.outerRing.bounds.size; 257 | NSInteger i = 0; 258 | [self.outerRing setProgress:i animated:NO]; 259 | while (i < self.kMaxNumberOfImages + self.kProgressOvershoot) { 260 | NSString *filePath = [folder stringByAppendingPathComponent:[NSString stringWithFormat:@"outer-%ld-%ld-%ld@2x.png", (long)size.width, (long)self.outerRing.progressRingWidth, i]]; 261 | success = imagegen(self.outerRing, filePath, size); 262 | if (!success) 263 | break; 264 | i++; 265 | [self.outerRing setProgress:((float)i / self.kMaxNumberOfImages) animated:NO]; 266 | } 267 | [self.outerRing setProgress:op animated:NO]; 268 | 269 | // inner ring 270 | if (success) { 271 | size = self.innerRing.bounds.size; 272 | i = 0; 273 | [self.innerRing setProgress:i animated:NO]; 274 | while (i < self.kMaxNumberOfImages + self.kProgressOvershoot) { 275 | NSString *filePath = [folder stringByAppendingPathComponent:[NSString stringWithFormat:@"inner-%ld-%ld-%ld@2x.png", (long)size.width, (long)self.innerRing.progressRingWidth, i]]; 276 | success = imagegen(self.innerRing, filePath, size); 277 | if (!success) 278 | break; 279 | i++; 280 | [self.innerRing setProgress:((float)i / self.kMaxNumberOfImages) animated:NO]; 281 | } 282 | [self.innerRing setProgress:ip animated:NO]; 283 | } 284 | 285 | if (success) { 286 | UIAlertController *ac = [UIAlertController alertControllerWithTitle:nil message:@"Images generated with file name format: RING_id–SIZE_in_pt–WIDTH_in_pt–COUNTER into folder:" preferredStyle:UIAlertControllerStyleAlert]; 287 | [ac addTextFieldWithConfigurationHandler:^(UITextField *textField) { 288 | textField.placeholder = @"Destination folder"; 289 | textField.text = folder; 290 | }]; 291 | [ac addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; 292 | [ac addAction:[UIAlertAction actionWithTitle:@"Copy folder" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 293 | [[UIPasteboard generalPasteboard] setString:folder]; 294 | }]]; 295 | [self presentViewController:ac 296 | animated:YES 297 | completion:nil]; 298 | } 299 | } 300 | 301 | - (void)saveToDefaults:(UITextField *)textField { 302 | 303 | NSUserDefaults *def = [NSUserDefaults standardUserDefaults]; 304 | 305 | if ([textField isEqual:self.outerForeground]) { 306 | [def setObject:textField.text forKey:DefaultsKeyOuterForegroundColor]; 307 | } else if ([textField isEqual:self.outerBackground]) { 308 | [def setObject:textField.text forKey:DefaultsKeyOuterBackgroundColor]; 309 | } else if ([textField isEqual:self.innerForeground]) { 310 | [def setObject:textField.text forKey:DefaultsKeyInnerForegroundColor]; 311 | } else if ([textField isEqual:self.innerBackground]) { 312 | [def setObject:textField.text forKey:DefaultsKeyInnerBackgroundColor]; 313 | 314 | } else if ([textField isEqual:self.outerRingSize]) { 315 | [def setObject:textField.text forKey:DefaultsKeyOuterSize]; 316 | } else if ([textField isEqual:self.outerRingWidth]) { 317 | [def setObject:textField.text forKey:DefaultsKeyOuterWidth]; 318 | } else if ([textField isEqual:self.outerRingProgress]) { 319 | [def setObject:textField.text forKey:DefaultsKeyOuterProgress]; 320 | } else if ([textField isEqual:self.innerRingSize]) { 321 | [def setObject:textField.text forKey:DefaultsKeyInnerSize]; 322 | } else if ([textField isEqual:self.innerRingWidth]) { 323 | [def setObject:textField.text forKey:DefaultsKeyInnerWidth]; 324 | } else if ([textField isEqual:self.innerRingProgress]) { 325 | [def setObject:textField.text forKey:DefaultsKeyInnerProgress]; 326 | } 327 | } 328 | 329 | - (IBAction)generateXCAssets:(id)sender { 330 | 331 | NSString *folder = [NSDocumentsFolder() stringByAppendingPathComponent:@"Ring.xcassets/"]; 332 | NSLog(@"Destination folder: %@", folder); 333 | [NSFileManager findOrCreateDirectoryPath:folder]; 334 | 335 | CGFloat op = [self.outerRingProgress.text doubleValue]; 336 | CGFloat ip = [self.innerRingProgress.text doubleValue]; 337 | 338 | NSInteger watchSize = [[NSUserDefaults standardUserDefaults] integerForKey:DefaultsKeyWatchSize]; 339 | if (watchSize == 0) 340 | watchSize = 38; 341 | 342 | BOOL (^imagegen)(M13ProgressViewRing *, NSString *, CGSize) = ^BOOL(M13ProgressViewRing *iv, NSString *filePath, CGSize size) { 343 | UIGraphicsBeginImageContextWithOptions(size, NO, 2.0); 344 | CGContextRef context = UIGraphicsGetCurrentContext(); 345 | [iv.layer renderInContext:context]; 346 | UIImage *stepImage = UIGraphicsGetImageFromCurrentImageContext(); 347 | UIGraphicsEndImageContext(); 348 | 349 | NSData *pngData = UIImagePNGRepresentation(stepImage); 350 | NSError *error = nil; 351 | [pngData writeToFile:filePath options:NSDataWritingFileProtectionNone error:&error]; 352 | if (error) { 353 | NSLog(@"Error writing to file path = %@", filePath); 354 | UIAlertController *ac = [UIAlertController alertControllerWithTitle:@"Oops, error" message:[error description] preferredStyle:UIAlertControllerStyleAlert]; 355 | [ac addAction:[UIAlertAction actionWithTitle:@"Grumpf" style:UIAlertActionStyleCancel handler:nil]]; 356 | [self presentViewController:ac 357 | animated:YES 358 | completion:nil]; 359 | return NO; 360 | } 361 | 362 | return YES; 363 | }; 364 | 365 | // create .xcassets for both 38mm and 42mm 366 | // adjust only the ring size by 20pt 367 | 368 | CGFloat outerSize38 = 0; 369 | CGFloat innerSize38 = 0; 370 | CGFloat outerSize42 = 0; 371 | CGFloat innerSize42 = 0; 372 | 373 | if (watchSize == 38) { 374 | outerSize38 = [self.outerRingSize.text doubleValue]; 375 | innerSize38 = [self.innerRingSize.text doubleValue]; 376 | outerSize42 = outerSize38 + 20.0; 377 | innerSize42 = innerSize38 + 20.0; 378 | } else { 379 | outerSize42 = [self.outerRingSize.text doubleValue]; 380 | innerSize42 = [self.innerRingSize.text doubleValue]; 381 | outerSize38 = outerSize42 - 20.0; 382 | innerSize38 = innerSize42 - 20.0; 383 | } 384 | 385 | 386 | // ok, now make the images for 38mm, then for 42mm 387 | 388 | // for ringSize, always use the value for the 38mm (thus making that the default size). it does not matter much, it's mainly to allow you to create multiple rings 389 | void (^ringSizeProcess)(NSString *, long, long, M13ProgressViewRing *, NSString *) = ^void(NSString *oi, long ringSize, long ringWidth, M13ProgressViewRing *ring, NSString *sizeSuffix) { 390 | NSString *ringSuffix = [NSString stringWithFormat:@"%@-%ld-%ld/", oi, ringSize, ringWidth]; 391 | NSString *ringFolder = [folder stringByAppendingPathComponent:ringSuffix]; 392 | NSLog(@"Ring folder: %@", ringFolder); 393 | [NSFileManager findOrCreateDirectoryPath:ringFolder]; 394 | 395 | NSInteger i = 0; 396 | [ring setProgress:i animated:NO]; 397 | while (i < self.kMaxNumberOfImages + self.kProgressOvershoot) { 398 | NSString *oneRingFolder = [ringFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%ld-%ld-%ld.imageset/", oi, ringSize, (long)ringWidth, i]]; 399 | [NSFileManager findOrCreateDirectoryPath:oneRingFolder]; 400 | 401 | NSString *filePath = [oneRingFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%ld-%ld-%ld-%@@2x.png", oi, ringSize, (long)ringWidth, i, sizeSuffix]]; 402 | BOOL success = imagegen(ring, filePath, ring.bounds.size); 403 | if (!success) 404 | break; 405 | i++; 406 | [ring setProgress:((float)i / self.kMaxNumberOfImages) animated:NO]; 407 | } 408 | }; 409 | 410 | // now generate Contents.json file for both outer and inner rings 411 | // example Contents.json file 412 | /* 413 | { 414 | "images" : [ 415 | { 416 | "idiom" : "watch", 417 | "scale" : "2x" 418 | }, 419 | { 420 | "idiom" : "watch", 421 | "screenWidth" : "{130,145}", 422 | "filename" : "outer-4-0@38mm-2x.png", 423 | "scale" : "2x" 424 | }, 425 | { 426 | "idiom" : "watch", 427 | "screenWidth" : "{146,165}", 428 | "filename" : "outer-4-0@42mm-2x.png", 429 | "scale" : "2x" 430 | } 431 | ], 432 | "info" : { 433 | "version" : 1, 434 | "author" : "xcode" 435 | } 436 | } 437 | */ 438 | void (^jsonProcess)(NSString *, long, long) = ^void(NSString *oi, long ringSize, long ringWidth) { 439 | NSString *ringSuffix = [NSString stringWithFormat:@"%@-%ld-%ld/", oi, ringSize, ringWidth]; 440 | NSString *ringFolder = [folder stringByAppendingPathComponent:ringSuffix]; 441 | 442 | NSInteger i = 0; 443 | while (i < self.kMaxNumberOfImages + self.kProgressOvershoot) { 444 | NSString *oneRingFolder = [ringFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-%ld-%ld-%ld.imageset/", oi, ringSize, (long)ringWidth, i]]; 445 | 446 | NSDictionary *d = @{ 447 | @"info": @{ 448 | @"version": @(1), 449 | @"author": @"xcode" 450 | }, 451 | @"images": @[ 452 | @{ 453 | @"idiom": @"watch", 454 | @"scale": @"2x", 455 | }, 456 | @{ 457 | @"idiom": @"watch", 458 | @"screenWidth": @"{130,145}", 459 | @"filename": [NSString stringWithFormat:@"%@-%ld-%ld-%ld-%@@2x.png", oi, ringSize, (long)ringWidth, i, @"38mm"], 460 | @"scale": @"2x", 461 | }, 462 | @{ 463 | @"idiom": @"watch", 464 | @"screenWidth": @"{146,165}", 465 | @"filename": [NSString stringWithFormat:@"%@-%ld-%ld-%ld-%@@2x.png", oi, ringSize, (long)ringWidth, i, @"42mm"], 466 | @"scale": @"2x", 467 | } 468 | ] 469 | }; 470 | NSString *filePath = [oneRingFolder stringByAppendingPathComponent:@"Contents.json"]; 471 | NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO]; 472 | [stream open]; 473 | [NSJSONSerialization writeJSONObject:d toStream:stream options:NSJSONWritingPrettyPrinted error:nil]; 474 | [stream close]; 475 | i++; 476 | } 477 | }; 478 | 479 | 480 | void (^restoreDisplay)() = ^void() { 481 | // restore progress bar values 482 | 483 | [self.outerRing setProgress:op animated:NO]; 484 | [self.innerRing setProgress:ip animated:NO]; 485 | 486 | // restore watch size 487 | 488 | if (watchSize == 38) { 489 | [self switchTo38mm:nil]; 490 | 491 | self.outerRingSize.text = [@(outerSize38) stringValue]; 492 | self.innerRingSize.text = [@(innerSize38) stringValue]; 493 | [self setupSizes:nil]; 494 | 495 | } else { 496 | // do nothing, it's already at 42mm 497 | } 498 | 499 | 500 | { 501 | UIAlertController *ac = [UIAlertController alertControllerWithTitle:nil message:@"Generated .xcassets including both 38mm and 42mm" preferredStyle:UIAlertControllerStyleAlert]; 502 | [ac addTextFieldWithConfigurationHandler:^(UITextField *textField) { 503 | textField.placeholder = @"Destination folder"; 504 | textField.text = folder; 505 | }]; 506 | [ac addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; 507 | [ac addAction:[UIAlertAction actionWithTitle:@"Copy folder" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { 508 | [[UIPasteboard generalPasteboard] setString:folder]; 509 | }]]; 510 | [self presentViewController:ac 511 | animated:YES 512 | completion:nil]; 513 | } 514 | }; 515 | 516 | 517 | 518 | if (watchSize != 38) { 519 | [self switchTo38mm:nil]; 520 | 521 | self.outerRingSize.text = [@(outerSize38) stringValue]; 522 | self.innerRingSize.text = [@(innerSize38) stringValue]; 523 | 524 | [self setupSizes:^{ 525 | ringSizeProcess(@"outer", (long)outerSize38, (long)self.outerRing.progressRingWidth, self.outerRing, @"38mm"); 526 | ringSizeProcess(@"inner", (long)innerSize38, (long)self.innerRing.progressRingWidth, self.innerRing, @"38mm"); 527 | 528 | { 529 | [self switchTo42mm:nil]; 530 | 531 | self.outerRingSize.text = [@(outerSize42) stringValue]; 532 | self.innerRingSize.text = [@(innerSize42) stringValue]; 533 | 534 | [self setupSizes:^{ 535 | ringSizeProcess(@"outer", (long)outerSize38, (long)self.outerRing.progressRingWidth, self.outerRing, @"42mm"); 536 | ringSizeProcess(@"inner", (long)innerSize38, (long)self.innerRing.progressRingWidth, self.innerRing, @"42mm"); 537 | 538 | jsonProcess(@"outer", (long)outerSize38, (long)self.outerRing.progressRingWidth); 539 | jsonProcess(@"inner", (long)innerSize38, (long)self.innerRing.progressRingWidth); 540 | 541 | restoreDisplay(); 542 | }]; 543 | } 544 | }]; 545 | } else { 546 | ringSizeProcess(@"outer", (long)outerSize38, (long)self.outerRing.progressRingWidth, self.outerRing, @"38mm"); 547 | ringSizeProcess(@"inner", (long)innerSize38, (long)self.innerRing.progressRingWidth, self.innerRing, @"38mm"); 548 | 549 | { 550 | [self switchTo42mm:nil]; 551 | 552 | self.outerRingSize.text = [@(outerSize42) stringValue]; 553 | self.innerRingSize.text = [@(innerSize42) stringValue]; 554 | 555 | [self setupSizes:^{ 556 | ringSizeProcess(@"outer", (long)outerSize38, (long)self.outerRing.progressRingWidth, self.outerRing, @"42mm"); 557 | ringSizeProcess(@"inner", (long)innerSize38, (long)self.innerRing.progressRingWidth, self.innerRing, @"42mm"); 558 | 559 | jsonProcess(@"outer", (long)outerSize38, (long)self.outerRing.progressRingWidth); 560 | jsonProcess(@"inner", (long)innerSize38, (long)self.innerRing.progressRingWidth); 561 | 562 | restoreDisplay(); 563 | }]; 564 | } 565 | } 566 | } 567 | 568 | @end 569 | --------------------------------------------------------------------------------