├── 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 | 
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 |
--------------------------------------------------------------------------------