32 | #import "DRColorPickerViewController.h"
33 |
34 | // stands for Digital Ruby Color Picker Translation
35 | NSString* DRCPTR(NSString* key, ...);
36 |
37 | UIImage* DRColorPickerImage(NSString* subPath);
38 |
39 | // size of each individual color in points on iPhone, default is 42
40 | extern CGFloat DRColorPickerThumbnailSizeInPointsPhone;
41 |
42 | // size of each individual color in points on iPad, default is 54
43 | extern CGFloat DRColorPickerThumbnailSizeInPointsPad;
44 |
45 | // font to use for the color picker
46 | extern UIFont* DRColorPickerFont;
47 |
48 | // color of labels text in the color picker views
49 | extern UIColor* DRColorPickerLabelColor;
50 |
51 | // background color of the view controllers
52 | extern UIColor* DRColorPickerBackgroundColor;
53 |
54 | // border color of the colors - pick something that is different than the background color to help your colors stand out
55 | extern UIColor* DRColorPickerBorderColor;
56 |
57 | // maximum colors in the favorites, recent and standard color list - default 200
58 | extern NSInteger DRColorPickerStoreMaxColors;
59 |
60 | // should a saturation bar be shown on the color wheel view? Default is NO
61 | extern BOOL DRColorPickerShowSaturationBar;
62 |
63 | // highlight the last hue picked in the hue view, default is NO
64 | extern BOOL DRColorPickerHighlightLastHue;
65 |
66 | // if you are allowing textures, they default to JPEG2000 to save disk space. This is slower to save and may have a tiny loss of quality,
67 | // so if performance is a concern, set this to YES.
68 | // ***** once you have set this once for your app, do not ever change it as it will invalidate the hashes for all your textures (this would be bad) *****
69 | extern BOOL DRColorPickerUsePNG;
70 |
71 | // default is 0.9f, value of 0.0f to 1.0f, 1.0f is lossless but biggest file size and hence more disk space used - ignored if using PNG for texture
72 | // ***** once you have set this once for your app, do not ever change it as it will invalidate the hashes for all your textures (this would be bad) *****
73 | extern CGFloat DRColorPickerJPEG2000Quality;
74 |
75 | // new in iOS 8 is the concept of shared folders - if you want the color picker to use a shared folder accessible by apps
76 | // with the same group id, set this to your group id, otherwise leave nil to use the documents folder. Default is nil
77 | extern NSString* DRColorPickerSharedAppGroup;
78 |
--------------------------------------------------------------------------------
/DRColorPickerExample.xcodeproj/xcuserdata/Tim.xcuserdatad/xcschemes/DRColorPickerExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/DRColorPickerExample.xcodeproj/xcuserdata/jjxtra.xcuserdatad/xcschemes/DRColorPickerExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
94 |
100 |
101 |
102 |
103 |
105 |
106 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Digital Ruby, LLC Color Picker
5 |
6 | I'm Jeff Johnson (@jjxtra) and I created DRColorPicker to use in my app You Doodle for iOS.
7 |
8 | In order to give back to the community, I have open sourced this color picker under the MIT license.
9 |
10 | Are you using DRColorPicker in your own app? Send me an email (jjxtra AT gmail DOT com) and I'll post your app here under the "Apps Using" section.
11 |
12 | DRColorPicker is a world class color picker for iOS. Here is a list of the features:
13 |
14 |
15 | - Supports any resolution, portrait and landscape, and works on iPhone and iPad
16 | - Allows favorite colors, recent colors, colors by hue, color wheel or import of texture
17 | - Color wheel shows the RGB color and allows changing the color by typing an RGB value
18 | - Alpha of color can be changed
19 | - State is saved and loaded automatically to / from a JSON file, textures are saved to files
20 | - Textures can be stored in JPEG 2000 (default) or PNG
21 | - Texture de-duplication ensures that only one version of each texture is stored
22 | - Favorites can be managed by long-tapping on a color
23 | - Imports colors from the NEO Color Picker automatically
24 | - Localized into most languages
25 |
26 |
27 | Requirements:
28 |
29 | - Xcode 6 or newer recommended
30 | - iOS 6 or greater
31 | - ARC enabled for the color picker source code
32 | - Modules enabled for the project (i.e. @import ...)
33 |
34 |
35 | Install via CocoaPods:
36 |
pod 'DRColorPicker'
37 |
38 |
39 | Manual install: Add the DRColorPickerExample/DRColorPicker folder to your iOS project. The DRColorPickerExample folder contains a sample project with a view controller that will show you how to setup and display the color picker.
40 |
41 |
42 | Apps using DRColorPicker
43 |
44 | You Doodle
45 | Website: http://youdoodle.net
46 | Download: http://bit.ly/YouDoodleApp
47 |
48 | Living Flame
49 | Website: https://livingflame.wordpress.com
50 | Download: https://itunes.apple.com/us/app/living-flame/id1251818535?mt=8
51 |
52 | Universally
53 | Website: https://www.facebook.com/MusicallyMediaPlayer/
54 | Download: https://itunes.apple.com/us/app/universally-media-fitness/id1147036298?ls=1&mt=8
55 |
56 |
57 | Are you using DRColorPicker in your own app? Send me an email (jjxtra AT gmail DOT com) and I'll post your app here under the "Apps Using" section.
58 |
59 |
60 |
61 | Screenshots:
62 | iPad
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | iPhone
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPickerGridViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPickerGridViewController.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPickerGridViewController.h"
32 | #import "DRColorPickerStore.h"
33 | #import "DRColorPickerHomeViewController.h"
34 | #import "DRColorPicker.h"
35 |
36 | @interface DRColorPickerGridViewController ()
37 |
38 | @property (nonatomic, strong) DRColorPickerGridView* gridView;
39 | @property (nonatomic, strong) DRColorPickerColorView* colorViewLongPressed;
40 | @property (nonatomic, strong) NSIndexPath* colorViewIndexPath;
41 |
42 | @end
43 |
44 | @implementation DRColorPickerGridViewController
45 |
46 | - (id) init
47 | {
48 | if ((self = [super init]) == nil) { return nil; }
49 |
50 | self.gridView = [[DRColorPickerGridView alloc] init];
51 |
52 | return self;
53 | }
54 |
55 | - (void) viewDidLoad
56 | {
57 | [super viewDidLoad];
58 |
59 | self.gridView.frame = self.view.bounds;
60 | [self.view addSubview:self.gridView];
61 |
62 | __weak DRColorPickerGridViewController* weakSelf = self;
63 | self.gridView.colorSelectedBlock = ^(DRColorPickerColor* color)
64 | {
65 | DRColorPickerGridViewController* strongSelf = weakSelf;
66 | if (strongSelf.colorSelectedBlock != nil)
67 | {
68 | strongSelf.colorSelectedBlock(color, strongSelf);
69 | }
70 | };
71 | self.gridView.colorLongPressBlock = ^(DRColorPickerColorView* colorView, NSIndexPath* indexPath)
72 | {
73 | DRColorPickerGridViewController* strongSelf = weakSelf;
74 | if (strongSelf.list == DRColorPickerStoreListFavorites)
75 | {
76 | [strongSelf handleLongTap:colorView indexPath:indexPath];
77 | }
78 | };
79 | }
80 |
81 | - (void) handleLongTap:(DRColorPickerColorView*)colorView indexPath:(NSIndexPath*)indexPath
82 | {
83 | self.colorViewLongPressed = colorView;
84 | self.colorViewIndexPath = indexPath;
85 | UIActionSheet* actionSheet = [[UIActionSheet alloc] init];
86 | NSInteger offset = 0;
87 | if (indexPath.row != 0)
88 | {
89 | [actionSheet addButtonWithTitle:DRCPTR(@"MoveToFront")];
90 | offset = 1;
91 | }
92 | [actionSheet addButtonWithTitle:DRCPTR(@"Delete")];
93 | [actionSheet addButtonWithTitle:DRCPTR(@"Cancel")];
94 | actionSheet.destructiveButtonIndex = offset;
95 | actionSheet.cancelButtonIndex = offset + 1;
96 | actionSheet.delegate = self;
97 | CGRect rect = [self.view convertRect:colorView.bounds fromView:colorView];
98 | [actionSheet showFromRect:rect inView:self.view animated:YES];
99 | }
100 |
101 | - (void) updateColorsFromList
102 | {
103 | NSArray* colors = [[DRColorPickerStore sharedInstance] colorsForList:self.list];
104 | [self.gridView setColors:colors];
105 | }
106 |
107 | - (void) setList:(DRColorPickerStoreList)list
108 | {
109 | _list = list;
110 |
111 | [self updateColorsFromList];
112 | }
113 |
114 | - (void) deleteColor
115 | {
116 | [[DRColorPickerStore sharedInstance] deleteColor:self.colorViewLongPressed.color fromList:self.list];
117 | @try
118 | {
119 | [self.gridView deleteItemsAtIndexPaths:@[ self.colorViewIndexPath]];
120 | }
121 | @catch (...)
122 | {
123 | [self.gridView reloadData];
124 | }
125 | }
126 |
127 | - (void) moveColorToFront
128 | {
129 | [[DRColorPickerStore sharedInstance] upsertColor:self.colorViewLongPressed.color list:self.list moveToFront:YES];
130 | [self.gridView moveItemAtIndexPath:self.colorViewIndexPath toIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
131 | }
132 |
133 | - (void) actionSheet:(UIActionSheet*)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
134 | {
135 | if (buttonIndex == actionSheet.destructiveButtonIndex)
136 | {
137 | [self deleteColor];
138 | }
139 | else if ([[actionSheet buttonTitleAtIndex:buttonIndex] isEqualToString:DRCPTR(@"MoveToFront")])
140 | {
141 | [self moveColorToFront];
142 | }
143 | self.colorViewLongPressed = nil;
144 | self.colorViewIndexPath = nil;
145 | }
146 |
147 | @end
148 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPickerColorView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPickerColorView.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPickerColorView.h"
32 | #import "DRColorPickerStore.h"
33 | #import "DRColorPicker.h"
34 |
35 | static UIColor* s_borderColor;
36 | static UIImage* s_transparencyImage;
37 |
38 | @interface DRColorPickerColorView ()
39 |
40 | @property (nonatomic, strong) UIImageView* thumbnailView;
41 | @property (nonatomic, strong) UIImageView* transparencyView;
42 |
43 | @end
44 |
45 | @implementation DRColorPickerColorView
46 |
47 | + (void) initialize
48 | {
49 | if (self == [DRColorPickerColorView class])
50 | {
51 | s_borderColor = (DRColorPickerBorderColor ?: [UIColor colorWithWhite:0.85f alpha:1.0f]);
52 | s_transparencyImage = DRColorPickerImage(@"images/common/drcolorpicker-checkerboard.png");
53 | }
54 | }
55 |
56 | - (id) initWithFrame:(CGRect)frame
57 | {
58 | if ((self = [super initWithFrame:frame]) == nil) { return nil; }
59 |
60 | self.transparencyView = [[UIImageView alloc] initWithFrame:self.bounds];
61 | self.transparencyView.contentScaleFactor = [[UIScreen mainScreen] scale];
62 | self.transparencyView.clipsToBounds = YES;
63 | [self addSubview:self.transparencyView];
64 |
65 | self.thumbnailView = [[UIImageView alloc] initWithFrame:self.bounds];
66 | self.thumbnailView.contentScaleFactor = [[UIScreen mainScreen] scale];
67 | self.thumbnailView.layer.cornerRadius = self.thumbnailView.bounds.size.width * 0.5f;
68 | self.thumbnailView.layer.borderWidth = 1.0f;
69 | self.thumbnailView.layer.borderColor = s_borderColor.CGColor;
70 | [self addSubview:self.thumbnailView];
71 |
72 | return self;
73 | }
74 |
75 | - (void) setColor:(DRColorPickerColor*)color
76 | {
77 | _color = color;
78 | self.highlighted = NO;
79 |
80 | if (color == nil)
81 | {
82 | self.hidden = YES;
83 | self.thumbnailView.image = nil;
84 | return;
85 | }
86 |
87 | self.hidden = NO;
88 |
89 | self.thumbnailView.image = nil;
90 | if (self.color.rgbColor == nil)
91 | {
92 | self.thumbnailView.alpha = self.color.alpha;
93 | self.thumbnailView.backgroundColor = [UIColor clearColor];
94 |
95 | [[DRColorPickerStore sharedInstance] thumbnailImageForColor:self.color completion:^(UIImage* image)
96 | {
97 | // if we are still the same color, set the image
98 | if (self.color.rgbColor == nil)
99 | {
100 | self.thumbnailView.image = image;
101 | }
102 | }];
103 | }
104 | else
105 | {
106 | self.thumbnailView.alpha = 1.0f;
107 | self.thumbnailView.backgroundColor = self.color.rgbColor;
108 | }
109 |
110 | self.transparencyView.hidden = (self.color.alpha == 1.0f);
111 |
112 | if (self.transparencyView.hidden)
113 | {
114 | self.transparencyView.image = nil;
115 | self.transparencyView.layer.cornerRadius = 0.0f;
116 | }
117 | else
118 | {
119 | self.transparencyView.image = s_transparencyImage;
120 | self.transparencyView.layer.cornerRadius = self.transparencyView.bounds.size.width * 0.5f;
121 | }
122 | }
123 |
124 | - (void) setHighlighted:(BOOL)highlighted
125 | {
126 | if (_highlighted != highlighted)
127 | {
128 | _highlighted = highlighted;
129 |
130 | if (highlighted)
131 | {
132 | self.thumbnailView.layer.cornerRadius = 0.0f;
133 | self.thumbnailView.layer.shadowColor = self.thumbnailView.layer.borderColor;
134 | self.thumbnailView.layer.shadowOffset = CGSizeZero;
135 | self.thumbnailView.layer.shadowRadius = 6.0f;
136 | self.thumbnailView.layer.shadowOpacity = 0.9f;
137 | self.thumbnailView.layer.borderWidth = 2.0f;
138 | }
139 | else
140 | {
141 | self.thumbnailView.layer.cornerRadius = self.thumbnailView.bounds.size.width * 0.5f;
142 | self.thumbnailView.layer.shadowColor = [UIColor clearColor].CGColor;
143 | self.thumbnailView.layer.shadowOffset = CGSizeZero;
144 | self.thumbnailView.layer.shadowRadius = 0.0f;
145 | self.thumbnailView.layer.shadowOpacity = 0.0f;
146 | self.thumbnailView.layer.borderWidth = 1.0f;
147 | }
148 | }
149 | }
150 |
151 | @end
152 |
153 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPickerHorizontalCollectionViewLayout.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPickerHorizontalCollectionViewLayout.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPickerHorizontalCollectionViewLayout.h"
32 | #import "DRColorPickerStore.h"
33 |
34 | @implementation DRColorPickerHorizontalCollectionViewLayout
35 | {
36 | CGSize _itemSize;
37 | NSInteger _sections;
38 | NSInteger _cellCount;
39 | CGFloat _padding;
40 | CGFloat _borderPadding;
41 | CGFloat _horizontalPadding;
42 | CGFloat _verticalPadding;
43 | CGRect _lastRect;
44 | NSMutableArray* _allAttributes;
45 | }
46 |
47 | - (id) init
48 | {
49 | if ((self = [super init]) == nil) { return nil; }
50 |
51 | CGFloat size = [DRColorPickerStore thumbnailSizePixels] / UIScreen.mainScreen.scale;
52 | _itemSize = CGSizeMake(size, size);
53 | _padding = 4.0f;
54 | _borderPadding = 4.0f;
55 | _allAttributes = [NSMutableArray array];
56 |
57 | return self;
58 | }
59 |
60 | - (void) prepareLayout
61 | {
62 | _sections = [self.collectionView numberOfSections];
63 | _cellCount = 0;
64 | for (NSInteger i = 0; i < _sections; i++)
65 | {
66 | _cellCount += [self.collectionView numberOfItemsInSection:i];
67 | }
68 | _lastRect = self.collectionView.bounds;
69 | }
70 |
71 | - (CGSize) calculatePages
72 | {
73 | CGSize baseSize = self.collectionView.bounds.size;
74 | CGSize adjustedSize = CGSizeMake(baseSize.width - _borderPadding - _borderPadding, baseSize.height - _borderPadding - _borderPadding);
75 | _rows = floorf(adjustedSize.height / _itemSize.height);
76 | while (_rows > 0)
77 | {
78 | CGFloat rowsHeight = (_rows * _itemSize.height);
79 | CGFloat rowsHeightWithPadding = rowsHeight + (_padding * _rows - 1);
80 | if (rowsHeightWithPadding < adjustedSize.height)
81 | {
82 | if (self.collectionView.contentMode == UIViewContentModeCenter)
83 | {
84 | CGFloat maxSpacing = (adjustedSize.height - rowsHeight) / (CGFloat)(_rows - 1);
85 | _verticalPadding = MAX(maxSpacing, _padding);
86 | }
87 | else
88 | {
89 | _verticalPadding = _padding;
90 | }
91 | break;
92 | }
93 | _rows--;
94 | }
95 |
96 | _columns = floorf(baseSize.width / _itemSize.width);
97 | while (_columns > 0)
98 | {
99 | CGFloat columnsWidth = (_columns * _itemSize.width);
100 | CGFloat columnsWidthWithPadding = columnsWidth + (_padding * _columns - 1);
101 | if (columnsWidthWithPadding < adjustedSize.width)
102 | {
103 | CGFloat maxSpacing = (adjustedSize.width - columnsWidth) / (CGFloat)(_columns - 1);
104 | _horizontalPadding = MAX(maxSpacing, _padding);
105 | break;
106 | }
107 | _columns--;
108 | }
109 |
110 | _itemsPerPage = _rows * _columns;
111 | _numberOfPages = ceilf((CGFloat)_cellCount / (CGFloat)_itemsPerPage);
112 |
113 | baseSize.width = _numberOfPages * baseSize.width;
114 |
115 | return baseSize;
116 | }
117 |
118 | - (CGSize) collectionViewContentSize
119 | {
120 | return [self calculatePages];
121 | }
122 |
123 | - (NSArray*) layoutAttributesForElementsInRect:(CGRect)rect
124 | {
125 | if (_allAttributes.count != 0)
126 | {
127 | return _allAttributes;
128 | }
129 |
130 | for (NSUInteger i = 0; i < _sections; i++)
131 | {
132 | NSInteger sectionItems = [self.collectionView numberOfItemsInSection:i];
133 | for (NSUInteger j = 0; j < sectionItems; j++)
134 | {
135 | NSIndexPath* indexPath = [NSIndexPath indexPathForRow:j inSection:i];
136 | UICollectionViewLayoutAttributes *attr = [self internalLayoutForAttributesForCellAtIndexPath:indexPath];
137 | [_allAttributes addObject:attr];
138 | }
139 | }
140 |
141 | return _allAttributes;
142 | }
143 |
144 | - (UICollectionViewLayoutAttributes*) layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
145 | {
146 | return [self internalLayoutForAttributesForCellAtIndexPath:indexPath];
147 | }
148 |
149 | - (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
150 | {
151 | BOOL result = !CGSizeEqualToSize(newBounds.size, _lastRect.size);
152 | _lastRect = newBounds;
153 |
154 | if (result)
155 | {
156 | [_allAttributes removeAllObjects];
157 | }
158 |
159 | return result;
160 | }
161 |
162 | - (UICollectionViewLayoutAttributes*) internalLayoutForAttributesForCellAtIndexPath:(NSIndexPath*)indexPath
163 | {
164 | UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
165 |
166 | if (_rows <= 0 || _columns <= 0)
167 | {
168 | return attr;
169 | }
170 |
171 | NSInteger row = indexPath.row;
172 | CGRect bounds = self.collectionView.bounds;
173 | NSInteger columnPosition = row % _columns;
174 | NSInteger rowPosition = (row / _columns) % _rows;
175 | NSInteger itemPage = floorf(row / _itemsPerPage);
176 |
177 | CGRect frame;
178 | frame.origin.x = itemPage * bounds.size.width + (columnPosition * _itemSize.width);
179 | frame.origin.x += _borderPadding + (columnPosition * _horizontalPadding);
180 | frame.origin.y = rowPosition * _itemSize.height;
181 | frame.origin.y += _borderPadding + (rowPosition * _verticalPadding);
182 | frame.size = _itemSize;
183 |
184 | attr.frame = CGRectIntegral(frame);
185 |
186 | return attr;
187 | }
188 |
189 | - (void) invalidateLayout
190 | {
191 | [super invalidateLayout];
192 |
193 | [_allAttributes removeAllObjects];
194 | }
195 |
196 | @end
197 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPickerColor.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPickerColor.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPickerColor.h"
32 | #import "DRColorPicker+UIColor.h"
33 | #import "DRColorPickerStore.h"
34 |
35 | @implementation DRColorPickerColor
36 | {
37 | UIImage* _image;
38 | UIImage* _thumbnailImage;
39 | }
40 |
41 | - (instancetype) initWithColor:(UIColor*)color
42 | {
43 | if ((self = [super init]) == nil) { return nil; }
44 |
45 | if (!color.canProvideRGBComponents)
46 | {
47 | color = [UIColor redColor];
48 | }
49 |
50 | self.rgbColor = color;
51 | self.alpha = color.alpha;
52 |
53 | return self;
54 | }
55 |
56 | - (instancetype) initWithImage:(UIImage*)image
57 | {
58 | if ((self = [super init]) == nil) { return nil; }
59 |
60 | NSAssert(image != nil, @"Image must not be nil");
61 |
62 | // on iOS 6, to account for older devices, make the max size smaller
63 | CGFloat systemVersion = [[UIDevice currentDevice].systemVersion floatValue];
64 | CGFloat maxSize = MIN((systemVersion >= 7.0f ? 1024.0f : 512.0f), MAX(image.size.width / image.scale, image.size.height / image.scale));
65 |
66 | {
67 | CGRect rect = [self fitSize:image.size inRect:CGRectMake(0.0f, 0.0f, maxSize, maxSize)];
68 | _image = [self normalizeImage:image size:rect.size clip:NO];
69 | }
70 |
71 | {
72 | CGFloat size = [DRColorPickerStore thumbnailSizePixels];
73 | _thumbnailImage = [self normalizeImage:_image size:CGSizeMake(size, size) clip:YES];
74 | }
75 |
76 | self.alpha = 1.0f;
77 |
78 | return self;
79 | }
80 |
81 | - (instancetype) initWithDictionary:(NSDictionary*)dictionary
82 | {
83 | if ((self = [super init]) == nil) { return nil; }
84 |
85 | _alpha = ((NSNumber*)dictionary[@"Alpha"]).floatValue;
86 | NSString* rgbaHex = (NSString*)dictionary[@"Rgba"];
87 | if (rgbaHex.length != 0)
88 | {
89 | _rgbColor = [UIColor colorWithHexString:rgbaHex];
90 | }
91 | _fullImageHash = (NSString*)dictionary[@"FullImageHash"];
92 |
93 | return self;
94 | }
95 |
96 | - (instancetype) initWithClone:(DRColorPickerColor*)color
97 | {
98 | if ((self = [super init]) == nil) { return nil; }
99 |
100 | _alpha = color.alpha;
101 | _rgbColor = color.rgbColor;
102 | _image = color.image;
103 | _thumbnailImage = color.thumbnailImage;
104 | _fullImageHash = color.fullImageHash;
105 |
106 | return self;
107 | }
108 |
109 | - (UIImage*) normalizeImage:(UIImage*)image size:(CGSize)size clip:(BOOL)clip
110 | {
111 | if (image == nil)
112 | {
113 | return nil;
114 | }
115 |
116 | // we normalize images so that the hashes compute properly - compressed formats are stored in a variety of pixel and alpha formats,
117 | // and we want everything to be BGRA 32 bits per pixel, 8 bits per component
118 | CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
119 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
120 | CGContextRef ctx = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, 4 * rect.size.width, colorSpaceRef, (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little));
121 | CGColorSpaceRelease(colorSpaceRef);
122 | CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
123 | CGContextSetBlendMode(ctx, kCGBlendModeCopy);
124 | CGContextClearRect(ctx, rect);
125 | if (clip)
126 | {
127 | CGContextAddEllipseInRect(ctx, rect);
128 | CGContextClip(ctx);
129 | }
130 | CGContextDrawImage(ctx, rect, image.CGImage);
131 | CGImageRef imgRef = CGBitmapContextCreateImage(ctx);
132 | UIImage* normalizedImage = [UIImage imageWithCGImage:imgRef];
133 | CGImageRelease(imgRef);
134 | CGContextRelease(ctx);
135 |
136 | return normalizedImage;
137 | }
138 |
139 | - (CGRect) fitSize:(CGSize)size inRect:(CGRect)inRect
140 | {
141 | CGFloat widthScale = (inRect.size.width / size.width);
142 | CGFloat heightScale = (inRect.size.height / size.height);
143 | CGFloat ratio = MIN(widthScale, heightScale);
144 | CGSize newSize = CGSizeMake(size.width * ratio, size.height * ratio);
145 | CGFloat x = inRect.origin.x + (inRect.size.width - newSize.width) / 2.0f;
146 | CGFloat y = inRect.origin.y + (inRect.size.height - newSize.height) / 2.0f;
147 | CGRect newFrame = CGRectMake(x, y, newSize.width, newSize.height);
148 |
149 | return CGRectMake(0.0f, 0.0f, ceilf(newFrame.size.width), ceilf(newFrame.size.height));
150 | }
151 |
152 | - (void) setAlpha:(CGFloat)alpha
153 | {
154 | _alpha = alpha;
155 |
156 | if (self.rgbColor != nil)
157 | {
158 | self.rgbColor = [self.rgbColor colorWithAlphaComponent:alpha];
159 | }
160 | }
161 |
162 | - (NSDictionary*) dictionary
163 | {
164 | NSMutableDictionary* d = [NSMutableDictionary dictionary];
165 |
166 | d[@"Alpha"] = @(self.alpha);
167 | if (self.rgbColor != nil)
168 | {
169 | d[@"Rgba"] = self.rgbColor.hexStringFromColor;
170 | }
171 | if (self.fullImageHash.length != 0)
172 | {
173 | d[@"FullImageHash"] = self.fullImageHash;
174 | }
175 |
176 | return d;
177 | }
178 |
179 | - (void) clearImages
180 | {
181 | _image = nil;
182 | _thumbnailImage = nil;
183 | }
184 |
185 | - (UIImage*) image
186 | {
187 | if (_image == nil && self.fullImageHash.length != 0)
188 | {
189 | NSString* path = [[DRColorPickerStore sharedInstance] fullPathForColor:self];
190 | return [UIImage imageWithContentsOfFile:path];
191 | }
192 |
193 | return _image;
194 | }
195 |
196 | - (UIImage*) thumbnailImage
197 | {
198 | if (_thumbnailImage == nil && self.fullImageHash.length != 0)
199 | {
200 | NSString* path = [[DRColorPickerStore sharedInstance] thumbnailPathForColor:self];
201 | return [UIImage imageWithContentsOfFile:path];
202 | }
203 |
204 | return _thumbnailImage;
205 | }
206 |
207 | - (NSString*) description
208 | {
209 | return [NSString stringWithFormat:@"RGB: %@, Alpha: %f, Hash: %@, %@", self.rgbColor.hexStringFromColor, self.alpha, self.fullImageHash, super.description];
210 | }
211 |
212 | @end
213 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPickerHueView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPickerHueView.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPickerHueView.h"
32 | #import "DRColorPicker.h"
33 |
34 | #define DR_COLOR_PICKER_HUE_VIEW_PAGE_COUNT 12
35 |
36 | @interface DRColorPickerHueView ()
37 |
38 | @property (nonatomic, strong) UIImageView* hueBar;
39 | @property (nonatomic, strong) UIImageView* hueIndicator;
40 | @property (nonatomic, strong) NSMutableArray* hueColors;
41 | @property (nonatomic, assign) NSInteger lastPage;
42 |
43 | @end
44 |
45 | @implementation DRColorPickerHueView
46 |
47 | - (id) initWithFrame:(CGRect)frame
48 | {
49 | if ((self = [super initWithFrame:frame]) == nil) { return nil; }
50 |
51 | self.hueGrid = [[DRColorPickerGridView alloc] init];
52 | __weak DRColorPickerHueView* weakSelf = self;
53 | self.hueGrid.scrolledBlock = ^
54 | {
55 | [weakSelf updatePage];
56 | };
57 | [self addSubview:self.hueGrid];
58 |
59 | UIImage* hueBarImage = DRColorPickerImage(@"images/common/drcolorpicker-color-bar.png");
60 | self.hueBar = [[UIImageView alloc] initWithImage:hueBarImage];
61 | self.hueBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
62 | self.hueBar.userInteractionEnabled = YES;
63 | UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hueBarTapped:)];
64 | [self.hueBar addGestureRecognizer:tapGesture];
65 | UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(hueBarPanned:)];
66 | [self.hueBar addGestureRecognizer:panGesture];
67 | [self addSubview:self.hueBar];
68 |
69 | self.hueIndicator = [[UIImageView alloc] initWithImage:DRColorPickerImage(@"images/common/drcolorpicker-brightnessguide.png")];
70 | self.hueIndicator.layer.shadowColor = [UIColor blackColor].CGColor;
71 | self.hueIndicator.layer.shadowOffset = CGSizeZero;
72 | self.hueIndicator.layer.shadowRadius = 1;
73 | self.hueIndicator.layer.shadowOpacity = 0.8f;
74 | self.hueIndicator.layer.shouldRasterize = YES;
75 | self.hueIndicator.layer.rasterizationScale = UIScreen.mainScreen.scale;
76 | [self addSubview:self.hueIndicator];
77 |
78 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
79 |
80 | [self handleFrameChange];
81 |
82 | return self;
83 | }
84 |
85 | - (void) setFrame:(CGRect)f
86 | {
87 | self.lastPage = self.hueGrid.page;
88 |
89 | super.frame = f;
90 |
91 | [self handleFrameChange];
92 | }
93 |
94 | - (void) updatePage
95 | {
96 | if (self.hueGrid.contentSize.width <= 0.0f || self.hueGrid.contentSize.height <= 0.0f)
97 | {
98 | return;
99 | }
100 |
101 | CGFloat hueSize = self.hueBar.bounds.size.width / DR_COLOR_PICKER_HUE_VIEW_PAGE_COUNT;
102 | CGFloat pages = (self.hueGrid.contentSize.width - self.hueGrid.bounds.size.width);
103 | if (pages < 1.0f)
104 | {
105 | return;
106 | }
107 | CGFloat percent = self.hueGrid.contentOffset.x / pages;
108 | CGFloat trackWidth = self.hueBar.bounds.size.width - hueSize;
109 | CGPoint center = CGPointMake(self.hueBar.frame.origin.x + (hueSize * 0.5f) + (trackWidth * percent), self.hueBar.center.y);
110 | self.hueIndicator.center = center;
111 | }
112 |
113 | - (void) handleFrameChange
114 | {
115 | if (self.hueGrid == nil)
116 | {
117 | return;
118 | }
119 |
120 | NSInteger page = self.lastPage;
121 | CGFloat hueBarPadding = 10.0f;
122 | CGFloat hueBarHeight = 44.0f;
123 | self.hueBar.frame = CGRectMake(hueBarPadding, self.bounds.size.height - hueBarPadding - hueBarHeight, self.bounds.size.width - hueBarPadding - hueBarPadding, hueBarHeight);
124 | self.hueGrid.frame = CGRectMake(0.0f, 0.0f, self.bounds.size.width, self.bounds.size.height - hueBarPadding - hueBarHeight - hueBarPadding);
125 | [self.hueGrid.drCollectionViewLayout calculatePages];
126 | [self createDataSource];
127 |
128 | // restore the page
129 | CGFloat newX = page * self.hueGrid.bounds.size.width;
130 | [self.hueGrid setContentOffset:CGPointMake(newX, 0.0f) animated:NO];
131 |
132 | // do this after the next run loop to allow the hueGrid to lay itself out and get it's content offset sorted out
133 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
134 | {
135 | [self updatePage];
136 | });
137 | }
138 |
139 | - (void) createDataSource
140 | {
141 | if (self.hueGrid.collectionViewLayout == nil)
142 | {
143 | return;
144 | }
145 |
146 | self.hueColors = [NSMutableArray array];
147 |
148 | CGFloat luminosityMultiplier = 1.0f / (CGFloat)(self.hueGrid.drCollectionViewLayout.rows * 2);
149 | CGFloat saturationMultiplier = 1.0f / (CGFloat)(self.hueGrid.drCollectionViewLayout.columns * 2);
150 |
151 | for (NSInteger i = 0 ; i < DR_COLOR_PICKER_HUE_VIEW_PAGE_COUNT; i++)
152 | {
153 | CGFloat hue = i * 30 / 360.0;
154 | for (NSInteger x = 0; x < self.hueGrid.drCollectionViewLayout.itemsPerPage; x++)
155 | {
156 | NSInteger row = x / self.hueGrid.drCollectionViewLayout.columns;
157 | NSInteger column = x % self.hueGrid.drCollectionViewLayout.columns;
158 |
159 | CGFloat saturation = (column * saturationMultiplier) + 0.25f;
160 | CGFloat luminosity = 1.0f - (row * luminosityMultiplier);
161 | UIColor* color = [UIColor colorWithHue:hue saturation:saturation brightness:luminosity alpha:1.0];
162 | [self.hueColors addObject:[[DRColorPickerColor alloc] initWithColor:color]];
163 | }
164 | }
165 |
166 | [self.hueGrid setColors:self.hueColors];
167 | [self updatePage];
168 | }
169 |
170 | - (void) scrollFromColorBarPoint:(CGPoint)p
171 | {
172 | NSInteger page = p.x / (self.hueBar.bounds.size.width / DR_COLOR_PICKER_HUE_VIEW_PAGE_COUNT);
173 | [self.hueGrid scrollRectToVisible:CGRectMake(page * self.hueGrid.bounds.size.width, 0, self.hueGrid.bounds.size.width, self.hueGrid.bounds.size.height) animated:YES];
174 | }
175 |
176 | - (void) hueBarTapped:(UITapGestureRecognizer*)gesture
177 | {
178 | CGPoint point = [gesture locationInView:self.hueBar];
179 | [self scrollFromColorBarPoint:point];
180 | }
181 |
182 | - (void) hueBarPanned:(UIPanGestureRecognizer*)gesture
183 | {
184 | CGPoint point = [gesture locationInView:self.hueBar];
185 | [self scrollFromColorBarPoint:point];
186 | }
187 |
188 | @end
189 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPickerGridView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPickerGridView.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPickerGridView.h"
32 | #import "DRColorPickerGridViewCell.h"
33 | #import "DRColorPickerStore.h"
34 |
35 | @interface DRColorPickerGridView ()
36 |
37 | @property (nonatomic, strong) UILongPressGestureRecognizer* longPressGesture;
38 |
39 | @end
40 |
41 | @implementation DRColorPickerGridView
42 |
43 | - (id) initWithFrame:(CGRect)frame
44 | {
45 | UICollectionViewLayout* layout = [[DRColorPickerHorizontalCollectionViewLayout alloc] init];
46 | return [self initWithFrame:frame collectionViewLayout:layout];
47 | }
48 |
49 | - (id) initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
50 | {
51 | if ((self = [super initWithFrame:frame collectionViewLayout:layout]) == nil) { return nil; }
52 |
53 | self.contentMode = UIViewContentModeCenter;
54 | self.delegate = self;
55 | self.dataSource = self;
56 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
57 | self.backgroundColor = [UIColor clearColor];
58 | self.showsHorizontalScrollIndicator = self.showsVerticalScrollIndicator = NO;
59 | self.pagingEnabled = YES;
60 | self.clipsToBounds = NO;
61 | self.longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressed:)];
62 | self.longPressGesture.delaysTouchesBegan = YES;
63 | [self addGestureRecognizer:self.longPressGesture];
64 | [self registerClass:[DRColorPickerGridViewCell class] forCellWithReuseIdentifier:@"DRColorPickerGridViewCell"];
65 |
66 | return self;
67 | }
68 |
69 | - (NSInteger) itemsInSize:(CGFloat)size itemSize:(CGFloat)itemSize padding:(CGFloat)padding totalSize:(CGFloat*)totalSize
70 | {
71 | NSInteger count = 0;
72 |
73 | while (YES)
74 | {
75 | CGFloat _totalSize = (padding * count) + (itemSize * (count + 1));
76 | if (_totalSize > size)
77 | {
78 | break;
79 | }
80 | if (totalSize != NULL)
81 | {
82 | *totalSize = _totalSize;
83 | }
84 | count++;
85 | }
86 |
87 | return count;
88 | }
89 |
90 | - (void) setColors:(NSArray *)colors
91 | {
92 | _colors = colors;
93 |
94 | if (self.highlightColor.rgbColor != nil)
95 | {
96 | NSInteger index = 0;
97 | for (DRColorPickerColor* color in colors)
98 | {
99 | if ([self.highlightColor.rgbColor isEqual:color.rgbColor])
100 | {
101 | self.highlightColor = color;
102 | // scroll to the highlighted cell
103 | dispatch_async(dispatch_get_main_queue(), ^
104 | {
105 | NSInteger page = index / self.drCollectionViewLayout.itemsPerPage;
106 | [self setContentOffset:CGPointMake(self.bounds.size.width * page, 0.0f) animated:NO];
107 | });
108 | break;
109 | }
110 | index++;
111 | }
112 | }
113 |
114 | [self reloadData];
115 | }
116 |
117 | - (NSInteger) numberOfSectionsInCollectionView:(UICollectionView*)collectionView
118 | {
119 | [self.collectionViewLayout invalidateLayout];
120 |
121 | return 1;
122 | }
123 |
124 | - (NSInteger) collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section
125 | {
126 | return self.colors.count;
127 | }
128 |
129 | - (UICollectionViewCell*) collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath
130 | {
131 | DRColorPickerColor* color = nil;
132 |
133 | if (self.colors.count != 0)
134 | {
135 | if (indexPath.row >= 0 && indexPath.row < self.colors.count)
136 | {
137 | color = (DRColorPickerColor*)self.colors[indexPath.row];
138 | }
139 |
140 | if (color != nil)
141 | {
142 | @try
143 | {
144 | DRColorPickerGridViewCell* cell = [self dequeueReusableCellWithReuseIdentifier:@"DRColorPickerGridViewCell" forIndexPath:indexPath];
145 | cell.colorView.color = color;
146 | cell.colorView.highlighted = (self.highlightColor != nil && color == self.highlightColor);
147 | return cell;
148 | }
149 | @catch (...)
150 | {
151 |
152 | }
153 | }
154 | }
155 |
156 | return [[UICollectionViewCell alloc] init];
157 | }
158 |
159 | - (void) collectionView:(UICollectionView*)collectionView didSelectItemAtIndexPath:(NSIndexPath*)indexPath
160 | {
161 | if (self.colorSelectedBlock != nil && indexPath.row >= 0 && indexPath.row < self.colors.count)
162 | {
163 | DRColorPickerColor* color = (DRColorPickerColor*)self.colors[indexPath.row];
164 | self.colorSelectedBlock(color);
165 | }
166 | }
167 |
168 | - (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
169 | {
170 | _page = self.contentOffset.x / self.bounds.size.width;
171 | if (self.scrolledBlock != nil)
172 | {
173 | self.scrolledBlock();
174 | }
175 | }
176 |
177 | - (void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
178 | {
179 | _page = self.contentOffset.x / self.bounds.size.width;
180 | if (self.scrolledBlock != nil)
181 | {
182 | self.scrolledBlock();
183 | }
184 | }
185 |
186 | - (void) scrollViewDidScroll:(UIScrollView *)scrollView
187 | {
188 | _page = self.contentOffset.x / self.bounds.size.width;
189 | if (self.scrolledBlock != nil)
190 | {
191 | self.scrolledBlock();
192 | }
193 | }
194 |
195 | - (DRColorPickerHorizontalCollectionViewLayout*) drCollectionViewLayout
196 | {
197 | return (DRColorPickerHorizontalCollectionViewLayout*)self.collectionViewLayout;
198 | }
199 |
200 | - (void) longPressed:(id)sender
201 | {
202 | if (self.longPressGesture.state != UIGestureRecognizerStateBegan || self.colorLongPressBlock == nil)
203 | {
204 | return;
205 | }
206 |
207 | CGPoint p = [self.longPressGesture locationInView:self];
208 | NSIndexPath* indexPath = [self indexPathForItemAtPoint:p];
209 | if (indexPath != nil)
210 | {
211 | DRColorPickerGridViewCell* cell = (DRColorPickerGridViewCell*)[self cellForItemAtIndexPath:indexPath];
212 | self.colorLongPressBlock(cell.colorView, indexPath);
213 | }
214 | }
215 |
216 | @end
217 |
--------------------------------------------------------------------------------
/Pod/Classes/DRColorPicker+UIColor.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRColorPicker+UIColor.m
3 | //
4 | // Created by Jeff on 8/10/14.
5 | // Copyright (c) 2014 Digital Ruby, LLC. All rights reserved.
6 | //
7 | /*
8 | The MIT License (MIT)
9 |
10 | Copyright (c) 2014 Digital Ruby, LLC
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in
20 | all copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 | THE SOFTWARE.
29 | */
30 |
31 | #import "DRColorPicker+UIColor.h"
32 |
33 | @implementation UIColor (DRColorPicker)
34 |
35 | - (NSString*) hexStringFromColor
36 | {
37 | return [NSString stringWithFormat:@"%0.8X", (unsigned int)self.rgbaHex];
38 | }
39 |
40 | - (NSString*) hexStringFromColorAlphaOne
41 | {
42 | return [NSString stringWithFormat:@"%0.6XFF", (unsigned int)self.rgbHex];
43 | }
44 |
45 | - (NSString*) hexStringFromColorNoAlpha
46 | {
47 | return [NSString stringWithFormat:@"%0.6X", (unsigned int)self.rgbHex];
48 | }
49 |
50 | + (UIColor*) colorWithRGBHex:(UInt32)hex
51 | {
52 | CGFloat r = (hex >> 16) & 0xFF;
53 | CGFloat g = (hex >> 8) & 0xFF;
54 | CGFloat b = (hex) & 0xFF;
55 |
56 | return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:1.0f];
57 | }
58 |
59 | - (NSString *)stringFromColor
60 | {
61 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -stringFromColor");
62 | NSString *result;
63 | switch (self.colorSpaceModel)
64 | {
65 | case kCGColorSpaceModelRGB:
66 | result = [NSString stringWithFormat:@"{%0.3f, %0.3f, %0.3f, %0.3f}", self.red, self.green, self.blue, self.alpha];
67 | break;
68 | case kCGColorSpaceModelMonochrome:
69 | result = [NSString stringWithFormat:@"{%0.3f, %0.3f}", self.white, self.alpha];
70 | break;
71 | default:
72 | result = nil;
73 | }
74 | return result;
75 | }
76 |
77 | + (UIColor*) colorWithString:(NSString *)stringToConvert
78 | {
79 | NSScanner *scanner = [NSScanner scannerWithString:stringToConvert];
80 | if (![scanner scanString:@"{" intoString:NULL])
81 | {
82 | return nil;
83 | }
84 | const NSUInteger kMaxComponents = 4;
85 | float c[kMaxComponents];
86 | NSUInteger i = 0;
87 | if (![scanner scanFloat:&c[i++]])
88 | {
89 | return nil;
90 | }
91 |
92 | while (1) {
93 | if ([scanner scanString:@"}" intoString:NULL])
94 | {
95 | break;
96 | }
97 | else if (i >= kMaxComponents)
98 | {
99 | return nil;
100 | }
101 | else if ([scanner scanString:@"," intoString:NULL])
102 | {
103 | if (![scanner scanFloat:&c[i++]]) return nil;
104 | }
105 | else
106 | {
107 | return nil;
108 | }
109 | }
110 | if (![scanner isAtEnd])
111 | {
112 | return nil;
113 | }
114 |
115 | UIColor *color;
116 | switch (i)
117 | {
118 | case 2: // monochrome
119 | color = [UIColor colorWithWhite:c[0] alpha:c[1]];
120 | break;
121 | case 4: // RGB
122 | color = [UIColor colorWithRed:c[0] green:c[1] blue:c[2] alpha:c[3]];
123 | break;
124 | default:
125 | color = nil;
126 | }
127 | return color;
128 | }
129 |
130 | + (UIColor*) colorWithRGBAHex:(UInt32)hex
131 | {
132 | CGFloat r = (hex >> 24) & 0xFF;
133 | CGFloat g = (hex >> 16) & 0xFF;
134 | CGFloat b = (hex >> 8) & 0xFF;
135 | CGFloat a = (hex) & 0xFF;
136 |
137 | return [UIColor colorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
138 | }
139 |
140 | // Returns a UIColor by scanning the string for a hex number and passing that to +[UIColor colorWithRGBHex:]
141 | // Skips any leading whitespace and ignores any trailing characters
142 | + (UIColor*) colorWithHexString:(NSString*)stringToConvert
143 | {
144 | NSScanner* scanner = [NSScanner scannerWithString:stringToConvert];
145 | unsigned hexNum;
146 | if (![scanner scanHexInt:&hexNum])
147 | {
148 | return nil;
149 | }
150 |
151 | return (stringToConvert.length == 6 ? [UIColor colorWithRGBHex:hexNum] : [UIColor colorWithRGBAHex:hexNum]);
152 | }
153 |
154 | - (CGFloat) alpha
155 | {
156 | return CGColorGetAlpha(self.CGColor);
157 | }
158 |
159 | - (UInt32) rgbHex
160 | {
161 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbHex");
162 |
163 | CGFloat r,g,b,a;
164 | if (![self red:&r green:&g blue:&b alpha:&a])
165 | {
166 | return 0;
167 | }
168 |
169 | r = MIN(MAX(self.red, 0.0f), 1.0f);
170 | g = MIN(MAX(self.green, 0.0f), 1.0f);
171 | b = MIN(MAX(self.blue, 0.0f), 1.0f);
172 |
173 | return (((UInt32)roundf(r * 255)) << 16) | (((UInt32)roundf(g * 255)) << 8) | (((UInt32)roundf(b * 255)));
174 | }
175 |
176 | - (UInt32) rgbaHex
177 | {
178 | NSAssert(self.canProvideRGBComponents, @"Must be a RGB color to use rgbaHex");
179 |
180 | CGFloat r,g,b,a;
181 | if (![self red:&r green:&g blue:&b alpha:&a])
182 | {
183 | return 0;
184 | }
185 |
186 | r = MIN(MAX(self.red, 0.0f), 1.0f);
187 | g = MIN(MAX(self.green, 0.0f), 1.0f);
188 | b = MIN(MAX(self.blue, 0.0f), 1.0f);
189 | a = MIN(MAX(self.alpha, 0.0f), 1.0f);
190 |
191 | return (((UInt32)roundf(r * 255)) << 24) | (((UInt32)roundf(g * 255)) << 16) | (((UInt32)roundf(b * 255)) << 8) | (((UInt32)roundf(a * 255)));
192 | }
193 |
194 | - (BOOL) red:(CGFloat *)red green:(CGFloat *)green blue:(CGFloat *)blue alpha:(CGFloat *)alpha
195 | {
196 | const CGFloat* components = CGColorGetComponents(self.CGColor);
197 |
198 | CGFloat r,g,b,a;
199 |
200 | switch (self.colorSpaceModel)
201 | {
202 | case kCGColorSpaceModelMonochrome:
203 | r = g = b = components[0];
204 | a = components[1];
205 | break;
206 | case kCGColorSpaceModelRGB:
207 | r = components[0];
208 | g = components[1];
209 | b = components[2];
210 | a = components[3];
211 | break;
212 | default: // We don't know how to handle this model
213 | return NO;
214 | }
215 |
216 | if (red) *red = r;
217 | if (green) *green = g;
218 | if (blue) *blue = b;
219 | if (alpha) *alpha = a;
220 |
221 | return YES;
222 | }
223 |
224 | - (CGFloat) red
225 | {
226 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -red");
227 | const CGFloat* c = CGColorGetComponents(self.CGColor);
228 | return c[0];
229 | }
230 |
231 | - (CGFloat) green
232 | {
233 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -green");
234 | const CGFloat* c = CGColorGetComponents(self.CGColor);
235 | if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0];
236 | return c[1];
237 | }
238 |
239 | - (CGFloat) blue
240 | {
241 | NSAssert(self.canProvideRGBComponents, @"Must be an RGB color to use -blue");
242 | const CGFloat* c = CGColorGetComponents(self.CGColor);
243 | if (self.colorSpaceModel == kCGColorSpaceModelMonochrome) return c[0];
244 | return c[2];
245 | }
246 |
247 | - (CGFloat) white
248 | {
249 | NSAssert(self.colorSpaceModel == kCGColorSpaceModelMonochrome, @"Must be a Monochrome color to use -white");
250 | const CGFloat* c = CGColorGetComponents(self.CGColor);
251 | return c[0];
252 | }
253 |
254 | - (BOOL) canProvideRGBComponents
255 | {
256 | switch (self.colorSpaceModel)
257 | {
258 | case kCGColorSpaceModelRGB:
259 | case kCGColorSpaceModelMonochrome:
260 | return YES;
261 | default:
262 | return NO;
263 | }
264 | }
265 |
266 | - (CGColorSpaceModel) colorSpaceModel
267 | {
268 | return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor));
269 | }
270 |
271 | @end
272 |
--------------------------------------------------------------------------------