├── .gitignore ├── CSColorPicker Resources ├── CSColorPicker │ ├── CSColorPicker.h │ ├── NSString+CSColorPicker.h │ └── UIColor+CSColorPicker.h ├── CSColorPicker_ReadMe.md └── libCSColorPicker.dylib ├── LICENSE ├── Makefile ├── README.md ├── control ├── deprecated ├── CSColorPicker.h ├── CSColorPicker.mm ├── UIColor+CSColorPicker.h └── UIColor+CSColorPicker.m └── source ├── CSColorPicker.h ├── Categories ├── NSString+CSColorPicker.h ├── NSString+CSColorPicker.m ├── UIColor+CSColorPicker.h └── UIColor+CSColorPicker.m ├── Cells ├── CSBaseDisplayCell.h ├── CSBaseDisplayCell.m ├── CSColorDisplayCell.h ├── CSColorDisplayCell.m ├── CSGradientDisplayCell.h └── CSGradientDisplayCell.m ├── Controllers ├── CSColorPickerViewController.h └── CSColorPickerViewController.m ├── Controls ├── CSColorSlider.h └── CSColorSlider.m ├── Headers ├── PSSpecifier.h ├── PSTableCell.h └── PSViewController.h ├── Prefix.h └── Views ├── CSColorPickerBackgroundView.h ├── CSColorPickerBackgroundView.m ├── CSGradientSelection.h └── CSGradientSelection.m /.gitignore: -------------------------------------------------------------------------------- 1 | so 2 | *.jar 3 | *.rar 4 | *.tar 5 | *.zip 6 | *.deb 7 | 8 | *.com 9 | *.class 10 | *.dll 11 | *.exe 12 | *.o 13 | *.so 14 | 15 | .DS_Store 16 | .DS_Store? 17 | ._* 18 | .Spotlight-V100 19 | .Trashes 20 | ehthumbs.db 21 | Thumbs.db 22 | 23 | obj/ 24 | _/ 25 | packages/ 26 | .theos/ 27 | -------------------------------------------------------------------------------- /CSColorPicker Resources/CSColorPicker/CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Dana Buehre on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import "Categories/UIColor+CSColorPicker.h" 7 | #import "Categories/NSString+CSColorPicker.h" 8 | -------------------------------------------------------------------------------- /CSColorPicker Resources/CSColorPicker/NSString+CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 7/28/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | #import 6 | 7 | @class UIColor; 8 | 9 | // 10 | // api v1 11 | // 12 | @interface NSString (CSColorPicker) 13 | 14 | // 15 | // same as csp_colorFromHexString: returning a dynamic color 16 | // if the hex string is not dynamic, this will fallback to cscp_colorFromHexString: 17 | // supported formats include 'l:RGB d:RGB', 'l:ARGB d:ARGB', 'l:RRGGBB d:RRGGBB', 'l:AARRGGBB d:AARRGGBB', 'l:RGB:0.500000 d:RGB:0.500000', 'l:RRGGBB:0.500000 d:RRGGBB:0.500000' 18 | // all formats work with or without # 19 | // 20 | + (UIColor *)cscp_dynamicColorFromHexString:(NSString *)hexString; 21 | 22 | // 23 | // returns a UIColor from the hex string eg [UIColor cscp_colorFromHexString:@"#FF0000"]; 24 | // if the hex string is invalid, returns red 25 | // supported formats include 'RGB', 'ARGB', 'RRGGBB', 'AARRGGBB', 'RGB:0.500000', 'RRGGBB:0.500000' 26 | // all formats work with or without # 27 | // 28 | + (UIColor *)cscp_colorFromHexString:(NSString *)hexString; 29 | 30 | // 31 | // returns true if the string is a valid hex (will pass with or without #) 32 | + (BOOL)cscp_isValidHexString:(NSString *)hexString; 33 | 34 | // 35 | // returns a string dynamic hex string representation of the string instance 36 | // 37 | - (UIColor *)cscp_dynamicHexColor; 38 | 39 | // 40 | // returns a string hex string representation of the string instance 41 | // 42 | - (UIColor *)cscp_hexColor; 43 | 44 | // 45 | // returns true if the string instance is a valid hex value 46 | // 47 | - (BOOL)cscp_validHex; 48 | 49 | // 50 | // returns an array of UIColors from a gradient hex array 51 | // eg: @"FF0000,00FF00,0000FF", or @"FF0000:0.500000,00FF00:0.500000,0000FF:0.500000" or @"FFFF0000,FF00FF00,FF0000FF" 52 | // 53 | - (NSArray *)cscp_gradientStringColors; 54 | 55 | // 56 | // returns an array of CGColors for setting CAGradientLayer.colors property 57 | // same usage as gradientStringColors 58 | // 59 | - (NSArray *)cscp_gradientStringCGColors; 60 | 61 | // 62 | // legacy api methods deprecated 63 | // 64 | 65 | + (UIColor *)colorFromHexString:(NSString *)hexString 66 | __attribute__((deprecated("WARNING: (colorFromHexString:) has been deprecated, use cscp_colorFromHexString instead."))); 67 | 68 | + (BOOL)isValidHexString:(NSString *)hexString 69 | __attribute__((deprecated("WARNING: (isValidHexString:) has been deprecated, use cscp_isValidHexString: instead."))); 70 | 71 | - (UIColor *)hexColor 72 | __attribute__((deprecated("WARNING: (hexColor) has been deprecated, use cscp_hexColor instead."))); 73 | 74 | - (BOOL)validHex 75 | __attribute__((deprecated("WARNING: (validHex) has been deprecated, use cscp_validHex instead."))); 76 | 77 | - (NSArray *)gradientStringColors 78 | __attribute__((deprecated("WARNING: (gradientStringColors) has been deprecated, use cscp_gradientStringColors instead."))); 79 | 80 | - (NSArray *)gradientStringCGColors 81 | __attribute__((deprecated("WARNING: (gradientStringCGColors) has been deprecated, use cscp_gradientStringCGColors instead."))); 82 | 83 | @end -------------------------------------------------------------------------------- /CSColorPicker Resources/CSColorPicker/UIColor+CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | #import 6 | 7 | @interface UIColor (CSColorPicker) 8 | 9 | // 10 | // returns a UIColor from the hex string eg [UIColor colorFromHexString:@"#FF0000"]; 11 | // if the hex string is invalid, returns red 12 | // supported formats include 'RGB', 'ARGB', 'RRGGBB', 'AARRGGBB', 'RGB:0.500000', 'RRGGBB:0.500000' 13 | // all formats work with or without # 14 | // 15 | + (UIColor *)cscp_colorFromHexString:(NSString *)hexString; 16 | 17 | // 18 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_dynamicHexStringFromColor:[UIColor redColor]]; outputs @"l:#FF0000 d:#FF0000" 19 | // 20 | + (NSString *)cscp_dynamicHexStringFromColor: (UIColor *)color; 21 | 22 | // 23 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_dynamicHexStringFromColor:[UIColor redColor] alpha:YES]; outputs @"#l:FFFF0000 d:FFFF0000" 24 | // 25 | + (NSString *)cscp_dynamicHexStringFromColor: (UIColor *)color alpha:(BOOL)include; 26 | 27 | // 28 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_hexStringFromColor:[UIColor redColor]]; outputs @"#FF0000" 29 | // 30 | + (NSString *)cscp_hexStringFromColor:(UIColor *)color; 31 | 32 | // 33 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_hexStringFromColor:[UIColor redColor] alpha:YES]; outputs @"#FFFF0000" 34 | // 35 | + (NSString *)cscp_hexStringFromColor:(UIColor *)color alpha:(BOOL)include; 36 | 37 | // 38 | // returns the brightness of the color where black = 0.0 and white = 256.0 39 | // credit goes to https://w3.org for the algorithm 40 | // 41 | + (CGFloat)cscp_brightnessOfColor:(UIColor *)color; 42 | 43 | // 44 | // returns true if the color is light using csp_brightnessOfColor > 0.5 45 | // 46 | + (BOOL)cscp_isColorLight:(UIColor *)color; 47 | 48 | // 49 | // returns true if the string is a valid hex (will pass with or without #) 50 | // 51 | + (BOOL)cscp_isValidHexString:(NSString *)hexString; 52 | 53 | // 54 | // the alpha component the color instance 55 | // 56 | - (CGFloat)cscp_alpha; 57 | 58 | // 59 | // the red component the color instance 60 | // 61 | - (CGFloat)cscp_red; 62 | 63 | // 64 | // the green component the color instance 65 | // 66 | - (CGFloat)cscp_green; 67 | 68 | // 69 | // the blue component the color instance 70 | // 71 | - (CGFloat)cscp_blue; 72 | 73 | // 74 | // the hue component the color instance 75 | // 76 | - (CGFloat)cscp_hue; 77 | 78 | // 79 | // the saturation component the color instance 80 | // 81 | - (CGFloat)cscp_saturation; 82 | 83 | // 84 | // the brightness component the color instance 85 | // 86 | - (CGFloat)cscp_brightness; 87 | 88 | // 89 | // the dynamic hexString value of the color instance 90 | // 91 | - (NSString *)cscp_dynamicHexString; 92 | 93 | // 94 | // the dynamic hexString value of the color instance with alpha included 95 | // 96 | - (NSString *)cscp_dynamicHexStringWithAlpha; 97 | 98 | // 99 | // the hexString value of the color instance 100 | // 101 | - (NSString *)cscp_hexString; 102 | 103 | // 104 | // the hexString value of the color instance with alpha included 105 | // 106 | - (NSString *)cscp_hexStringWithAlpha; 107 | 108 | // 109 | // is this color instance light 110 | // 111 | - (BOOL)cscp_light; 112 | 113 | // 114 | // is this color instance dark 115 | // 116 | - (BOOL)cscp_dark; 117 | 118 | // 119 | // legacy api methods deprecated 120 | // 121 | 122 | + (UIColor *)colorFromHexString:(NSString *)hexString 123 | __attribute__((deprecated("WARNING: (colorFromHexString:) has been deprecated, use cscp_colorFromHexString instead."))); 124 | 125 | + (NSString *)hexStringFromColor:(UIColor *)color 126 | __attribute__((deprecated("WARNING: (hexStringFromColor:) has been deprecated, use cscp_hexStringFromColor instead."))); 127 | 128 | + (NSString *)hexStringFromColor:(UIColor *)color alpha:(BOOL)include 129 | __attribute__((deprecated("WARNING: (hexStringFromColor:alpha:) has been deprecated, use cscp_hexStringFromColor:alpha: instead."))); 130 | 131 | + (CGFloat)brightnessOfColor:(UIColor *)color 132 | __attribute__((deprecated("WARNING: (brightnessOfColor:alpha:) has been deprecated, use cscp_brightnessOfColor instead."))); 133 | 134 | + (BOOL)isColorLight:(UIColor *)color 135 | __attribute__((deprecated("WARNING: (isColorLight:) has been deprecated, use cscp_isColorLight instead."))); 136 | 137 | + (BOOL)isValidHexString:(NSString *)hexString 138 | __attribute__((deprecated("WARNING: (isValidHexString:) has been deprecated, use cscp_isValidHexString instead."))); 139 | 140 | - (CGFloat)alpha 141 | __attribute__((deprecated("WARNING: (alpha) has been deprecated, use cscp_alpha instead."))); 142 | 143 | - (CGFloat)red 144 | __attribute__((deprecated("WARNING: (red) has been deprecated, use cscp_red instead."))); 145 | 146 | - (CGFloat)green 147 | __attribute__((deprecated("WARNING: (green) has been deprecated, use cscp_green instead."))); 148 | 149 | - (CGFloat)blue 150 | __attribute__((deprecated("WARNING: (blue) has been deprecated, use cscp_blue instead."))); 151 | 152 | - (CGFloat)hue 153 | __attribute__((deprecated("WARNING: (hue) has been deprecated, use cscp_hue instead."))); 154 | 155 | - (CGFloat)saturation 156 | __attribute__((deprecated("WARNING: (saturation) has been deprecated, use cscp_saturation instead."))); 157 | 158 | - (CGFloat)brightness 159 | __attribute__((deprecated("WARNING: (brightness) has been deprecated, use cscp_brightness instead."))); 160 | 161 | - (NSString *)hexString 162 | __attribute__((deprecated("WARNING: (hexString) has been deprecated, use cscp_hexString instead."))); 163 | 164 | - (NSString *)hexStringWithAlpha 165 | __attribute__((deprecated("WARNING: (hexStringWithAlpha) has been deprecated, use cscp_hexStringWithAlpha instead."))); 166 | 167 | - (BOOL)light 168 | __attribute__((deprecated("WARNING: (light) has been deprecated, use cscp_light instead."))); 169 | 170 | - (BOOL)dark 171 | __attribute__((deprecated("WARNING: (dark) has been deprecated, use cscp_dark instead."))); 172 | 173 | @end 174 | -------------------------------------------------------------------------------- /CSColorPicker Resources/CSColorPicker_ReadMe.md: -------------------------------------------------------------------------------- 1 | ## v1.0 API changes 2 | 3 | libCSColorPicker v1.0 has been released. in this release the API has been reworked to resolve conflicts with other projects, the legacy API has been officially marked depreciated and should not be used in new/updated projects. Legacy API will remain operational for now to ensure no projects are broken with this update. **Please Download the new [project resources](https://github.com/CreatureSurvive/libCSColorPicker/releases/latest) for theos and update your projects.** 4 | 5 | # libCSColorPicker 6 | 7 | A simplistic yet powerful color picker for use in PreferenceLoader preference bundles. Supports iOS 9 - 11, other iOS versions have not been tested. 8 | 9 | Best when used with [libCSPreference](https://creaturesurvive.github.io/repo/cydia/libcspreferences/depiction/) but also works fine on it's own. 10 | 11 | ### Install Instructions 12 | 13 | * Place CSColorPicker folder in the vendor/include directory of your Theos installation. 14 | * Place libCSColorPicker.dylib in the /lib directory of your Theos installation. 15 | * Thats it. 16 | 17 | 18 | 19 | ### Usage Instructions 20 | 21 | * Lets start with the Makefile and get libCSColorPicker properly linked to your project, to do so you can simply add this to your Makefile `MyAwesomeTweak_LDFLAGS += -lCSColorPicker`. Here is an example makefile for a project using libCSColorPicker: 22 | 23 | ```makefile 24 | ARCHS = arm64 25 | TARGET = iphone:clang:11.2:latest 26 | 27 | GO_EASY_ON_ME = 1 28 | FINALPACKAGE = 1 29 | DEBUG = 0 30 | 31 | include $(THEOS)/makefiles/common.mk 32 | 33 | TWEAK_NAME = MyAwesomeTweak 34 | $(TWEAK_NAME)_FILES = Tweak.xm 35 | $(TWEAK_NAME)_CLAGS += -fobjc-arc 36 | $(TWEAK_NAME)_LDFLAGS += -lCSColorPicker 37 | 38 | include $(THEOS_MAKE_PATH)/tweak.mk 39 | SUBPROJECTS += preferences 40 | include $(THEOS_MAKE_PATH)/aggregate.mk 41 | 42 | after-install:: 43 | install.exec "killall -9 SpringBoard" 44 | ``` 45 | 46 | 47 | 48 | Now lets take a look at the usage within your your Tweak.xm 49 | 50 | ```objective-c 51 | #include 52 | #define PLIST_PATH @"/User/Library/Preferences/com.creaturecoding.MyAwesomeTweak.plist" 53 | 54 | // lets set up a simple means of fetching values from out preferences 55 | // NOTE: this is just an example, you should cache the preference dictionary 56 | // if you plan to use this in a tweak 57 | inline NSString *StringForPreferenceKey(NSString *key) { 58 | NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:PLIST_PATH] ? : [NSDictionary new]; 59 | return prefs[key]; 60 | } 61 | 62 | %hook MyAwesomeView 63 | 64 | // lets change the background color of MyAwesomeView using the color set in our preferences 65 | - (void)setBackgroundColor:(UIColor *)color { 66 | // libCSColorPicker extends the UIColor class so that usage is familiar 67 | // you can also use the C method cscp_hexStringFromColor(UIColor *color); 68 | color = [UIColor cscp_colorFromHexString:StringForPreferenceKey(@"k_myAwesomeBackgroundColor)]; 69 | 70 | // lets see what else we can do with libCSColorPicker 71 | // outputs FF0000 assuming out color is red 72 | NSString *hex = [UIColor cscp_hexStringFromColor:color]; 73 | NSLog(hex); 74 | 75 | // and now with the alpha channel included 76 | // outputs FFFF0000 assuming out color is red with an alpha value of 1 77 | // supported formats include 'RGB', 'ARGB', 'RRGGBB', 'AARRGGBB', 'RGB:0.25', 'RRGGBB:0.25' 78 | NSString *hex = [UIColor cscp_hexStringFromColor:color alpha:YES]; 79 | NSLog(hex); 80 | 81 | // we can also validate our hex string if we need 82 | BOOL valid = [UIColor cscp_isValidHexString:@"FFFFFF"]; 83 | } 84 | 85 | - (void)setGradientView { 86 | // get an array of CGColors from a preference value, the value is a comma separated string of hex colors eg" FFFFFF,000000,111111 87 | NSArray *gradientColors = [StringForPreferenceKey(@"k_myAwesomeBackgroundGradient) cscp_gradientStringCGColors]; 88 | 89 | CAGradientLayer *gradient = [CAGradientLayer layer]; 90 | gradient.frame = self.view.bounds; 91 | 92 | // left to right gradient 93 | gradient.startPoint = CGPointMake(0, 0.5); 94 | gradient.endPoint = CGPointMake(1, 0.5); 95 | 96 | // set the gradient colors 97 | gradient.colors = gradientColors; 98 | 99 | // add the gradient to the view 100 | [self.view addSublayer:gradient]; 101 | 102 | // if upi need an array of UIColors instead of CGColors use: 103 | NSArray ui_colors = [StringForPreferenceKey(@"k_myAwesomeBackgroundGradient) cscp_gradientStringColors]; 104 | } 105 | 106 | %end 107 | ``` 108 | 109 | 110 | 111 | * Now lets look at how we can set this up in our preferences to use the color picker. Once again lets start with the makefile by adding `MyAwesomeTweakPrefs_LDFLAGS += -lCSColorPicker` Here is an example makefile for a preferences using libCSColorPicker: 112 | 113 | ``` 114 | ARCHS = arm64 115 | TARGET = iphone:clang:11.2:latest 116 | 117 | include $(THEOS)/makefiles/common.mk 118 | 119 | BUNDLE_NAME = MyAwesomeTweak 120 | $(BUNDLE_NAME)_FILES = $(wildcard *.m) 121 | $(BUNDLE_NAME)_INSTALL_PATH = /Library/PreferenceBundles 122 | $(BUNDLE_NAME)_FRAMEWORKS = UIKit 123 | $(BUNDLE_NAME)_PRIVATE_FRAMEWORKS = Preferences 124 | $(BUNDLE_NAME)_LDFLAGS += -lCSColorPicker 125 | 126 | include $(THEOS_MAKE_PATH)/bundle.mk 127 | 128 | internal-stage:: 129 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 130 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/MyAwesomeTweak.plist$(ECHO_END) 131 | 132 | 133 | ``` 134 | 135 | 136 | 137 | * Now all thats left is to add it to one of our preferences plist like the Root.plist and set up the specifier. 138 | 139 | ```xml 140 | 141 | 142 | 143 | 144 | title 145 | MyAwesomeTweak 146 | 147 | items 148 | 149 | 150 | 151 | 152 | PostNotification 153 | com.creaturecoding.MyAwesomeTweak.prefsChanged 154 | cell 155 | PSLinkCell 156 | cellClass 157 | CSColorDisplayCell 158 | defaults 159 | com.creaturecoding.MyAwesomeTweak 160 | defaultsPath 161 | /Library/PreferenceBundles/MyAwesomeTweak.bundle 162 | key 163 | k_myAwesomeBackgroundColor 164 | label 165 | My Awesome Background Color 166 | fallback 167 | FFFFFF 168 | alpha 169 | 170 | 171 | 172 | 173 | 174 | PostNotification 175 | com.creaturecoding.MyAwesomeTweak.prefsChanged 176 | cell 177 | PSLinkCell 178 | cellClass 179 | CSGradientDisplayCell 180 | defaults 181 | com.creaturecoding.MyAwesomeTweak 182 | defaultsPath 183 | /Library/PreferenceBundles/MyAwesomeTweak.bundle 184 | key 185 | k_myAwesomeBackgroundGradient 186 | label 187 | My Awesome Background Gradient 188 | fallback 189 | FFFFFF,000000 190 | alpha 191 | 192 | 193 | 194 | 195 | 196 | ``` 197 | 198 | if you want to use defaultsPath instead of fallback to set default colors, then create a plist named `defaults.plist` in your preferences Resource folder and add an entry for each color like so: 199 | 200 | ```xml 201 | 202 | 203 | 204 | 205 | 206 | k_myAwesomeBackgroundColor 207 | FFFFFF 208 | 209 | 210 | k_myAwesomeBackgroundGradient 211 | FFFFFF,000000 212 | 213 | 214 | ``` 215 | 216 | 217 | 218 | Thats it, libCSColorPicker is ready to use in your project. 219 | 220 | ### Specifier configuration 221 | 222 | * 'cell' is required and should to be set to `PSLinkCell` to function properly 223 | * 'cellClass' is required and should be set to `CSColorDisplayCell` or `CSGradientDisplayCell` 224 | * 'defaults' is required and should be the name of your preferences of your preferences plist in `/User/Library/Preferences` 225 | * 'defaultsPath' is optional, if provided it should point to your tweaks preference bundle. this allows you to store a `defaults.plist` in the resource folder of your preferences that will store default color values for your tweak. 226 | * 'fallback' is optional and if you have no value set in your preferences .plist, or defaultsPath, then this value will be used (NOTE: if none of these values are provided the color will default to RED or FF0000) 227 | * 'alpha' is optional and will default to true if not provided, it is responsible for disabling the alpha slider in the color picker, and all colors will have an alpha of 1 -------------------------------------------------------------------------------- /CSColorPicker Resources/libCSColorPicker.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatureSurvive/libCSColorPicker/2af729ca35bae5ad8dcc90cd399dbe1337f7a84e/CSColorPicker Resources/libCSColorPicker.dylib -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 CreatureSurvive 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ARCHS = armv7 armv7s arm64 arm64e 2 | TARGET = iphone:clang:13.0:5.0 3 | 4 | DEBUG = 1 5 | FINALPACKAGE = 1 6 | LEAN_AND_MEAN = 1 7 | GO_EASY_ON_ME = 0 8 | 9 | DEBUG_EXT = 10 | PACKAGE_VERSION = $(THEOS_PACKAGE_BASE_VERSION)$(DEBUG_EXT) 11 | INSTALL_TARGET_PROCESSES = Preferences TweakSettings 12 | 13 | include $(THEOS)/makefiles/common.mk 14 | 15 | INCLUDES = $(THEOS_PROJECT_DIR)/source 16 | 17 | LIBRARY_NAME = libCSColorPicker 18 | $(LIBRARY_NAME)_FILES = $(wildcard source/*.m source/*/*.m) 19 | $(LIBRARY_NAME)_FRAMEWORKS = UIKit CoreGraphics CoreFoundation 20 | $(LIBRARY_NAME)_PRIVATE_FRAMEWORKS = Preferences 21 | $(LIBRARY_NAME)_CFLAGS += -fobjc-arc -I$(INCLUDES) -IPrefix.pch 22 | 23 | include $(THEOS_MAKE_PATH)/library.mk 24 | 25 | after-stage:: 26 | $(ECHO_NOTHING)mkdir -p "$(THEOS_STAGING_DIR)/usr/include/CSColorPicker"$(ECHO_END) 27 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/CSColorPicker.h" "$(THEOS_STAGING_DIR)/usr/include/CSColorPicker/CSColorPicker.h"$(ECHO_END) 28 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/Categories/UIColor+CSColorPicker.h" "$(THEOS_STAGING_DIR)/usr/include/CSColorPicker/UIColor+CSColorPicker.h"$(ECHO_END) 29 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/Categories/NSString+CSColorPicker.h" "$(THEOS_STAGING_DIR)/usr/include/CSColorPicker/NSString+CSColorPicker.h"$(ECHO_END) 30 | 31 | $(ECHO_NOTHING)mkdir -p "$(THEOS_PROJECT_DIR)/CSColorPicker Resources/CSColorPicker"$(ECHO_END) 32 | $(ECHO_NOTHING)cp "$(THEOS_STAGING_DIR)/usr/lib/libCSColorPicker.dylib" "$(THEOS_PROJECT_DIR)/CSColorPicker Resources/libCSColorPicker.dylib"$(ECHO_END) 33 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/CSColorPicker.h" "$(THEOS_PROJECT_DIR)/CSColorPicker Resources/CSColorPicker/CSColorPicker.h"$(ECHO_END) 34 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/Categories/UIColor+CSColorPicker.h" "$(THEOS_PROJECT_DIR)/CSColorPicker Resources/CSColorPicker/UIColor+CSColorPicker.h"$(ECHO_END) 35 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/Categories/NSString+CSColorPicker.h" "$(THEOS_PROJECT_DIR)/CSColorPicker Resources/CSColorPicker/NSString+CSColorPicker.h"$(ECHO_END) 36 | $(ECHO_NOTHING)zip "$(THEOS_PROJECT_DIR)/CSColorPicker Resources.zip" "$(THEOS_PROJECT_DIR)/CSColorPicker Resources"$(ECHO_END) 37 | 38 | $(ECHO_NOTHING)mkdir -p "$(THEOS)/include/CSColorPicker"$(ECHO_END) 39 | $(ECHO_NOTHING)cp "$(THEOS_STAGING_DIR)/usr/lib/libCSColorPicker.dylib" "$(THEOS)/lib/libCSColorPicker.dylib"$(ECHO_END) 40 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/CSColorPicker.h" "$(THEOS)/include/CSColorPicker/CSColorPicker.h"$(ECHO_END) 41 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/Categories/UIColor+CSColorPicker.h" "$(THEOS)/include/CSColorPicker/UIColor+CSColorPicker.h"$(ECHO_END) 42 | $(ECHO_NOTHING)cp "$(THEOS_PROJECT_DIR)/source/Categories/NSString+CSColorPicker.h" "$(THEOS)/include/CSColorPicker/NSString+CSColorPicker.h"$(ECHO_END) 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## v1.0 API changes 2 | 3 | libCSColorPicker v1.0 has been released. in this release the API has been reworked to resolve conflicts with other projects, the legacy API has been officially marked depreciated and should not be used in new/updated projects. Legacy API will remain operational for now to ensure no projects are broken with this update. **Please Download the new [project resources](https://github.com/CreatureSurvive/libCSColorPicker/files/3063116/CSColorPicker.Resources.zip) for theos and update your projects.** 4 | 5 | # libCSColorPicker 6 | 7 | [libCSColorPicker](https://creaturesurvive.github.io/repo/cydia/libcscolorpicker/depiction/) is a simplistic yet powerful color picker for use in PreferenceLoader preference bundles. Supports iOS 9 - 11, other iOS versions have not been tested. 8 | 9 | Best when used with [libCSPreference](https://creaturesurvive.github.io/repo/cydia/libcspreferences/depiction/) but also works fine on it's own. 10 | 11 | ### Install Instructions 12 | 13 | - Place CSColorPicker folder in the vendor/include directory of your Theos installation. 14 | - Place libCSColorPicker.dylib in the /lib directory of your Theos installation. 15 | - Thats it. 16 | 17 | 18 | 19 | ### Usage Instructions 20 | 21 | * Lets start with the Makefile and get libCSColorPicker properly linked to your project, to do so you can simply add this to your Makefile `MyAwesomeTweak_LDFLAGS += -lCSColorPicker`. Here is an example makefile for a project using libCSColorPicker: 22 | 23 | ```makefile 24 | ARCHS = arm64 25 | TARGET = iphone:clang:11.2:latest 26 | 27 | GO_EASY_ON_ME = 1 28 | FINALPACKAGE = 1 29 | DEBUG = 0 30 | 31 | include $(THEOS)/makefiles/common.mk 32 | 33 | TWEAK_NAME = MyAwesomeTweak 34 | $(TWEAK_NAME)_FILES = Tweak.xm 35 | $(TWEAK_NAME)_CFLAGS += -fobjc-arc 36 | $(TWEAK_NAME)_LDFLAGS += -lCSColorPicker 37 | 38 | include $(THEOS_MAKE_PATH)/tweak.mk 39 | SUBPROJECTS += preferences 40 | include $(THEOS_MAKE_PATH)/aggregate.mk 41 | 42 | after-install:: 43 | install.exec "killall -9 SpringBoard" 44 | ``` 45 | 46 | 47 | 48 | Now lets take a look at the usage within your your Tweak.xm 49 | 50 | ```objective-c 51 | #include 52 | #define PLIST_PATH @"/User/Library/Preferences/com.creaturecoding.MyAwesomeTweak.plist" 53 | 54 | // lets set up a simple means of fetching values from out preferences 55 | // NOTE: this is just an example, you should cache the preference dictionary 56 | // if you plan to use this in a tweak 57 | inline NSString *StringForPreferenceKey(NSString *key) { 58 | NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:PLIST_PATH] ? : [NSDictionary new]; 59 | return prefs[key]; 60 | } 61 | 62 | %hook MyAwesomeView 63 | 64 | // lets change the background color of MyAwesomeView using the color set in our preferences 65 | - (void)setBackgroundColor:(UIColor *)color { 66 | // libCSColorPicker extends the UIColor class so that usage is familiar 67 | // you can also use the C method cscp_hexStringFromColor(UIColor *color); 68 | color = [UIColor cscp_colorFromHexString:StringForPreferenceKey(@"k_myAwesomeBackgroundColor)]; 69 | 70 | // lets see what else we can do with libCSColorPicker 71 | // outputs FF0000 assuming out color is red 72 | NSString *hex = [UIColor cscp_hexStringFromColor:color]; 73 | NSLog(hex); 74 | 75 | // and now with the alpha channel included 76 | // outputs FFFF0000 assuming out color is red with an alpha value of 1 77 | // supported formats include 'RGB', 'ARGB', 'RRGGBB', 'AARRGGBB', 'RGB:0.25', 'RRGGBB:0.25' 78 | NSString *hex = [UIColor cscp_hexStringFromColor:color alpha:YES]; 79 | NSLog(hex); 80 | 81 | // we can also validate our hex string if we need 82 | BOOL valid = [UIColor cscp_isValidHexString:@"FFFFFF"]; 83 | } 84 | 85 | - (void)setGradientView { 86 | // get an array of CGColors from a preference value, the value is a comma separated string of hex colors eg" FFFFFF,000000,111111 87 | NSArray *gradientColors = [StringForPreferenceKey(@"k_myAwesomeBackgroundGradient) cscp_gradientStringCGColors]; 88 | 89 | CAGradientLayer *gradient = [CAGradientLayer layer]; 90 | gradient.frame = self.view.bounds; 91 | 92 | // left to right gradient 93 | gradient.startPoint = CGPointMake(0, 0.5); 94 | gradient.endPoint = CGPointMake(1, 0.5); 95 | 96 | // set the gradient colors 97 | gradient.colors = gradientColors; 98 | 99 | // add the gradient to the view 100 | [self.view addSublayer:gradient]; 101 | 102 | // if upi need an array of UIColors instead of CGColors use: 103 | NSArray ui_colors = [StringForPreferenceKey(@"k_myAwesomeBackgroundGradient) cscp_gradientStringColors]; 104 | } 105 | 106 | %end 107 | ``` 108 | 109 | 110 | 111 | * Now lets look at how we can set this up in our preferences to use the color picker. Once again lets start with the makefile by adding `MyAwesomeTweakPrefs_LDFLAGS += -lCSColorPicker` Here is an example makefile for a preferences using libCSColorPicker: 112 | 113 | ``` 114 | ARCHS = arm64 115 | TARGET = iphone:clang:11.2:latest 116 | 117 | include $(THEOS)/makefiles/common.mk 118 | 119 | BUNDLE_NAME = MyAwesomeTweak 120 | $(BUNDLE_NAME)_FILES = $(wildcard *.m) 121 | $(BUNDLE_NAME)_INSTALL_PATH = /Library/PreferenceBundles 122 | $(BUNDLE_NAME)_FRAMEWORKS = UIKit 123 | $(BUNDLE_NAME)_PRIVATE_FRAMEWORKS = Preferences 124 | $(BUNDLE_NAME)_LDFLAGS += -lCSColorPicker 125 | 126 | include $(THEOS_MAKE_PATH)/bundle.mk 127 | 128 | internal-stage:: 129 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END) 130 | $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/MyAwesomeTweak.plist$(ECHO_END) 131 | 132 | 133 | ``` 134 | 135 | 136 | 137 | * Now all thats left is to add it to one of our preferences plist like the Root.plist and set up the specifier. 138 | 139 | ```xml 140 | 141 | 142 | 143 | 144 | title 145 | MyAwesomeTweak 146 | 147 | items 148 | 149 | 150 | 151 | 152 | PostNotification 153 | com.creaturecoding.MyAwesomeTweak.prefsChanged 154 | cell 155 | PSLinkCell 156 | cellClass 157 | CSColorDisplayCell 158 | defaults 159 | com.creaturecoding.MyAwesomeTweak 160 | defaultsPath 161 | /Library/PreferenceBundles/MyAwesomeTweak.bundle 162 | key 163 | k_myAwesomeBackgroundColor 164 | label 165 | My Awesome Background Color 166 | fallback 167 | FFFFFF 168 | alpha 169 | 170 | 171 | 172 | 173 | 174 | PostNotification 175 | com.creaturecoding.MyAwesomeTweak.prefsChanged 176 | cell 177 | PSLinkCell 178 | cellClass 179 | CSGradientDisplayCell 180 | defaults 181 | com.creaturecoding.MyAwesomeTweak 182 | defaultsPath 183 | /Library/PreferenceBundles/MyAwesomeTweak.bundle 184 | key 185 | k_myAwesomeBackgroundGradient 186 | label 187 | My Awesome Background Gradient 188 | fallback 189 | FFFFFF,000000 190 | alpha 191 | 192 | 193 | 194 | 195 | 196 | ``` 197 | 198 | if you want to use defaultsPath instead of fallback to set default colors, then create a plist named `defaults.plist` in your preferences Resource folder and add an entry for each color like so: 199 | 200 | ```xml 201 | 202 | 203 | 204 | 205 | 206 | k_myAwesomeBackgroundColor 207 | FFFFFF 208 | 209 | 210 | k_myAwesomeBackgroundGradient 211 | FFFFFF,000000 212 | 213 | 214 | ``` 215 | 216 | 217 | 218 | Thats it, libCSColorPicker is ready to use in your project. 219 | 220 | ### Specifier configuration 221 | 222 | * 'cell' is required and should to be set to `PSLinkCell` to function properly 223 | * 'cellClass' is required and should be set to `CSColorDisplayCell` or `CSGradientDisplayCell` 224 | * 'defaults' is required and should be the name of your preferences of your preferences plist in `/User/Library/Preferences` 225 | * 'defaultsPath' is optional, if provided it should point to your tweaks preference bundle. this allows you to store a `defaults.plist` in the resource folder of your preferences that will store default color values for your tweak. 226 | * 'fallback' is optional and if you have no value set in your preferences .plist, or defaultsPath, then this value will be used (NOTE: if none of these values are provided the color will default to RED or FF0000) 227 | * 'alpha' is optional and will default to true if not provided, it is responsible for disabling the alpha slider in the color picker, and all colors will have an alpha of 1 228 | 229 | 230 | ## Credits and Acknowledgments 231 | 232 | Developed and Maintained by [CreatureSurvive](https://creaturecoding.com/) (Dana Buehre) 233 | 234 | ## License 235 | 236 | - This software is licensed under the MIT License, detailed in the [LICENSE](https://github.com/CreatureSurvive/libCSColorPicker/tree/master/LICENCE) file 237 | - __Please don't steel my code!__ if you like to use it, then go ahead. Just be sure to provide proper credit. 238 | 239 | ## Submit Bugs & or Fixes 240 | 241 | https://github.com/CreatureSurvive/libCSColorPicker/issues -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.creaturesurvive.libcscolorpicker 2 | Name: libCSColorPicker 3 | Pre-Depends: firmware (>= 7.0) 4 | Version: 1.0.6 5 | Architecture: iphoneos-arm 6 | Description: A minimal color picker library for developers 7 | Maintainer: CreatureSurvive 8 | Author: CreatureSurvive 9 | Section: System 10 | Homepage: https://creaturecoding.com/ 11 | Depiction: https://creaturecoding.com/?page=depiction&id=libcscolorpicker 12 | SileoDepiction: https://creaturecoding.com/sileo/depiction/?package_id=libcscolorpicker 13 | Tag: purpose::extension, compatible_min::iosiOS7 14 | 15 | -------------------------------------------------------------------------------- /deprecated/CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | #import 10 | #import 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | UIColor *colorFromHexString(NSString *hexString); 17 | NSString *hexStringFromColor(UIColor *color); 18 | BOOL isValidHexString(NSString *hexString); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /deprecated/CSColorPicker.mm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatureSurvive/libCSColorPicker/2af729ca35bae5ad8dcc90cd399dbe1337f7a84e/deprecated/CSColorPicker.mm -------------------------------------------------------------------------------- /deprecated/UIColor+CSColorPicker.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatureSurvive/libCSColorPicker/2af729ca35bae5ad8dcc90cd399dbe1337f7a84e/deprecated/UIColor+CSColorPicker.h -------------------------------------------------------------------------------- /deprecated/UIColor+CSColorPicker.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatureSurvive/libCSColorPicker/2af729ca35bae5ad8dcc90cd399dbe1337f7a84e/deprecated/UIColor+CSColorPicker.m -------------------------------------------------------------------------------- /source/CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Dana Buehre on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import "Categories/UIColor+CSColorPicker.h" 7 | #import "Categories/NSString+CSColorPicker.h" 8 | -------------------------------------------------------------------------------- /source/Categories/NSString+CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 7/28/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | #import 6 | 7 | @class UIColor; 8 | 9 | // 10 | // api v1 11 | // 12 | @interface NSString (CSColorPicker) 13 | 14 | // 15 | // same as csp_colorFromHexString: returning a dynamic color 16 | // if the hex string is not dynamic, this will fallback to cscp_colorFromHexString: 17 | // supported formats include 'l:RGB d:RGB', 'l:ARGB d:ARGB', 'l:RRGGBB d:RRGGBB', 'l:AARRGGBB d:AARRGGBB', 'l:RGB:0.500000 d:RGB:0.500000', 'l:RRGGBB:0.500000 d:RRGGBB:0.500000' 18 | // all formats work with or without # 19 | // 20 | + (UIColor *)cscp_dynamicColorFromHexString:(NSString *)hexString; 21 | 22 | // 23 | // returns a UIColor from the hex string eg [UIColor cscp_colorFromHexString:@"#FF0000"]; 24 | // if the hex string is invalid, returns red 25 | // supported formats include 'RGB', 'ARGB', 'RRGGBB', 'AARRGGBB', 'RGB:0.500000', 'RRGGBB:0.500000' 26 | // all formats work with or without # 27 | // 28 | + (UIColor *)cscp_colorFromHexString:(NSString *)hexString; 29 | 30 | // 31 | // returns true if the string is a valid hex (will pass with or without #) 32 | + (BOOL)cscp_isValidHexString:(NSString *)hexString; 33 | 34 | // 35 | // returns a string dynamic hex string representation of the string instance 36 | // 37 | - (UIColor *)cscp_dynamicHexColor; 38 | 39 | // 40 | // returns a string hex string representation of the string instance 41 | // 42 | - (UIColor *)cscp_hexColor; 43 | 44 | // 45 | // returns true if the string instance is a valid hex value 46 | // 47 | - (BOOL)cscp_validHex; 48 | 49 | // 50 | // returns an array of UIColors from a gradient hex array 51 | // eg: @"FF0000,00FF00,0000FF", or @"FF0000:0.500000,00FF00:0.500000,0000FF:0.500000" or @"FFFF0000,FF00FF00,FF0000FF" 52 | // 53 | - (NSArray *)cscp_gradientStringColors; 54 | 55 | // 56 | // returns an array of CGColors for setting CAGradientLayer.colors property 57 | // same usage as gradientStringColors 58 | // 59 | - (NSArray *)cscp_gradientStringCGColors; 60 | 61 | // 62 | // legacy api methods deprecated 63 | // 64 | 65 | + (UIColor *)colorFromHexString:(NSString *)hexString 66 | __attribute__((deprecated("WARNING: (colorFromHexString:) has been deprecated, use cscp_colorFromHexString instead."))); 67 | 68 | + (BOOL)isValidHexString:(NSString *)hexString 69 | __attribute__((deprecated("WARNING: (isValidHexString:) has been deprecated, use cscp_isValidHexString: instead."))); 70 | 71 | - (UIColor *)hexColor 72 | __attribute__((deprecated("WARNING: (hexColor) has been deprecated, use cscp_hexColor instead."))); 73 | 74 | - (BOOL)validHex 75 | __attribute__((deprecated("WARNING: (validHex) has been deprecated, use cscp_validHex instead."))); 76 | 77 | - (NSArray *)gradientStringColors 78 | __attribute__((deprecated("WARNING: (gradientStringColors) has been deprecated, use cscp_gradientStringColors instead."))); 79 | 80 | - (NSArray *)gradientStringCGColors 81 | __attribute__((deprecated("WARNING: (gradientStringCGColors) has been deprecated, use cscp_gradientStringCGColors instead."))); 82 | 83 | @end -------------------------------------------------------------------------------- /source/Categories/NSString+CSColorPicker.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 7/28/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import "NSString+CSColorPicker.h" 7 | #import 8 | #import 9 | 10 | struct CGFloat; 11 | @interface NSString (Private) 12 | + (CGFloat)_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length 13 | __attribute__((deprecated("WARNING: (_colorComponentFrom:start:length:) has been deprecated, use _cscp_colorComponentFrom:start:length: instead."))); 14 | + (CGFloat)_cscp_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length; 15 | @end 16 | 17 | @implementation NSString (CSColorPicker) 18 | 19 | + (UIColor *)cscp_dynamicColorFromHexString:(NSString *)hexString { 20 | 21 | if (!hexString || !hexString.length) return [UIColor redColor]; 22 | 23 | if (![hexString containsString:@" "] || ![hexString.lowercaseString containsString:@"l:"] || ![hexString.lowercaseString containsString:@"d:"]) { 24 | return [self _cscp_colorFromHexString:hexString]; 25 | } 26 | 27 | NSArray *components = [hexString componentsSeparatedByString:@" "]; 28 | 29 | if (!components || !components.count) { 30 | return [self _cscp_colorFromHexString:hexString]; 31 | } 32 | 33 | UIColor *light = [self _cscp_colorFromHexString:[components.firstObject substringFromIndex:2]]; 34 | 35 | if (@available(iOS 13.0, *)) { 36 | UIColor *dark = [self _cscp_colorFromHexString:[components.lastObject substringFromIndex:2]]; 37 | return [UIColor colorWithDynamicProvider:^UIColor * (UITraitCollection *traitCollection) { 38 | switch (traitCollection.userInterfaceStyle) { 39 | case UIUserInterfaceStyleUnspecified: 40 | case UIUserInterfaceStyleLight: 41 | return light; 42 | case UIUserInterfaceStyleDark: 43 | return dark; 44 | } 45 | return light; 46 | }]; 47 | } 48 | 49 | return light; 50 | 51 | } 52 | 53 | + (UIColor *)cscp_colorFromHexString:(NSString *)hexString { 54 | return [self _cscp_colorFromHexString:hexString]; 55 | } 56 | 57 | + (UIColor *)_cscp_colorFromHexString:(NSString *)hexString { 58 | NSString *colorString = [[hexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString]; 59 | CGFloat alpha, red, blue, green; 60 | 61 | if ([colorString rangeOfString:@":"].location != NSNotFound) { 62 | NSArray *stringComponents = [colorString componentsSeparatedByString:@":"]; 63 | colorString = stringComponents[0]; 64 | alpha = [stringComponents[1] floatValue] ? : 1.0f; 65 | } 66 | 67 | if (![self cscp_isValidHexString:colorString]) { 68 | return [UIColor colorWithRed:255.0f green:0.0f blue:0.0f alpha:alpha]; 69 | } 70 | 71 | switch ([colorString length]) { 72 | case 3: 73 | alpha = 1.0f; 74 | red = [self _cscp_colorComponentFrom:colorString start:0 length:1]; 75 | green = [self _cscp_colorComponentFrom:colorString start:1 length:1]; 76 | blue = [self _cscp_colorComponentFrom:colorString start:2 length:1]; 77 | break; 78 | case 4: 79 | alpha = [self _cscp_colorComponentFrom:colorString start:0 length:1]; 80 | red = [self _cscp_colorComponentFrom:colorString start:1 length:1]; 81 | green = [self _cscp_colorComponentFrom:colorString start:2 length:1]; 82 | blue = [self _cscp_colorComponentFrom:colorString start:3 length:1]; 83 | break; 84 | case 6: 85 | alpha = 1.0f; 86 | red = [self _cscp_colorComponentFrom:colorString start:0 length:2]; 87 | green = [self _cscp_colorComponentFrom:colorString start:2 length:2]; 88 | blue = [self _cscp_colorComponentFrom:colorString start:4 length:2]; 89 | break; 90 | case 8: 91 | alpha = [self _cscp_colorComponentFrom:colorString start:0 length:2]; 92 | red = [self _cscp_colorComponentFrom:colorString start:2 length:2]; 93 | green = [self _cscp_colorComponentFrom:colorString start:4 length:2]; 94 | blue = [self _cscp_colorComponentFrom:colorString start:6 length:2]; 95 | break; 96 | default: 97 | alpha = 100.0f; 98 | red = green = blue = 255.0f; 99 | break; 100 | } 101 | return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; 102 | } 103 | 104 | + (CGFloat)_cscp_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length { 105 | NSString *substring = [string substringWithRange:NSMakeRange(start, length)]; 106 | NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat:@"%@%@", substring, substring]; 107 | unsigned hexComponent; 108 | [[NSScanner scannerWithString:fullHex] scanHexInt:&hexComponent]; 109 | return hexComponent / 255.0; 110 | } 111 | 112 | + (BOOL)cscp_isValidHexString:(NSString *)hexString { 113 | NSCharacterSet *hexChars = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"] invertedSet]; 114 | return (NSNotFound == [[[hexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString] rangeOfCharacterFromSet:hexChars].location); 115 | } 116 | 117 | - (UIColor *)cscp_dynamicHexColor { 118 | return [NSString cscp_dynamicColorFromHexString:self]; 119 | } 120 | 121 | - (UIColor *)cscp_hexColor { 122 | return [NSString cscp_colorFromHexString:self]; 123 | } 124 | 125 | - (BOOL)cscp_validHex { 126 | return [NSString cscp_isValidHexString:self]; 127 | } 128 | 129 | - (NSArray *)cscp_gradientStringColors { 130 | NSMutableArray *colors = [NSMutableArray new]; 131 | for (NSString *hex in [[self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsSeparatedByString:@","]) { 132 | [colors addObject:[hex cscp_hexColor]]; 133 | } 134 | return colors.copy; 135 | } 136 | 137 | - (NSArray *)cscp_gradientStringCGColors { 138 | NSMutableArray *colors = [NSMutableArray new]; 139 | for (NSString *hex in [[self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsSeparatedByString:@","]) { 140 | [colors addObject:(id)[hex cscp_hexColor].CGColor]; 141 | } 142 | return colors.copy; 143 | } 144 | 145 | // 146 | // legacy api methods deprecated 147 | // 148 | 149 | + (UIColor *)colorFromHexString:(NSString *)hexString { 150 | return [self cscp_colorFromHexString:hexString]; 151 | } 152 | 153 | + (CGFloat)_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length { 154 | return [self _cscp_colorComponentFrom:string start:start length:length]; 155 | } 156 | 157 | + (BOOL)isValidHexString:(NSString *)hexString { 158 | return [self cscp_isValidHexString:hexString]; 159 | } 160 | 161 | - (UIColor *)hexColor { 162 | return [self cscp_hexColor]; 163 | } 164 | 165 | - (BOOL)validHex { 166 | return [self cscp_validHex]; 167 | } 168 | 169 | - (NSArray *)gradientStringColors { 170 | return [self cscp_gradientStringColors]; 171 | } 172 | 173 | - (NSArray *)gradientStringCGColors { 174 | return [self cscp_gradientStringCGColors]; 175 | } 176 | 177 | @end -------------------------------------------------------------------------------- /source/Categories/UIColor+CSColorPicker.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | #import 6 | 7 | @interface UIColor (CSColorPicker) 8 | 9 | // 10 | // returns a UIColor from the hex string eg [UIColor colorFromHexString:@"#FF0000"]; 11 | // if the hex string is invalid, returns red 12 | // supported formats include 'RGB', 'ARGB', 'RRGGBB', 'AARRGGBB', 'RGB:0.500000', 'RRGGBB:0.500000' 13 | // all formats work with or without # 14 | // 15 | + (UIColor *)cscp_colorFromHexString:(NSString *)hexString; 16 | 17 | // 18 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_dynamicHexStringFromColor:[UIColor redColor]]; outputs @"l:#FF0000 d:#FF0000" 19 | // 20 | + (NSString *)cscp_dynamicHexStringFromColor: (UIColor *)color; 21 | 22 | // 23 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_dynamicHexStringFromColor:[UIColor redColor] alpha:YES]; outputs @"#l:FFFF0000 d:FFFF0000" 24 | // 25 | + (NSString *)cscp_dynamicHexStringFromColor: (UIColor *)color alpha:(BOOL)include; 26 | 27 | // 28 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_hexStringFromColor:[UIColor redColor]]; outputs @"#FF0000" 29 | // 30 | + (NSString *)cscp_hexStringFromColor:(UIColor *)color; 31 | 32 | // 33 | // returns a NSString representation of a UIColor in hex format eg [UIColor cscp_hexStringFromColor:[UIColor redColor] alpha:YES]; outputs @"#FFFF0000" 34 | // 35 | + (NSString *)cscp_hexStringFromColor:(UIColor *)color alpha:(BOOL)include; 36 | 37 | // 38 | // returns the brightness of the color where black = 0.0 and white = 256.0 39 | // credit goes to https://w3.org for the algorithm 40 | // 41 | + (CGFloat)cscp_brightnessOfColor:(UIColor *)color; 42 | 43 | // 44 | // returns true if the color is light using csp_brightnessOfColor > 0.5 45 | // 46 | + (BOOL)cscp_isColorLight:(UIColor *)color; 47 | 48 | // 49 | // returns true if the string is a valid hex (will pass with or without #) 50 | // 51 | + (BOOL)cscp_isValidHexString:(NSString *)hexString; 52 | 53 | // 54 | // the alpha component the color instance 55 | // 56 | - (CGFloat)cscp_alpha; 57 | 58 | // 59 | // the red component the color instance 60 | // 61 | - (CGFloat)cscp_red; 62 | 63 | // 64 | // the green component the color instance 65 | // 66 | - (CGFloat)cscp_green; 67 | 68 | // 69 | // the blue component the color instance 70 | // 71 | - (CGFloat)cscp_blue; 72 | 73 | // 74 | // the hue component the color instance 75 | // 76 | - (CGFloat)cscp_hue; 77 | 78 | // 79 | // the saturation component the color instance 80 | // 81 | - (CGFloat)cscp_saturation; 82 | 83 | // 84 | // the brightness component the color instance 85 | // 86 | - (CGFloat)cscp_brightness; 87 | 88 | // 89 | // the dynamic hexString value of the color instance 90 | // 91 | - (NSString *)cscp_dynamicHexString; 92 | 93 | // 94 | // the dynamic hexString value of the color instance with alpha included 95 | // 96 | - (NSString *)cscp_dynamicHexStringWithAlpha; 97 | 98 | // 99 | // the hexString value of the color instance 100 | // 101 | - (NSString *)cscp_hexString; 102 | 103 | // 104 | // the hexString value of the color instance with alpha included 105 | // 106 | - (NSString *)cscp_hexStringWithAlpha; 107 | 108 | // 109 | // is this color instance light 110 | // 111 | - (BOOL)cscp_light; 112 | 113 | // 114 | // is this color instance dark 115 | // 116 | - (BOOL)cscp_dark; 117 | 118 | // 119 | // legacy api methods deprecated 120 | // 121 | 122 | + (UIColor *)colorFromHexString:(NSString *)hexString 123 | __attribute__((deprecated("WARNING: (colorFromHexString:) has been deprecated, use cscp_colorFromHexString instead."))); 124 | 125 | + (NSString *)hexStringFromColor:(UIColor *)color 126 | __attribute__((deprecated("WARNING: (hexStringFromColor:) has been deprecated, use cscp_hexStringFromColor instead."))); 127 | 128 | + (NSString *)hexStringFromColor:(UIColor *)color alpha:(BOOL)include 129 | __attribute__((deprecated("WARNING: (hexStringFromColor:alpha:) has been deprecated, use cscp_hexStringFromColor:alpha: instead."))); 130 | 131 | + (CGFloat)brightnessOfColor:(UIColor *)color 132 | __attribute__((deprecated("WARNING: (brightnessOfColor:alpha:) has been deprecated, use cscp_brightnessOfColor instead."))); 133 | 134 | + (BOOL)isColorLight:(UIColor *)color 135 | __attribute__((deprecated("WARNING: (isColorLight:) has been deprecated, use cscp_isColorLight instead."))); 136 | 137 | + (BOOL)isValidHexString:(NSString *)hexString 138 | __attribute__((deprecated("WARNING: (isValidHexString:) has been deprecated, use cscp_isValidHexString instead."))); 139 | 140 | - (CGFloat)alpha 141 | __attribute__((deprecated("WARNING: (alpha) has been deprecated, use cscp_alpha instead."))); 142 | 143 | - (CGFloat)red 144 | __attribute__((deprecated("WARNING: (red) has been deprecated, use cscp_red instead."))); 145 | 146 | - (CGFloat)green 147 | __attribute__((deprecated("WARNING: (green) has been deprecated, use cscp_green instead."))); 148 | 149 | - (CGFloat)blue 150 | __attribute__((deprecated("WARNING: (blue) has been deprecated, use cscp_blue instead."))); 151 | 152 | - (CGFloat)hue 153 | __attribute__((deprecated("WARNING: (hue) has been deprecated, use cscp_hue instead."))); 154 | 155 | - (CGFloat)saturation 156 | __attribute__((deprecated("WARNING: (saturation) has been deprecated, use cscp_saturation instead."))); 157 | 158 | - (CGFloat)brightness 159 | __attribute__((deprecated("WARNING: (brightness) has been deprecated, use cscp_brightness instead."))); 160 | 161 | - (NSString *)hexString 162 | __attribute__((deprecated("WARNING: (hexString) has been deprecated, use cscp_hexString instead."))); 163 | 164 | - (NSString *)hexStringWithAlpha 165 | __attribute__((deprecated("WARNING: (hexStringWithAlpha) has been deprecated, use cscp_hexStringWithAlpha instead."))); 166 | 167 | - (BOOL)light 168 | __attribute__((deprecated("WARNING: (light) has been deprecated, use cscp_light instead."))); 169 | 170 | - (BOOL)dark 171 | __attribute__((deprecated("WARNING: (dark) has been deprecated, use cscp_dark instead."))); 172 | 173 | @end 174 | -------------------------------------------------------------------------------- /source/Categories/UIColor+CSColorPicker.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import "UIColor+CSColorPicker.h" 7 | 8 | @interface UIColor (Private) 9 | + (CGFloat)_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length 10 | __attribute__((deprecated("WARNING: (_colorComponentFrom:start:length:) has been deprecated, use _cscp_colorComponentFrom:start:length: instead."))); 11 | + (CGFloat)_cscp_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length; 12 | @end 13 | 14 | @implementation UIColor (CSColorPicker) 15 | 16 | + (UIColor *)cscp_dynamicColorFromHexString:(NSString *)hexString { 17 | 18 | if (!hexString || !hexString.length) return [UIColor redColor]; 19 | 20 | if (![hexString containsString:@" "] || ![hexString.lowercaseString containsString:@"l:"] || ![hexString.lowercaseString containsString:@"d:"]) { 21 | return [self _cscp_colorFromHexString:hexString]; 22 | } 23 | 24 | NSArray *components = [hexString componentsSeparatedByString:@" "]; 25 | 26 | if (!components || !components.count) { 27 | return [self _cscp_colorFromHexString:hexString]; 28 | } 29 | 30 | UIColor *light = [self _cscp_colorFromHexString:[components.firstObject substringFromIndex:2]]; 31 | 32 | if (@available(iOS 13.0, *)) { 33 | UIColor *dark = [self _cscp_colorFromHexString:[components.lastObject substringFromIndex:2]]; 34 | return [UIColor colorWithDynamicProvider:^UIColor * (UITraitCollection *traitCollection) { 35 | switch (traitCollection.userInterfaceStyle) { 36 | case UIUserInterfaceStyleUnspecified: 37 | case UIUserInterfaceStyleLight: 38 | return light; 39 | case UIUserInterfaceStyleDark: 40 | return dark; 41 | } 42 | return light; 43 | }]; 44 | } 45 | 46 | return light; 47 | 48 | } 49 | 50 | + (UIColor *)cscp_colorFromHexString:(NSString *)hexString { 51 | return [self _cscp_colorFromHexString:hexString]; 52 | } 53 | 54 | + (UIColor *)_cscp_colorFromHexString:(NSString *)hexString { 55 | 56 | NSString *colorString = [[hexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString]; 57 | CGFloat alpha, red, blue, green; 58 | 59 | if ([colorString rangeOfString:@":"].location != NSNotFound) { 60 | NSArray *stringComponents = [colorString componentsSeparatedByString:@":"]; 61 | colorString = stringComponents[0]; 62 | alpha = [stringComponents[1] floatValue] ? : 1.0f; 63 | } 64 | 65 | if (![UIColor cscp_isValidHexString:colorString]) { 66 | return [UIColor redColor]; 67 | } 68 | 69 | switch ([colorString length]) { 70 | case 3: 71 | alpha = 1.0f; 72 | red = [self _cscp_colorComponentFrom:colorString start:0 length:1]; 73 | green = [self _cscp_colorComponentFrom:colorString start:1 length:1]; 74 | blue = [self _cscp_colorComponentFrom:colorString start:2 length:1]; 75 | break; 76 | case 4: 77 | alpha = [self _cscp_colorComponentFrom:colorString start:0 length:1]; 78 | red = [self _cscp_colorComponentFrom:colorString start:1 length:1]; 79 | green = [self _cscp_colorComponentFrom:colorString start:2 length:1]; 80 | blue = [self _cscp_colorComponentFrom:colorString start:3 length:1]; 81 | break; 82 | case 6: 83 | alpha = 1.0f; 84 | red = [self _cscp_colorComponentFrom:colorString start:0 length:2]; 85 | green = [self _cscp_colorComponentFrom:colorString start:2 length:2]; 86 | blue = [self _cscp_colorComponentFrom:colorString start:4 length:2]; 87 | break; 88 | case 8: 89 | alpha = [self _cscp_colorComponentFrom:colorString start:0 length:2]; 90 | red = [self _cscp_colorComponentFrom:colorString start:2 length:2]; 91 | green = [self _cscp_colorComponentFrom:colorString start:4 length:2]; 92 | blue = [self _cscp_colorComponentFrom:colorString start:6 length:2]; 93 | break; 94 | default: 95 | alpha = 100.0f; 96 | red = green = blue = 255.0f; 97 | break; 98 | } 99 | return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; 100 | } 101 | 102 | + (CGFloat)_cscp_colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length { 103 | NSString *substring = [string substringWithRange:NSMakeRange(start, length)]; 104 | NSString *fullHex = length == 2 ? substring : [NSString stringWithFormat:@"%@%@", substring, substring]; 105 | unsigned hexComponent; 106 | [[NSScanner scannerWithString:fullHex] scanHexInt:&hexComponent]; 107 | return hexComponent / 255.0; 108 | } 109 | 110 | + (BOOL)cscp_isValidHexString:(NSString *)hexString { 111 | NSCharacterSet *hexChars = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEFabcdef"] invertedSet]; 112 | return (NSNotFound == [[[hexString stringByReplacingOccurrencesOfString:@"#" withString:@""] uppercaseString] rangeOfCharacterFromSet:hexChars].location); 113 | } 114 | 115 | + (NSString *)cscp_dynamicHexStringFromColor: (UIColor *)color { 116 | return [self cscp_dynamicHexStringFromColor:color alpha:NO]; 117 | } 118 | 119 | + (NSString *)cscp_dynamicHexStringFromColor: (UIColor *)color alpha:(BOOL)include { 120 | 121 | CGFloat rl,gl,bl,al,rd,gd,bd,ad; 122 | BOOL (^componentsForColor)(UIColor*, CGFloat*, CGFloat*, CGFloat*, CGFloat*) = ^BOOL (UIColor*color ,CGFloat*red, CGFloat*green, CGFloat*blue, CGFloat*alpha) { 123 | BOOL value = [color getRed:red green:green blue:blue alpha:alpha]; 124 | *red = roundf((float) (*red * 255.0f)); 125 | *green = roundf((float) (*green * 255.0f)); 126 | *blue = roundf((float) (*blue * 255.0f)); 127 | *alpha = roundf((float) (*blue * 255.0f)); 128 | return value; 129 | }; 130 | 131 | if (@available(iOS 13.0, *)) { 132 | componentsForColor( 133 | [color resolvedColorWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]], 134 | &rl, &gl, &bl, &al 135 | ); 136 | componentsForColor( 137 | [color resolvedColorWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]], 138 | &rd, &gd, &bd, &ad 139 | ); 140 | } else { 141 | componentsForColor(color, &rl, &gl, &bl, &al); 142 | componentsForColor(color, &rd, &gd, &bd, &ad); 143 | } 144 | 145 | return include ? [[NSString stringWithFormat:@"l:%02x%02x%02x%02x d:%02x%02x%02x%02x", (int)al, (int)rl, (int)gl, (int)bl, (int)ad, (int)rd, (int)gd, (int)bd] uppercaseString] : 146 | [[NSString stringWithFormat:@"l:%02x%02x%02x d:%02x%02x%02x", (int)rl, (int)gl, (int)bl, (int)rd, (int)gd, (int)bd] uppercaseString]; 147 | } 148 | 149 | + (NSString *)cscp_hexStringFromColor:(UIColor *)color { 150 | 151 | CGFloat red, green, blue; 152 | [color getRed:&red green:&green blue:&blue alpha:nil]; 153 | red = roundf(red * 255.0f); 154 | green = roundf(green * 255.0f); 155 | blue = roundf(blue * 255.0f); 156 | 157 | return [[NSString stringWithFormat:@"%02x%02x%02x", (int)red, (int)green, (int)blue] uppercaseString]; 158 | } 159 | 160 | + (NSString *)cscp_hexStringFromColor:(UIColor *)color alpha:(BOOL)include { 161 | 162 | CGFloat red, green, blue, alpha; 163 | [color getRed:&red green:&green blue:&blue alpha:&alpha]; 164 | red = roundf(red * 255.0f); 165 | green = roundf(green * 255.0f); 166 | blue = roundf(blue * 255.0f); 167 | alpha = roundf(alpha * 255.0f); 168 | 169 | return include ? [[NSString stringWithFormat:@"%02x%02x%02x%02x", (int)alpha, (int)red, (int)green, (int)blue] uppercaseString] : 170 | [[NSString stringWithFormat:@"%02x%02x%02x", (int)red, (int)green, (int)blue] uppercaseString]; 171 | } 172 | 173 | + (CGFloat)cscp_brightnessOfColor:(UIColor *)color { 174 | CGFloat red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0; 175 | [color getRed:&red green:&green blue:&blue alpha:&alpha]; 176 | 177 | return (((red * 255) * 299) + ((green * 255) * 587) + ((blue * 255) * 114)) / 1000; 178 | } 179 | 180 | + (BOOL)cscp_isColorLight:(UIColor *)color { 181 | return ([UIColor cscp_brightnessOfColor:color] >= 128.0); 182 | } 183 | 184 | - (CGFloat)cscp_alpha { 185 | CGFloat a; 186 | [self getWhite:NULL alpha:&a]; 187 | return a; 188 | } 189 | 190 | - (CGFloat)cscp_red { 191 | CGFloat r; 192 | [self getRed:&r green:NULL blue:NULL alpha:NULL]; 193 | return r; 194 | } 195 | 196 | - (CGFloat)cscp_green { 197 | CGFloat g; 198 | [self getRed:NULL green:&g blue:NULL alpha:NULL]; 199 | return g; 200 | } 201 | 202 | - (CGFloat)cscp_blue { 203 | CGFloat b; 204 | [self getRed:NULL green:NULL blue:&b alpha:NULL]; 205 | return b; 206 | } 207 | 208 | - (CGFloat)cscp_hue { 209 | CGFloat h; 210 | [self getHue:&h saturation:NULL brightness:NULL alpha:NULL]; 211 | return h; 212 | } 213 | 214 | - (CGFloat)cscp_saturation { 215 | CGFloat s; 216 | [self getHue:NULL saturation:&s brightness:NULL alpha:NULL]; 217 | return s; 218 | } 219 | 220 | - (CGFloat)cscp_brightness { 221 | CGFloat b; 222 | [self getHue:NULL saturation:NULL brightness:&b alpha:NULL]; 223 | return b; 224 | } 225 | 226 | - (NSString *)cscp_dynamicHexString { 227 | return [UIColor cscp_dynamicHexStringFromColor:self]; 228 | } 229 | 230 | - (NSString *)cscp_dynamicHexStringWithAlpha { 231 | return [UIColor cscp_dynamicHexStringFromColor:self alpha:YES]; 232 | } 233 | 234 | - (NSString *)cscp_hexString { 235 | return [UIColor cscp_hexStringFromColor:self]; 236 | } 237 | 238 | - (NSString *)cscp_hexStringWithAlpha { 239 | return [UIColor cscp_hexStringFromColor:self alpha:YES]; 240 | } 241 | 242 | - (BOOL)cscp_light { 243 | return [UIColor cscp_isColorLight:self]; 244 | } 245 | 246 | - (BOOL)cscp_dark { 247 | return ![UIColor cscp_isColorLight:self]; 248 | } 249 | 250 | // 251 | // legacy api methods deprecated 252 | // 253 | 254 | + (UIColor *)colorFromHexString:(NSString *)hexString { 255 | return [self cscp_colorFromHexString:hexString]; 256 | } 257 | 258 | + (CGFloat)colorComponentFrom:(NSString *)string start:(NSUInteger)start length:(NSUInteger)length { 259 | return [self _cscp_colorComponentFrom:string start:start length:length]; 260 | } 261 | 262 | + (BOOL)isValidHexString:(NSString *)hexString { 263 | return [self cscp_isValidHexString:hexString]; 264 | } 265 | 266 | + (NSString *)hexStringFromColor:(UIColor *)color { 267 | return [self cscp_hexStringFromColor:color]; 268 | } 269 | 270 | + (NSString *)hexStringFromColor:(UIColor *)color alpha:(BOOL)include { 271 | return [self cscp_hexStringFromColor:color alpha:include]; 272 | } 273 | 274 | + (CGFloat)brightnessOfColor:(UIColor *)color { 275 | return [self cscp_brightnessOfColor:color]; 276 | } 277 | 278 | + (BOOL)isColorLight:(UIColor *)color { 279 | return [self cscp_isColorLight:color]; 280 | } 281 | 282 | - (CGFloat)alpha { 283 | return [self cscp_alpha]; 284 | } 285 | 286 | - (CGFloat)red { 287 | return [self cscp_red]; 288 | } 289 | 290 | - (CGFloat)green { 291 | return [self cscp_green]; 292 | } 293 | 294 | - (CGFloat)blue { 295 | return [self cscp_blue]; 296 | } 297 | 298 | - (CGFloat)hue { 299 | return [self cscp_hue]; 300 | } 301 | 302 | - (CGFloat)saturation { 303 | return [self cscp_saturation]; 304 | } 305 | 306 | - (CGFloat)brightness { 307 | return [self cscp_brightness]; 308 | } 309 | 310 | - (NSString *)hexString { 311 | return [self cscp_hexString]; 312 | } 313 | 314 | - (NSString *)hexStringWithAlpha { 315 | return [self cscp_hexStringWithAlpha]; 316 | } 317 | 318 | - (BOOL)light { 319 | return [self cscp_light]; 320 | } 321 | 322 | - (BOOL)dark { 323 | return [self cscp_dark]; 324 | } 325 | 326 | @end -------------------------------------------------------------------------------- /source/Cells/CSBaseDisplayCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | #import 9 | #import 10 | 11 | 12 | @interface CSBaseDisplayCell : PSTableCell 13 | 14 | @property (nonatomic, retain) UIView *cellColorDisplay; 15 | 16 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)identifier specifier:(PSSpecifier *)specifier; 17 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier; 18 | 19 | - (void)openColorPickerView; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /source/Cells/CSBaseDisplayCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | @implementation CSBaseDisplayCell 10 | 11 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)identifier specifier:(PSSpecifier *)specifier { 12 | 13 | if ((self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier specifier:specifier])) { 14 | [specifier setTarget:self]; 15 | [specifier setButtonAction:@selector(openColorPickerView)]; 16 | self.detailTextLabel.textColor = secondaryLabelColor(); 17 | 18 | self.cellColorDisplay = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 58, 29)]; 19 | self.cellColorDisplay.tag = 199; 20 | self.cellColorDisplay.layer.cornerRadius = CGRectGetHeight(self.cellColorDisplay.frame) / 4; 21 | self.cellColorDisplay.layer.borderWidth = 2; 22 | self.cellColorDisplay.layer.borderColor = UIColor.lightGrayColor.CGColor; 23 | [self setAccessoryView:self.cellColorDisplay]; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier { 30 | [super refreshCellContentsWithSpecifier:specifier]; 31 | 32 | } 33 | 34 | - (void)openColorPickerView { 35 | PSViewController *viewController; 36 | UIViewController *vc; 37 | 38 | if ([self respondsToSelector:@selector(_viewControllerForAncestor)]) { 39 | vc = [self performSelector:@selector(_viewControllerForAncestor)]; 40 | } 41 | 42 | if (!vc) { 43 | if ([self.specifier propertyForKey:@"parent"]) { 44 | vc = [self.specifier propertyForKey:@"parent"]; 45 | } else { 46 | vc = UIViewParentController(self); 47 | } 48 | } 49 | 50 | if (vc && [vc isKindOfClass:[PSViewController class]]) { 51 | viewController = (PSViewController *)vc; 52 | } else { 53 | return; 54 | } 55 | 56 | 57 | CSColorPickerViewController *colorViewController = [[CSColorPickerViewController alloc] init]; 58 | 59 | if (self.specifier && [self.specifier propertyForKey:@"defaults"] && [self.specifier propertyForKey:@"key"]) { 60 | 61 | colorViewController.specifier = self.specifier; 62 | } 63 | 64 | colorViewController.view.frame = viewController.view.frame; 65 | colorViewController.parentController = viewController; 66 | colorViewController.specifier = self.specifier; 67 | 68 | if (firmwareGreaterThanEqual(@"13.0")) { 69 | NSLog(@"%@", viewController.debugDescription); 70 | [viewController presentViewController:NAVIGATION_WRAPPER_WITH_CONTROLLER(colorViewController) animated:YES completion:nil]; 71 | } else { 72 | [viewController.navigationController pushViewController:colorViewController animated:YES]; 73 | } 74 | } 75 | 76 | - (PSSpecifier *)specifier { 77 | 78 | return [super specifier]; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /source/Cells/CSColorDisplayCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | 9 | @interface CSColorDisplayCell : CSBaseDisplayCell 10 | 11 | - (void)refreshCellWithColor:(UIColor *)color; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /source/Cells/CSColorDisplayCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | @implementation CSColorDisplayCell 10 | 11 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier { 12 | [super refreshCellContentsWithSpecifier:specifier]; 13 | 14 | [self refreshCellWithColor:nil]; 15 | } 16 | 17 | - (void)refreshCellWithColor:(UIColor *)color { 18 | 19 | if (!color) { 20 | color = [self previewColor]; 21 | } else { 22 | [self.specifier setProperty:color.cscp_hexStringWithAlpha forKey:@"hexValue"]; 23 | [self.specifier setProperty:color forKey:@"color"]; 24 | } 25 | 26 | self.cellColorDisplay.backgroundColor = color; 27 | self.detailTextLabel.text = [NSString stringWithFormat:@"#%@", [color cscp_hexString]]; 28 | } 29 | 30 | - (void)didMoveToSuperview { 31 | [super didMoveToSuperview]; 32 | 33 | if (!self.specifier) { 34 | return; 35 | } 36 | 37 | [self refreshCellWithColor:nil]; 38 | } 39 | 40 | - (UIColor *)previewColor { 41 | NSString *userPrefsPath, *defaultsPlistPath, *motuumLSDefaultsPath, *hex; 42 | NSDictionary *prefsDict, *defaultsDict; 43 | UIColor *color; 44 | 45 | userPrefsPath = [NSString stringWithFormat:@"/User/Library/Preferences/%@.plist", [self.specifier propertyForKey:@"defaults"]]; 46 | defaultsPlistPath = [[NSBundle bundleWithPath:[self.specifier propertyForKey:@"defaultsPath"]] pathForResource:@"defaults" ofType:@"plist"]; 47 | motuumLSDefaultsPath = [[NSBundle bundleWithPath:@"/Library/PreferenceBundles/motuumLS.bundle"] pathForResource:@"com.creaturesurvive.motuumls_defaults" ofType:@"plist"]; 48 | 49 | if ((prefsDict = [NSDictionary dictionaryWithContentsOfFile:userPrefsPath])) { 50 | hex = prefsDict[[self.specifier propertyForKey:@"key"]]; 51 | } 52 | 53 | if (!hex && (defaultsDict = [NSDictionary dictionaryWithContentsOfFile:defaultsPlistPath])) { 54 | hex = defaultsDict[[self.specifier propertyForKey:@"key"]]; 55 | } 56 | 57 | if (!hex && (defaultsDict = [NSDictionary dictionaryWithContentsOfFile:motuumLSDefaultsPath])) { 58 | hex = defaultsDict[[self.specifier propertyForKey:@"key"]]; 59 | } 60 | 61 | if (!hex) { 62 | hex = [self.specifier propertyForKey:@"fallback"] ? : @"FF0000"; 63 | } 64 | 65 | color = [UIColor cscp_colorFromHexString:hex]; 66 | [self.specifier setProperty:hex forKey:@"hexValue"]; 67 | [self.specifier setProperty:color forKey:@"color"]; 68 | 69 | return color; 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /source/Cells/CSGradientDisplayCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | 9 | @interface CSGradientDisplayCell : CSBaseDisplayCell 10 | 11 | @property (nonatomic, retain) CAGradientLayer *gradient; 12 | 13 | - (void)refreshCellWithColors:(NSArray *)newColors; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /source/Cells/CSGradientDisplayCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | @implementation CSGradientDisplayCell 10 | 11 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)identifier specifier:(PSSpecifier *)specifier { 12 | 13 | if ((self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier specifier:specifier])) { 14 | [specifier setProperty:@(YES) forKey:@"gradient"]; 15 | 16 | self.gradient = [CAGradientLayer layer]; 17 | self.gradient.frame = self.cellColorDisplay.bounds; 18 | self.gradient.cornerRadius = self.cellColorDisplay.layer.cornerRadius; 19 | self.gradient.startPoint = CGPointMake(0, 0.5); 20 | self.gradient.endPoint = CGPointMake(1, 0.5); 21 | 22 | [self.cellColorDisplay.layer addSublayer:self.gradient]; 23 | } 24 | 25 | return self; 26 | } 27 | 28 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier { 29 | [super refreshCellContentsWithSpecifier:specifier]; 30 | 31 | [self refreshCellWithColors:nil]; 32 | } 33 | 34 | - (void)refreshCellWithColors:(NSArray *)newColors { 35 | 36 | if (!newColors) { 37 | newColors = [self previewColors]; 38 | } else { 39 | [self.specifier setProperty:newColors.lastObject.cscp_hexString forKey:@"hexValue"]; 40 | [self.specifier setProperty:newColors.lastObject forKey:@"color"]; 41 | [self.specifier setProperty:newColors forKey:@"colors"]; 42 | } 43 | 44 | self.detailTextLabel.text = nil; 45 | NSMutableArray *colors = [NSMutableArray new]; 46 | for (UIColor *color in newColors) { 47 | if (self.detailTextLabel.text) 48 | self.detailTextLabel.text = [self.detailTextLabel.text stringByAppendingFormat:@", #%@", color.cscp_hexString]; 49 | else self.detailTextLabel.text = [NSString stringWithFormat:@"#%@", color.cscp_hexString]; 50 | [colors addObject:(id)color.CGColor]; 51 | } 52 | 53 | if (colors.count == 1) { 54 | [colors addObject:colors.firstObject]; 55 | } 56 | 57 | self.gradient.colors = colors; 58 | } 59 | 60 | - (void)didMoveToSuperview { 61 | [super didMoveToSuperview]; 62 | 63 | if (!self.specifier) { 64 | return; 65 | } 66 | 67 | [self refreshCellWithColors:nil]; 68 | } 69 | 70 | - (NSArray *)previewColors { 71 | NSString *userPrefsPath, *defaultsPlistPath, *hexs; 72 | NSDictionary *prefsDict, *defaultsDict; 73 | NSMutableArray *colors = [NSMutableArray new]; 74 | 75 | userPrefsPath = [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", [self.specifier propertyForKey:@"defaults"]]; 76 | defaultsPlistPath = [[NSBundle bundleWithPath:[self.specifier propertyForKey:@"defaultsPath"]] pathForResource:@"defaults" ofType:@"plist"]; 77 | 78 | if ((prefsDict = [NSDictionary dictionaryWithContentsOfFile:userPrefsPath])) { 79 | hexs = prefsDict[[self.specifier propertyForKey:@"key"]]; 80 | } 81 | 82 | if (!hexs && (defaultsDict = [NSDictionary dictionaryWithContentsOfFile:defaultsPlistPath])) { 83 | hexs = defaultsDict[[self.specifier propertyForKey:@"key"]]; 84 | } 85 | 86 | if (!hexs) { 87 | hexs = [self.specifier propertyForKey:@"fallback"] ? : @"FF0000,FFFFFF"; 88 | } 89 | 90 | for (NSString *hex in [hexs componentsSeparatedByString:@","]) { 91 | [colors addObject:[hex cscp_hexColor]]; 92 | } 93 | 94 | if (colors.count < 2) [colors addObject:UIColor.redColor]; 95 | [self.specifier setProperty:colors.lastObject.cscp_hexStringWithAlpha forKey:@"hexValue"]; 96 | [self.specifier setProperty:colors.lastObject forKey:@"color"]; 97 | [self.specifier setProperty:colors forKey:@"colors"]; 98 | 99 | return colors; 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /source/Controllers/CSColorPickerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | @interface NSUserDefaults (Private) 15 | - (void)setObject:(id)object forKey:(NSString *)key inDomain:(NSString *)domain; 16 | @end 17 | 18 | @interface CSColorPickerViewController : PSViewController 19 | 20 | @property (nonatomic, strong) UIView *colorPickerContainerView; 21 | @property (nonatomic, strong) UILabel *colorInformationLable; 22 | @property (nonatomic, strong) UIImageView *colorTrackImageView; 23 | @property (nonatomic, strong) CSColorPickerBackgroundView *colorPickerBackgroundView; 24 | @property (nonatomic, strong) UIView *colorPickerPreviewView; 25 | @property (nonatomic, strong) CSGradientSelection *gradientSelection; 26 | @property (nonatomic, strong) UIVisualEffectView *topBackdrop; 27 | @property (nonatomic, strong) UIVisualEffectView *bottomBackdrop; 28 | 29 | @property (nonatomic, retain) CSColorSlider *colorPickerHueSlider; 30 | @property (nonatomic, retain) CSColorSlider *colorPickerSaturationSlider; 31 | @property (nonatomic, retain) CSColorSlider *colorPickerBrightnessSlider; 32 | @property (nonatomic, retain) CSColorSlider *colorPickerAlphaSlider; 33 | 34 | @property (nonatomic, retain) CSColorSlider *colorPickerRedSlider; 35 | @property (nonatomic, retain) CSColorSlider *colorPickerGreenSlider; 36 | @property (nonatomic, retain) CSColorSlider *colorPickerBlueSlider; 37 | 38 | @property (nonatomic, assign) BOOL alphaEnabled; 39 | @property (nonatomic, assign) BOOL isGradient; 40 | @property (nonatomic, assign) NSInteger selectedIndex; 41 | @property (nonatomic, retain) NSMutableArray *colors; 42 | 43 | - (void)loadColorPickerView; 44 | - (void)sliderDidChange:(CSColorSlider *)sender; 45 | - (BOOL)isLandscape; 46 | 47 | - (void)dismiss; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /source/Controllers/CSColorPickerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | #import 9 | #import 10 | 11 | #define SLIDER_HEIGHT 40.0 12 | #define GRADIENT_HEIGHT 50.0 13 | #define ALERT_TITLE @"Set Hex Color" 14 | #define ALERT_MESSAGE @"supported formats: 'RGB' 'ARGB' 'RRGGBB' 'AARRGGBB' 'RGB:0.25' 'RRGGBB:0.25'" 15 | #define ALERT_COPY @"Copy Color" 16 | #define ALERT_SET @"Set Color" 17 | #define ALERT_PASTEBOARD @"Set From PasteBoard" 18 | #define ALERT_CANCEL @"Cancel" 19 | 20 | @implementation CSColorPickerViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | 25 | [self loadColorPickerView]; 26 | [self setColorInformationTextWithInformationFromColor:[self colorForHSBSliders]]; 27 | self.colorPickerPreviewView.backgroundColor = [self startColor]; 28 | 29 | if (firmwareGreaterThanEqual(@"13.0")) { 30 | #pragma clang diagnostic push 31 | #pragma clang diagnostic ignored "-Wunguarded-availability" 32 | self.navigationItem.title = self.specifier.name; 33 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss)]; 34 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"number.circle"] style:UIBarButtonItemStylePlain target:self action:@selector(presentHexColorAlert)]; 35 | #pragma clang diagnostic pop 36 | } else { 37 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"#" style:UIBarButtonItemStylePlain target:self action:@selector(presentHexColorAlert)]; 38 | } 39 | } 40 | 41 | - (void)viewWillDisappear:(BOOL)animated { 42 | [super viewWillDisappear:animated]; 43 | [self saveColor]; 44 | } 45 | 46 | - (void)viewDidDisappear:(BOOL)animated { 47 | [super viewDidDisappear:animated]; 48 | } 49 | 50 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { 51 | 52 | [UIView animateWithDuration:duration animations:^{ 53 | [self setColorInformationTextWithInformationFromColor:[self colorForHSBSliders]]; 54 | }]; 55 | } 56 | 57 | - (void)loadColorPickerView { 58 | 59 | self.view.backgroundColor = [UIColor respondsToSelector:@selector(systemBackgroundColor)] ? [UIColor performSelector:@selector(systemBackgroundColor)] : [UIColor whiteColor]; 60 | 61 | // create views 62 | self.alphaEnabled = ([self.specifier propertyForKey:@"alpha"] && ![[self.specifier propertyForKey:@"alpha"] boolValue]) ? NO : YES; 63 | self.isGradient = ([self.specifier propertyForKey:@"gradient"] && [[self.specifier propertyForKey:@"gradient"] boolValue]); 64 | 65 | CGRect bounds = [self calculatedBounds]; 66 | self.colorPickerContainerView = [[UIView alloc] initWithFrame:bounds]; 67 | self.colorPickerContainerView.tag = 199; 68 | [self.colorPickerContainerView setTranslatesAutoresizingMaskIntoConstraints:NO]; 69 | self.colorPickerBackgroundView = [[CSColorPickerBackgroundView alloc] initWithFrame:bounds]; 70 | [self.colorPickerBackgroundView setTranslatesAutoresizingMaskIntoConstraints:NO]; 71 | self.colorPickerPreviewView = [[UIView alloc] initWithFrame:bounds]; 72 | self.colorPickerPreviewView.tag = 199; 73 | [self.colorPickerPreviewView setTranslatesAutoresizingMaskIntoConstraints:NO]; 74 | 75 | self.colors = [self.specifier propertyForKey:@"colors"]; 76 | self.selectedIndex = self.colors.count - 1; 77 | self.gradientSelection = [[CSGradientSelection alloc] initWithSize:CGSizeZero target:self addAction:@selector(addAction:) removeAction:@selector(removeAction:) selectAction:@selector(selectAction:)]; 78 | [self.gradientSelection setTranslatesAutoresizingMaskIntoConstraints:NO]; 79 | [self.colorPickerContainerView addSubview:self.gradientSelection]; 80 | 81 | UIBlurEffect *effect; 82 | if (firmwareGreaterThanEqual(@"13.0")) { 83 | #pragma clang diagnostic push 84 | #pragma clang diagnostic ignored "-Wunguarded-availability" 85 | effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemChromeMaterial]; 86 | #pragma clang diagnostic pop 87 | } else { 88 | effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]; 89 | } 90 | self.topBackdrop = [[UIVisualEffectView alloc] initWithEffect:effect]; 91 | self.bottomBackdrop = [[UIVisualEffectView alloc] initWithEffect:effect]; 92 | [self.topBackdrop setTranslatesAutoresizingMaskIntoConstraints:NO]; 93 | [self.bottomBackdrop setTranslatesAutoresizingMaskIntoConstraints:NO]; 94 | [self.colorPickerContainerView insertSubview:self.topBackdrop atIndex:0]; 95 | [self.colorPickerContainerView insertSubview:self.bottomBackdrop atIndex:1]; 96 | 97 | [self.gradientSelection addColors:self.colors]; 98 | 99 | self.colorInformationLable = [[UILabel alloc] initWithFrame:CGRectZero]; 100 | [self.colorInformationLable setNumberOfLines:self.alphaEnabled ? 11 : 9]; 101 | [self.colorInformationLable setFont:[UIFont boldSystemFontOfSize:[self isLandscape] ? 16 : 20]]; 102 | [self.colorInformationLable setBackgroundColor:[UIColor clearColor]]; 103 | [self.colorInformationLable setTextAlignment:NSTextAlignmentCenter]; 104 | [self.colorPickerContainerView addSubview:self.colorInformationLable]; 105 | [self.colorInformationLable setTranslatesAutoresizingMaskIntoConstraints:NO]; 106 | [self.colorInformationLable.layer setShadowOffset:CGSizeZero]; 107 | [self.colorInformationLable.layer setShadowRadius:2]; 108 | [self.colorInformationLable.layer setShadowOpacity:1]; 109 | self.colorInformationLable.tag = 199; 110 | 111 | //Alpha slider 112 | self.colorPickerAlphaSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeAlpha label:@"A" startColor:[self startColor]]; 113 | [self.colorPickerAlphaSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 114 | [self.colorPickerContainerView addSubview:self.colorPickerAlphaSlider]; 115 | [self.colorPickerAlphaSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 116 | 117 | //hue slider 118 | self.colorPickerHueSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeHue label:@"H" startColor:[self startColor]]; 119 | [self.colorPickerHueSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 120 | [self.colorPickerContainerView addSubview:self.colorPickerHueSlider]; 121 | [self.colorPickerHueSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 122 | 123 | // saturation slider 124 | self.colorPickerSaturationSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeSaturation label:@"S" startColor:[self startColor]]; 125 | [self.colorPickerSaturationSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 126 | [self.colorPickerContainerView addSubview:self.colorPickerSaturationSlider]; 127 | [self.colorPickerSaturationSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 128 | 129 | // brightness slider 130 | self.colorPickerBrightnessSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeBrightness label:@"B" startColor:[self startColor]]; 131 | [self.colorPickerBrightnessSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 132 | [self.colorPickerContainerView addSubview:self.colorPickerBrightnessSlider]; 133 | [self.colorPickerBrightnessSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 134 | 135 | // red slider 136 | self.colorPickerRedSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeRed label:@"R" startColor:[self startColor]]; 137 | [self.colorPickerRedSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 138 | [self.colorPickerContainerView addSubview:self.colorPickerRedSlider]; 139 | [self.colorPickerRedSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 140 | 141 | // green slider 142 | self.colorPickerGreenSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeGreen label:@"G" startColor:[self startColor]]; 143 | [self.colorPickerGreenSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 144 | [self.colorPickerContainerView addSubview:self.colorPickerGreenSlider]; 145 | [self.colorPickerGreenSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 146 | 147 | // blue slider 148 | self.colorPickerBlueSlider = [[CSColorSlider alloc] initWithFrame:CGRectZero sliderType:CSColorSliderTypeBlue label:@"B" startColor:[self startColor]]; 149 | [self.colorPickerBlueSlider addTarget:self action:@selector(sliderDidChange:) forControlEvents:UIControlEventValueChanged]; 150 | [self.colorPickerContainerView addSubview:self.colorPickerBlueSlider]; 151 | [self.colorPickerBlueSlider setTranslatesAutoresizingMaskIntoConstraints:NO]; 152 | 153 | [self.view insertSubview:self.colorPickerBackgroundView atIndex:0]; 154 | [self.view insertSubview:self.colorPickerPreviewView atIndex:1]; 155 | [self.view insertSubview:self.colorPickerContainerView atIndex:2]; 156 | 157 | // alpha enabled 158 | self.colorPickerAlphaSlider.hidden = !self.alphaEnabled; 159 | self.colorPickerAlphaSlider.userInteractionEnabled = self.alphaEnabled; 160 | self.gradientSelection.hidden = !self.isGradient; 161 | self.gradientSelection.userInteractionEnabled = self.isGradient; 162 | 163 | [NSLayoutConstraint activateConstraints:@[ 164 | // container view 165 | [self.colorPickerContainerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], 166 | [self.colorPickerContainerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], 167 | [self.colorPickerContainerView.topAnchor constraintEqualToAnchor:self.view.layoutMarginsGuide.topAnchor], 168 | [self.colorPickerContainerView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], 169 | // color preview 170 | [self.colorPickerPreviewView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], 171 | [self.colorPickerPreviewView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], 172 | [self.colorPickerPreviewView.topAnchor constraintEqualToAnchor:self.view.topAnchor], 173 | [self.colorPickerPreviewView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], 174 | // checker view 175 | [self.colorPickerBackgroundView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], 176 | [self.colorPickerBackgroundView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], 177 | [self.colorPickerBackgroundView.topAnchor constraintEqualToAnchor:self.view.topAnchor], 178 | [self.colorPickerBackgroundView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], 179 | // red slider 180 | [self.colorPickerRedSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 181 | [self.colorPickerRedSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 182 | [self.colorPickerRedSlider.topAnchor constraintEqualToAnchor:self.view.layoutMarginsGuide.topAnchor], 183 | [self.colorPickerRedSlider.heightAnchor constraintEqualToConstant:SLIDER_HEIGHT], 184 | // green slider 185 | [self.colorPickerGreenSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 186 | [self.colorPickerGreenSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 187 | [self.colorPickerGreenSlider.topAnchor constraintEqualToAnchor:self.colorPickerRedSlider.bottomAnchor], 188 | [self.colorPickerGreenSlider.heightAnchor constraintEqualToConstant:SLIDER_HEIGHT], 189 | // blue slider 190 | [self.colorPickerBlueSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 191 | [self.colorPickerBlueSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 192 | [self.colorPickerBlueSlider.topAnchor constraintEqualToAnchor:self.colorPickerGreenSlider.bottomAnchor], 193 | [self.colorPickerBlueSlider.heightAnchor constraintEqualToConstant:SLIDER_HEIGHT], 194 | // alpha slider 195 | [self.colorPickerAlphaSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 196 | [self.colorPickerAlphaSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 197 | [self.colorPickerAlphaSlider.bottomAnchor constraintEqualToAnchor:self.colorPickerHueSlider.topAnchor], 198 | [self.colorPickerAlphaSlider.heightAnchor constraintEqualToConstant:self.alphaEnabled ? SLIDER_HEIGHT : 0], 199 | // hue slider 200 | [self.colorPickerHueSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 201 | [self.colorPickerHueSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 202 | [self.colorPickerHueSlider.bottomAnchor constraintEqualToAnchor:self.colorPickerSaturationSlider.topAnchor], 203 | [self.colorPickerHueSlider.heightAnchor constraintEqualToConstant:SLIDER_HEIGHT], 204 | // saturation slider 205 | [self.colorPickerSaturationSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 206 | [self.colorPickerSaturationSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 207 | [self.colorPickerSaturationSlider.bottomAnchor constraintEqualToAnchor:self.colorPickerBrightnessSlider.topAnchor], 208 | [self.colorPickerSaturationSlider.heightAnchor constraintEqualToConstant:SLIDER_HEIGHT], 209 | // brightness slider 210 | [self.colorPickerBrightnessSlider.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 211 | [self.colorPickerBrightnessSlider.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 212 | [self.colorPickerBrightnessSlider.bottomAnchor constraintEqualToAnchor:self.colorPickerContainerView.layoutMarginsGuide.bottomAnchor], 213 | [self.colorPickerBrightnessSlider.heightAnchor constraintEqualToConstant:SLIDER_HEIGHT], 214 | // gradient selection 215 | [self.gradientSelection.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 216 | [self.gradientSelection.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 217 | [self.gradientSelection.topAnchor constraintEqualToAnchor:self.colorPickerBlueSlider.bottomAnchor], 218 | [self.gradientSelection.heightAnchor constraintEqualToConstant:self.isGradient ? GRADIENT_HEIGHT : 0], 219 | // info label 220 | [self.colorInformationLable.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 221 | [self.colorInformationLable.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 222 | [self.colorInformationLable.topAnchor constraintEqualToAnchor:self.gradientSelection.bottomAnchor], 223 | [self.colorInformationLable.bottomAnchor constraintEqualToAnchor:self.colorPickerAlphaSlider.topAnchor], 224 | // top backdrop 225 | [self.topBackdrop.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 226 | [self.topBackdrop.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 227 | [self.topBackdrop.topAnchor constraintEqualToAnchor:self.colorPickerContainerView.topAnchor], 228 | [self.topBackdrop.bottomAnchor constraintEqualToAnchor:self.gradientSelection.bottomAnchor], 229 | // bottom backdrop 230 | [self.bottomBackdrop.leadingAnchor constraintEqualToAnchor:self.colorPickerContainerView.leadingAnchor], 231 | [self.bottomBackdrop.trailingAnchor constraintEqualToAnchor:self.colorPickerContainerView.trailingAnchor], 232 | [self.bottomBackdrop.topAnchor constraintEqualToAnchor:self.colorPickerAlphaSlider.topAnchor], 233 | [self.bottomBackdrop.bottomAnchor constraintEqualToAnchor:self.colorPickerContainerView.bottomAnchor], 234 | ]]; 235 | } 236 | 237 | - (CGRect)calculatedBounds { 238 | UIEdgeInsets insets = UIEdgeInsetsZero; 239 | if ([self.view respondsToSelector:@selector(safeAreaInsets)]) { 240 | #pragma clang diagnostic push 241 | #pragma clang diagnostic ignored "-Wunguarded-availability-new" 242 | insets = [self.view safeAreaInsets]; 243 | #pragma clang diagnostic pop 244 | } 245 | 246 | return UIEdgeInsetsInsetRect(self.view.bounds, insets); 247 | } 248 | 249 | - (void)sliderDidChange:(CSColorSlider *)sender { 250 | UIColor *color = (sender.sliderType > 2) ? [self colorForRGBSliders] : [self colorForHSBSliders]; 251 | [self updateColor:color animated:NO]; 252 | } 253 | 254 | - (UIColor *)colorForHSBSliders { 255 | return [UIColor colorWithHue:self.colorPickerHueSlider.value 256 | saturation:self.colorPickerSaturationSlider.value 257 | brightness:self.colorPickerBrightnessSlider.value 258 | alpha:self.colorPickerAlphaSlider.value]; 259 | } 260 | 261 | - (UIColor *)colorForRGBSliders { 262 | return [UIColor colorWithRed:self.colorPickerRedSlider.value 263 | green:self.colorPickerGreenSlider.value 264 | blue:self.colorPickerBlueSlider.value 265 | alpha:self.colorPickerAlphaSlider.value]; 266 | } 267 | 268 | - (void)updateColor:(UIColor *)color animated:(BOOL)animated{ 269 | [self setColor:color animated:animated]; 270 | if (self.isGradient) { 271 | self.colors[self.selectedIndex] = color; 272 | [self.gradientSelection setColor:color atIndex:self.selectedIndex]; 273 | } 274 | } 275 | 276 | - (void)setColor:(UIColor *)color animated:(BOOL)animated{ 277 | void (^update)(void) = ^void(void) { 278 | [self.colorPickerAlphaSlider setColor:color]; 279 | [self.colorPickerHueSlider setColor:color]; 280 | [self.colorPickerSaturationSlider setColor:color]; 281 | [self.colorPickerBrightnessSlider setColor:color]; 282 | [self.colorPickerRedSlider setColor:color]; 283 | [self.colorPickerGreenSlider setColor:color]; 284 | [self.colorPickerBlueSlider setColor:color]; 285 | self.colorPickerPreviewView.backgroundColor = color; 286 | 287 | [self setColorInformationTextWithInformationFromColor:color]; 288 | }; 289 | 290 | if (animated) 291 | [UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ update(); } completion:nil]; 292 | else 293 | update(); 294 | } 295 | 296 | - (void)setColorInformationTextWithInformationFromColor:(UIColor *)color { 297 | [self.colorInformationLable setText:[self informationStringForColor:color]]; 298 | UIColor *legibilityTint = (!color.cscp_light && color.cscp_alpha > 0.5) ? UIColor.whiteColor : UIColor.blackColor; 299 | UIColor *shadowColor = legibilityTint == UIColor.blackColor ? UIColor.whiteColor : UIColor.blackColor; 300 | 301 | [self.colorInformationLable setTextColor:legibilityTint]; 302 | [self.colorInformationLable.layer setShadowColor:[shadowColor CGColor]]; 303 | [self.colorInformationLable setFont:[UIFont boldSystemFontOfSize:[self isLandscape] ? 16 : 20]]; 304 | } 305 | 306 | - (UIColor *)startColor { 307 | return self.isGradient ? self.colors.lastObject : [self.specifier propertyForKey:@"color"]; 308 | } 309 | 310 | - (void)saveColor { 311 | NSString *saveValue = nil; 312 | if (self.isGradient) { 313 | for (UIColor *color in self.colors) { 314 | saveValue = saveValue ? [saveValue stringByAppendingFormat:@",%@", color.cscp_hexStringWithAlpha] : [NSString stringWithFormat:@"%@", color.cscp_hexStringWithAlpha]; 315 | } 316 | } else { 317 | saveValue = [self colorForRGBSliders].cscp_hexStringWithAlpha; 318 | } 319 | 320 | NSString *key = [self.specifier propertyForKey:@"key"]; 321 | NSString *defaults = [self.specifier propertyForKey:@"defaults"]; 322 | 323 | NSString *plistPath = [NSString stringWithFormat:@"/User/Library/Preferences/%@.plist", defaults]; 324 | NSMutableDictionary *prefsDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath] ? : [NSMutableDictionary new]; 325 | UITableViewCell *cell = [self.specifier propertyForKey:@"cellObject"]; 326 | 327 | // save via plist 328 | [prefsDict setObject:saveValue forKey:key]; 329 | [prefsDict writeToFile:plistPath atomically:NO]; 330 | 331 | // save in CFPreferences 332 | CFPreferencesSetValue((__bridge CFStringRef)key, (__bridge CFPropertyListRef)saveValue, (__bridge CFStringRef)defaults, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 333 | CFPreferencesSynchronize((__bridge CFStringRef)defaults, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 334 | 335 | // save in domain for NSUserDefaults 336 | [[NSUserDefaults standardUserDefaults] setObject:saveValue forKey:key inDomain:defaults]; 337 | 338 | if (cell && [cell isKindOfClass:[CSColorDisplayCell class]]) 339 | [(CSColorDisplayCell *)cell refreshCellWithColor:[self colorForRGBSliders]]; 340 | else if (cell && [cell isKindOfClass:[CSGradientDisplayCell class]]) 341 | [(CSGradientDisplayCell *)cell refreshCellWithColors:self.colors]; 342 | 343 | if ([self.specifier propertyForKey:@"PostNotification"]) 344 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), 345 | (CFStringRef)[self.specifier propertyForKey:@"PostNotification"], 346 | (CFStringRef)[self.specifier propertyForKey:@"PostNotification"], 347 | NULL, 348 | YES); 349 | 350 | if ([self.specifier propertyForKey:@"callbackAction"]) { 351 | SEL callback = NSSelectorFromString([self.specifier propertyForKey:@"callbackAction"]); 352 | if ([self.specifier.target respondsToSelector:callback]) { 353 | ((void (*)(id, SEL))[self.specifier.target methodForSelector:callback])(self.specifier.target, callback); 354 | } 355 | } 356 | } 357 | 358 | - (void)addAction:(UIButton *)sender { 359 | UIColor *color = [self complementaryColorForColor:self.colors.lastObject]; 360 | [self.colors addObject:color]; 361 | [self.gradientSelection addColor:color]; 362 | [self setColor:self.colors.lastObject animated:YES]; 363 | self.selectedIndex = self.colors.count - 1; 364 | } 365 | 366 | - (void)removeAction:(UIButton *)sender { 367 | [self.colors removeObjectAtIndex:self.selectedIndex]; 368 | [self.gradientSelection removeColorAtIndex:self.selectedIndex]; 369 | [self setColor:self.colors.lastObject animated:YES]; 370 | self.selectedIndex = self.colors.count - 1; 371 | } 372 | 373 | - (void)selectAction:(UIButton *)sender { 374 | [self setColor:self.colors[sender.titleLabel.tag] animated:YES]; 375 | self.selectedIndex = sender.titleLabel.tag; 376 | } 377 | 378 | 379 | // well this is ugly 380 | - (NSString *)informationStringForColor:(UIColor *)color { 381 | CGFloat h, s, b, a, r, g, bb, aa; 382 | [color getHue:&h saturation:&s brightness:&b alpha:&a]; 383 | [color getRed:&r green:&g blue:&bb alpha:&aa]; 384 | if (self.view.bounds.size.width > self.view.bounds.size.height) { 385 | if (self.alphaEnabled) { 386 | return [NSString stringWithFormat:@"#%@\n\nR: %.f H: %.f\nG: %.f S: %.f\nB: %.f B: %.f\nA: %.f A: %.f", [color cscp_hexString], r * 255, h * 360, g * 255, s * 100, bb * 255, b * 100, aa * 100, a * 100]; 387 | } 388 | return [NSString stringWithFormat:@"#%@\n\nR: %.f H: %.f\nG: %.f S: %.f\nB: %.f B: %.f", [color cscp_hexString], r * 255, h * 360, g * 255, s * 100, bb * 255, b * 100]; 389 | } else { 390 | if (self.alphaEnabled) { 391 | return [NSString stringWithFormat:@"#%@\n\nR: %.f\nG: %.f\nB: %.f\nA: %.f\n\nH: %.f\nS: %.f\nB: %.f\nA: %.f", [color cscp_hexString], r * 255, g * 255, bb * 255, aa * 100, h * 360, s * 100, b * 100, a * 100]; 392 | } 393 | return [NSString stringWithFormat:@"#%@\n\nR: %.f\nG: %.f\nB: %.f\n\nH: %.f\nS: %.f\nB: %.f", [color cscp_hexString], r * 255, g * 255, bb * 255, h * 360, s * 100, b * 100]; 394 | } 395 | } 396 | 397 | - (void)presentHexColorAlert { 398 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:ALERT_TITLE message:ALERT_MESSAGE preferredStyle:UIAlertControllerStyleAlert]; 399 | 400 | [alertController addTextFieldWithConfigurationHandler:^(UITextField *hexField) { 401 | hexField.text = [NSString stringWithFormat:@"#%@", [[self colorForHSBSliders] cscp_hexString]]; 402 | hexField.textColor = [UIColor respondsToSelector:@selector(labelColor)] ? [UIColor performSelector:@selector(labelColor)] : [UIColor blackColor]; 403 | hexField.clearButtonMode = UITextFieldViewModeAlways; 404 | hexField.borderStyle = firmwareGreaterThanEqual(@"13.0") ? UITextBorderStyleNone : UITextBorderStyleRoundedRect; 405 | }]; 406 | 407 | [alertController addAction:[UIAlertAction actionWithTitle:ALERT_COPY style:UIAlertActionStyleDefault handler:^(UIAlertAction *copy) { 408 | [[UIPasteboard generalPasteboard] setString:[self colorForHSBSliders].cscp_hexStringWithAlpha]; 409 | }]]; 410 | 411 | [alertController addAction:[UIAlertAction actionWithTitle:ALERT_SET style:UIAlertActionStyleDefault handler:^(UIAlertAction *set) { 412 | [self updateColor:[UIColor cscp_colorFromHexString:alertController.textFields[0].text] animated:YES]; 413 | }]]; 414 | [alertController addAction:[UIAlertAction actionWithTitle:ALERT_PASTEBOARD style:UIAlertActionStyleDefault handler:^(UIAlertAction *set) { 415 | [self updateColor:[UIColor cscp_colorFromHexString:[UIPasteboard generalPasteboard].string] animated:YES]; 416 | }]]; 417 | 418 | [alertController addAction:[UIAlertAction actionWithTitle:ALERT_CANCEL style:UIAlertActionStyleCancel handler:nil]]; 419 | 420 | 421 | [self presentViewController:alertController animated:YES completion:nil]; 422 | } 423 | 424 | - (CGFloat)navigationHeight { 425 | return [self isLandscape] ? self.navigationController.navigationBar.frame.size.height : 426 | self.navigationController.navigationBar.frame.size.height + UIApplication.sharedApplication.statusBarFrame.size.height; 427 | } 428 | 429 | - (BOOL)isLandscape { 430 | return UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication.statusBarOrientation); 431 | } 432 | 433 | - (UIColor *)complementaryColorForColor:(UIColor *)color { 434 | CGFloat h, s, b, a; 435 | if ([color getHue:&h saturation:&s brightness:&b alpha:&a]) { 436 | if (s < 0.25) { 437 | b = ((int)((b * 100) + 10) % 100) / 100.0f; 438 | } 439 | 440 | else { 441 | h = h - 0.1; 442 | if (h < 0) h = 1.0f - fabs(h); 443 | } 444 | 445 | return [UIColor colorWithHue:h saturation:s brightness:b alpha:a]; 446 | } 447 | return nil; 448 | } 449 | 450 | - (void)dismiss { 451 | [self dismissViewControllerAnimated:YES completion:nil]; 452 | } 453 | 454 | @end 455 | -------------------------------------------------------------------------------- /source/Controls/CSColorSlider.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | enum { 9 | CSColorSliderTypeHue = 0, 10 | CSColorSliderTypeSaturation = 1, 11 | CSColorSliderTypeBrightness = 2, 12 | CSColorSliderTypeRed = 3, 13 | CSColorSliderTypeGreen = 4, 14 | CSColorSliderTypeBlue = 5, 15 | CSColorSliderTypeAlpha = 6 16 | }; 17 | typedef NSUInteger CSColorSliderType; 18 | 19 | @interface CSColorSlider : UISlider 20 | 21 | @property (nonatomic, strong) UIColor *color; 22 | @property (nonatomic, strong) UIColor *maxColor; 23 | @property (nonatomic, strong) UIColor *selectedColor; 24 | 25 | @property (nonatomic, strong) UILabel *sliderLabel; 26 | @property (nonatomic, assign) CSColorSliderType sliderType; 27 | 28 | @property (nonatomic, assign) NSUInteger colorTrackHeight; 29 | 30 | - (instancetype)initWithFrame:(CGRect)frame sliderType:(CSColorSliderType)sliderType label:(NSString *)label startColor:(UIColor *)startColor; 31 | - (void)updateTrackImage; 32 | @end 33 | -------------------------------------------------------------------------------- /source/Controls/CSColorSlider.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | @interface CSColorSlider () 10 | 11 | 12 | @property (nonatomic, strong) UIImageView *colorTrackImageView; 13 | 14 | @property (nonatomic, strong) UILabel *sliderValueLabel; 15 | 16 | @property (nonatomic, strong) UIImage *currentTrackImage; 17 | 18 | @end 19 | 20 | @implementation CSColorSlider 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame sliderType:(CSColorSliderType)sliderType label:(NSString *)label startColor:(UIColor *)startColor { 23 | self = [super initWithFrame:frame]; 24 | if (self) { 25 | [self baseInitWithType:sliderType label:label startColor:startColor]; 26 | } 27 | return self; 28 | } 29 | 30 | - (instancetype)initWithCoder:(NSCoder *)coder { 31 | self = [super initWithCoder:coder]; 32 | if (self) { 33 | [self baseInitWithType:0 label:@"" startColor:[UIColor whiteColor]]; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)baseInitWithType:(CSColorSliderType)sliderType label:(NSString *)label startColor:(UIColor *)startColor { 39 | 40 | _colorTrackImageView = [UIImageView new]; 41 | [self addSubview:_colorTrackImageView]; 42 | [self sendSubviewToBack:_colorTrackImageView]; 43 | 44 | // set clear track images to set margins on either side for labels 45 | UIImage *sliderValueImageRight = [self imageWithColor:[UIColor clearColor] size:CGSizeMake(29, 1)]; 46 | UIImage *sliderValueImageLeft = [self imageWithColor:[UIColor clearColor] size:CGSizeMake(29, 1)]; 47 | 48 | [self setMaximumValueImage:sliderValueImageRight]; 49 | [self setMinimumValueImage:sliderValueImageLeft]; 50 | 51 | // set thumb image 52 | [self setThumbImage:[self imageWithColor:[UIColor lightGrayColor] size:CGSizeMake(5, 30)] forState:UIControlStateNormal]; 53 | 54 | // set min/max thumb images for label margins 55 | [super setMinimumTrackImage:[self imageWithColor:[UIColor clearColor] size:CGSizeMake(1, 1)] forState:UIControlStateNormal]; 56 | [super setMaximumTrackImage:[self imageWithColor:[UIColor clearColor] size:CGSizeMake(1, 1)] forState:UIControlStateNormal]; 57 | 58 | // set the slider label 59 | self.sliderLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 60 | [self.sliderLabel setNumberOfLines:1]; 61 | [self.sliderLabel setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:16]]; 62 | [self.sliderLabel setText:label]; 63 | [self.sliderLabel setBackgroundColor:[UIColor clearColor]]; 64 | [self.sliderLabel setTextColor:[UIColor respondsToSelector:@selector(labelColor)] ? [UIColor performSelector:@selector(labelColor)] : [UIColor blackColor]]; 65 | [self.sliderLabel setTextAlignment:NSTextAlignmentLeft]; 66 | [self insertSubview:self.sliderLabel atIndex:0]; 67 | [self.sliderLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; 68 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.sliderLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:0.5 constant:0]]; 69 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.sliderLabel attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:14.5]]; 70 | 71 | // set the value slider 72 | self.sliderValueLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 73 | [self.sliderValueLabel setNumberOfLines:1]; 74 | [self.sliderValueLabel setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:16]]; 75 | [self.sliderValueLabel setText:@"0"]; 76 | [self.sliderValueLabel setBackgroundColor:[UIColor clearColor]]; 77 | [self.sliderValueLabel setTextColor:[UIColor respondsToSelector:@selector(labelColor)] ? [UIColor performSelector:@selector(labelColor)] : [UIColor blackColor]]; 78 | [self.sliderValueLabel setTextAlignment:NSTextAlignmentRight]; 79 | [self insertSubview:self.sliderValueLabel atIndex:0]; 80 | [self.sliderValueLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; 81 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.sliderValueLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:0.5 constant:0]]; 82 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.sliderValueLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:0.98 constant:0]]; 83 | 84 | self.sliderType = sliderType; 85 | self.selectedColor = startColor; 86 | 87 | _colorTrackHeight = (sliderType <= 2) ? 20 : (sliderType > 5) ? 20 : 2; 88 | [self updateTrackImage]; 89 | [self setColor:startColor]; 90 | 91 | // fix eclipse coloring tracks 92 | UIImageView *minTrack = [self respondsToSelector:@selector(_minTrackView)] ? [self performSelector:@selector(_minTrackView)] : nil; 93 | UIImageView *maxTrack = [self respondsToSelector:@selector(_maxTrackView)] ? [self performSelector:@selector(_maxTrackView)] : nil; 94 | if (minTrack) { 95 | minTrack.hidden = YES; 96 | minTrack.tag = 199; 97 | } 98 | 99 | if (maxTrack) { 100 | maxTrack.hidden = YES; 101 | maxTrack.tag = 199; 102 | } 103 | } 104 | 105 | - (void)layoutSubviews { 106 | [super layoutSubviews]; 107 | 108 | self.colorTrackImageView.frame = [self trackRectForBounds:self.bounds]; 109 | CGPoint center = self.colorTrackImageView.center; 110 | CGRect rect = self.colorTrackImageView.frame; 111 | rect.size.height = self.colorTrackHeight; 112 | self.colorTrackImageView.frame = rect; 113 | self.colorTrackImageView.center = center; 114 | 115 | } 116 | 117 | #pragma mark UISlider Implementation 118 | 119 | - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 120 | BOOL tracking = [super beginTrackingWithTouch:touch withEvent:event]; 121 | 122 | if (self.sliderValueLabel) { 123 | [self updateValueLabel]; 124 | } 125 | return tracking; 126 | } 127 | 128 | - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 129 | BOOL con = [super continueTrackingWithTouch:touch withEvent:event]; 130 | 131 | if (self.sliderValueLabel) { 132 | [self updateValueLabel]; 133 | } 134 | return con; 135 | } 136 | 137 | - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { 138 | [super endTrackingWithTouch:touch withEvent:event]; 139 | 140 | if (self.sliderValueLabel) { 141 | [self updateValueLabel]; 142 | } 143 | } 144 | 145 | - (void)cancelTrackingWithEvent:(UIEvent *)event { 146 | [super cancelTrackingWithEvent:event]; 147 | 148 | if (self.sliderValueLabel) { 149 | [self updateValueLabel]; 150 | } 151 | } 152 | 153 | - (void)setMinimumTrackImage:(UIImage *)image forState:(UIControlState)state {} 154 | - (void)setMaximumTrackImage:(UIImage *)image forState:(UIControlState)state {} 155 | 156 | #pragma mark - Color Methods 157 | 158 | - (UIColor *)colorFromCurrentValue { 159 | switch (self.sliderType) { 160 | case CSColorSliderTypeHue: { 161 | return [UIColor colorWithHue:self.value saturation:1 brightness:1.0 alpha:1.0]; 162 | } 163 | case CSColorSliderTypeSaturation: { 164 | return [UIColor colorWithHue:1.0 saturation:self.value brightness:1.0 alpha:1.0]; 165 | } 166 | case CSColorSliderTypeBrightness: { 167 | return [UIColor colorWithHue:1.0 saturation:1.0 brightness:self.value alpha:1.0]; 168 | } 169 | case CSColorSliderTypeRed: { 170 | return [UIColor colorWithRed:self.value green:1.0 blue:1.0 alpha:1.0]; 171 | } 172 | case CSColorSliderTypeGreen: { 173 | return [UIColor colorWithRed:1.0 green:self.value blue:1.0 alpha:1.0]; 174 | } 175 | case CSColorSliderTypeBlue: { 176 | return [UIColor colorWithRed:1.0 green:1.0 blue:self.value alpha:1.0]; 177 | } 178 | case CSColorSliderTypeAlpha: { 179 | return [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:self.value]; 180 | } 181 | default: { 182 | return [UIColor colorWithHue:self.value saturation:1 brightness:1.0 alpha:1.0]; 183 | } 184 | } 185 | } 186 | 187 | - (UIColor *)color { 188 | return [self colorFromCurrentValue]; 189 | } 190 | 191 | - (void)setColor:(UIColor *)color { 192 | 193 | CGFloat value; 194 | 195 | switch (self.sliderType) { 196 | case CSColorSliderTypeHue: { 197 | [color getHue:&value saturation:nil brightness:nil alpha:nil]; 198 | } break; 199 | case CSColorSliderTypeSaturation: { 200 | [color getHue:nil saturation:&value brightness:nil alpha:nil]; 201 | } break; 202 | case CSColorSliderTypeBrightness: { 203 | [color getHue:nil saturation:nil brightness:&value alpha:nil]; 204 | } break; 205 | case CSColorSliderTypeRed: { 206 | [color getRed:&value green:nil blue:nil alpha:nil]; 207 | } break; 208 | case CSColorSliderTypeGreen: { 209 | [color getRed:nil green:&value blue:nil alpha:nil]; 210 | } break; 211 | case CSColorSliderTypeBlue: { 212 | [color getRed:nil green:nil blue:&value alpha:nil]; 213 | } break; 214 | case CSColorSliderTypeAlpha: { 215 | [color getWhite:nil alpha:&value]; 216 | } break; 217 | default: { 218 | [color getRed:&value green:nil blue:nil alpha:nil]; 219 | } break; 220 | } 221 | 222 | [self setValue:value animated:YES]; 223 | self.selectedColor = color; 224 | [self updateTrackImage]; 225 | [self updateValueLabel]; 226 | } 227 | 228 | - (UIColor *)getMaxColor { 229 | switch (self.sliderType) { 230 | case CSColorSliderTypeBrightness: { 231 | CGFloat h,s,b = 1,a; 232 | [self.selectedColor getHue:&h saturation:&s brightness:nil alpha:&a]; 233 | return [UIColor colorWithHue:h saturation:s brightness:b alpha:a]; 234 | } 235 | case CSColorSliderTypeSaturation: { 236 | CGFloat h,s = 1,b,a; 237 | [self.selectedColor getHue:&h saturation:nil brightness:&b alpha:&a]; 238 | return [UIColor colorWithHue:h saturation:s brightness:b alpha:a]; 239 | } 240 | case CSColorSliderTypeAlpha: { 241 | CGFloat h,s,b,a = 1; 242 | [self.selectedColor getHue:&h saturation:&s brightness:&b alpha:nil]; 243 | return [UIColor colorWithHue:h saturation:s brightness:b alpha:a]; 244 | } 245 | default: { 246 | return self.selectedColor; 247 | } 248 | } 249 | } 250 | 251 | - (float)colorMaxValue { 252 | switch (self.sliderType) { 253 | case CSColorSliderTypeHue: { 254 | return 360; 255 | } 256 | case CSColorSliderTypeSaturation: 257 | case CSColorSliderTypeBrightness: 258 | case CSColorSliderTypeAlpha: { 259 | return 100; 260 | } 261 | case CSColorSliderTypeRed: 262 | case CSColorSliderTypeGreen: 263 | case CSColorSliderTypeBlue: { 264 | return 255; 265 | } 266 | default: { 267 | return 1; 268 | } 269 | } 270 | } 271 | 272 | #pragma mark - Update Views 273 | 274 | - (void)updateValueLabel { 275 | [self.sliderValueLabel setText:[NSString stringWithFormat:@"%.f", self.value * [self colorMaxValue]]]; 276 | } 277 | 278 | - (void)updateTrackImage { 279 | switch (self.sliderType) { 280 | case CSColorSliderTypeHue: { 281 | self.currentTrackImage = [self hueTrackImage]; 282 | } break; 283 | case CSColorSliderTypeSaturation: { 284 | UIColor *maxColor = [self getMaxColor]; 285 | BOOL maxChanged = (self.maxColor != maxColor); 286 | if ((self.maxColor = maxColor) && (!self.currentTrackImage || maxChanged)) self.currentTrackImage = [self imageWithGradientStart:[UIColor whiteColor] end:self.maxColor size:CGSizeMake(512, 1)]; 287 | } break; 288 | case CSColorSliderTypeBrightness: { 289 | UIColor *maxColor = [self getMaxColor]; 290 | BOOL maxChanged = (self.maxColor != maxColor); 291 | if ((self.maxColor = maxColor) && (!self.currentTrackImage || maxChanged)) self.currentTrackImage = [self imageWithGradientStart:[UIColor blackColor] end:self.maxColor size:CGSizeMake(512, 1)]; 292 | } break; 293 | case CSColorSliderTypeAlpha: { 294 | UIColor *maxColor = [self getMaxColor]; 295 | BOOL maxChanged = (self.maxColor != maxColor); 296 | if ((self.maxColor = maxColor) && (!self.currentTrackImage || maxChanged)) self.currentTrackImage = [self imageWithGradientStart:[UIColor clearColor] end:self.maxColor size:CGSizeMake(512, 1)]; 297 | } break; 298 | case CSColorSliderTypeRed: { 299 | if (!self.currentTrackImage) self.currentTrackImage = [self imageWithColor:[UIColor redColor] size:CGSizeMake(1, 1)]; 300 | } break; 301 | case CSColorSliderTypeGreen: { 302 | if (!self.currentTrackImage) self.currentTrackImage = [self imageWithColor:[UIColor greenColor] size:CGSizeMake(1, 1)]; 303 | } break; 304 | case CSColorSliderTypeBlue: { 305 | if (!self.currentTrackImage) self.currentTrackImage = [self imageWithColor:[UIColor blueColor] size:CGSizeMake(1, 1)]; 306 | } break; 307 | default: { 308 | if (!self.currentTrackImage) self.currentTrackImage = [self imageWithColor:[UIColor redColor] size:CGSizeMake(1, 1)]; 309 | } break; 310 | } 311 | 312 | [self.colorTrackImageView setImage:self.currentTrackImage]; 313 | } 314 | 315 | - (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size { 316 | CGRect rect = CGRectMake(0, 0, size.width, size.height); 317 | 318 | UIGraphicsBeginImageContext(rect.size); 319 | CGContextRef context = UIGraphicsGetCurrentContext(); 320 | CGContextSetFillColorWithColor(context, [color CGColor]); 321 | CGContextFillRect(context, rect); 322 | 323 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 324 | UIGraphicsEndImageContext(); 325 | 326 | return img; 327 | } 328 | 329 | - (UIImage *)imageWithGradientStart:(UIColor *)start end:(UIColor *)end size:(CGSize)size { 330 | CGRect rect = CGRectMake(0, 0, size.width, size.height); 331 | 332 | //make gradient 333 | CAGradientLayer *gradient = [CAGradientLayer layer]; 334 | gradient.frame = rect; 335 | gradient.startPoint = CGPointMake(0, 0.5); 336 | gradient.endPoint = CGPointMake(1, 0.5); 337 | gradient.colors = @[(id)start.CGColor, (id)end.CGColor]; 338 | 339 | UIGraphicsBeginImageContext(rect.size); 340 | CGContextRef context = UIGraphicsGetCurrentContext(); 341 | CGContextFillRect(context, rect); 342 | [gradient renderInContext:UIGraphicsGetCurrentContext()]; 343 | 344 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 345 | UIGraphicsEndImageContext(); 346 | 347 | return img; 348 | } 349 | 350 | - (UIImage *)hueTrackImage { 351 | CGRect rect = CGRectMake(0, 0, 512, 1); 352 | 353 | NSMutableArray *colors = [NSMutableArray array]; 354 | for (NSInteger deg = 0; deg <= 360; deg += 5) { 355 | 356 | UIColor *color; 357 | color = [UIColor colorWithHue:1.0f * deg / 360.0f 358 | saturation:1.0f 359 | brightness:1.0f 360 | alpha:1.0f]; 361 | [colors addObject:(__bridge id)[color CGColor]]; 362 | } 363 | 364 | //make gradient 365 | CAGradientLayer *gradient = [CAGradientLayer layer]; 366 | gradient.frame = rect; 367 | gradient.startPoint = CGPointMake(0, 0.5); 368 | gradient.endPoint = CGPointMake(1, 0.5); 369 | gradient.colors = colors; 370 | 371 | UIGraphicsBeginImageContext(rect.size); 372 | 373 | [gradient renderInContext:UIGraphicsGetCurrentContext()]; 374 | 375 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 376 | UIGraphicsEndImageContext(); 377 | 378 | return img; 379 | } 380 | 381 | @end 382 | -------------------------------------------------------------------------------- /source/Headers/PSSpecifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | @interface PSSpecifier : NSObject 9 | 10 | @property (nonatomic, strong) NSString *name; 11 | @property (nonatomic, assign) id target; 12 | @property (nonatomic, assign) SEL buttonAction; 13 | 14 | - (id)propertyForKey:(NSString *)key; 15 | - (void)setProperty:(id)value forKey:(NSString *)key; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /source/Headers/PSTableCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | @interface PSTableCell : UITableViewCell 9 | 10 | @property (nonatomic, retain) PSSpecifier *specifier; 11 | 12 | - (void)refreshCellContentsWithSpecifier:(PSSpecifier *)specifier; 13 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)identifier specifier:(PSSpecifier *)specifier; 14 | 15 | @end -------------------------------------------------------------------------------- /source/Headers/PSViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | @interface PSViewController : UIViewController 9 | 10 | @property (nonatomic, retain) PSSpecifier *specifier; 11 | 12 | - (void)setParentController:(PSViewController *)controller; 13 | - (PSViewController *)parentController; 14 | 15 | @end -------------------------------------------------------------------------------- /source/Prefix.h: -------------------------------------------------------------------------------- 1 | 2 | // get the associated view controller from a UIView 3 | // credits https://stackoverflow.com/questions/1372977/given-a-view-how-do-i-get-its-viewcontroller/24590678 4 | #define UIViewParentController(__view) ({ UIResponder *__responder = __view; while ([__responder isKindOfClass:[UIView class]]) __responder = [__responder nextResponder]; (UIViewController *)__responder; }) 5 | 6 | #ifdef DEBUG 7 | #define CSLog(format, ...) NSLog((@"(*** libCSColorPicker ***)%s [LOG Line %d] " format), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 8 | #define CSInfo(format, ...) NSLog((@"(*** libCSColorPicker ***)%s [INFO Line %d] " format), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 9 | #define CSError(format, ...) NSLog((@"(*** libCSColorPicker ***)%s [ERROR Line %d] " format), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 10 | #define CSWarn(format, ...) NSLog((@"(*** libCSColorPicker ***)%s [WARN Line %d] " format), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 11 | #else 12 | #define CSLog(format, ...) 13 | #define CSInfo(format, ...) 14 | #define CSError(format, ...) 15 | #define CSWarn(format, ...) 16 | #endif 17 | 18 | NS_INLINE BOOL firmwareGreaterThanEqual(NSString *version) { 19 | return [[[UIDevice currentDevice] systemVersion] floatValue] >= [version floatValue]; 20 | } 21 | 22 | NS_INLINE BOOL firmwareGreaterThan(NSString *version) { 23 | return [[[UIDevice currentDevice] systemVersion] floatValue] > [version floatValue]; 24 | } 25 | 26 | NS_INLINE BOOL firmwareLessThanEqual(NSString *version) { 27 | return [[[UIDevice currentDevice] systemVersion] floatValue] <= [version floatValue]; 28 | } 29 | 30 | NS_INLINE BOOL firmwareLessThan(NSString *version) { 31 | return [[[UIDevice currentDevice] systemVersion] floatValue] < [version floatValue]; 32 | } 33 | 34 | NS_INLINE BOOL firmwareEqual(NSString *version) { 35 | return [[[UIDevice currentDevice] systemVersion] floatValue] == [version floatValue]; 36 | } 37 | 38 | NS_INLINE __unused UINavigationController *NAVIGATION_WRAPPER_WITH_CONTROLLER(UIViewController *controller) { 39 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:controller]; 40 | return navigationController; 41 | } 42 | 43 | NS_INLINE __unused UIColor *secondaryLabelColor() { 44 | 45 | if (@available(iOS 13, *)) { 46 | return [UIColor secondaryLabelColor]; 47 | } 48 | 49 | return [UIColor lightGrayColor]; 50 | } -------------------------------------------------------------------------------- /source/Views/CSColorPickerBackgroundView.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | @interface CSColorPickerBackgroundView : UIView 10 | @property (nonatomic, assign) CGFloat gridCount; 11 | @end -------------------------------------------------------------------------------- /source/Views/CSColorPickerBackgroundView.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 3/17/17. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | // credits libColorPicker https://github.com/atomikpanda/libcolorpicker/blob/master/PFColorTransparentView.m 7 | #import 8 | 9 | @implementation CSColorPickerBackgroundView 10 | 11 | - (id)initWithFrame:(CGRect)frame { 12 | if ((self = [super initWithFrame:frame])) { 13 | self.gridCount = 10; 14 | self.tag = 199; 15 | } 16 | return self; 17 | } 18 | 19 | - (void)drawRect:(CGRect)rect { 20 | int scale = rect.size.width / 25; 21 | NSArray *colors = @[[UIColor whiteColor], [UIColor grayColor]]; 22 | 23 | for (int row = 0; row < rect.size.height; row += scale) { 24 | 25 | int index = row % (scale * 2) == 0 ? 0 : 1; 26 | 27 | for (int column = 0; column < rect.size.width; column += scale) { 28 | 29 | [[colors objectAtIndex:index++ % 2] setFill]; 30 | 31 | UIRectFill(CGRectMake(column, row, scale, scale)); 32 | } 33 | } 34 | } 35 | 36 | @end -------------------------------------------------------------------------------- /source/Views/CSGradientSelection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 4/7/19. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | @interface CSGradientSelection : UIView 9 | @property(nonatomic, weak, readonly) id target; 10 | @property(nonatomic, assign, readonly) SEL addAction; 11 | @property(nonatomic, assign, readonly) SEL selectAction; 12 | @property(nonatomic, assign, readonly) SEL removeAction; 13 | @property(nonatomic, retain, readonly) NSMutableArray *colors; 14 | @property(nonatomic, retain, readonly) NSMutableArray *buttons; 15 | @property(nonatomic, retain, readonly) CAGradientLayer *gradient; 16 | 17 | - (instancetype)initWithSize:(CGSize)size target:(id)target addAction:(SEL)add removeAction:(SEL)remove selectAction:(SEL)select; 18 | 19 | - (void)addColor:(UIColor *)color; 20 | - (void)addColors:(NSArray *)colors; 21 | - (void)removeColorAtIndex:(NSInteger)index; 22 | - (void)setColor:(UIColor *)color atIndex:(NSInteger)index; 23 | 24 | @end -------------------------------------------------------------------------------- /source/Views/CSGradientSelection.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by CreatureSurvive on 4/7/19. 3 | // Copyright (c) 2016 - 2019 CreatureCoding. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | #import 9 | 10 | @implementation CSGradientSelection { 11 | UIScrollView *_scrollView; 12 | CGFloat _height; 13 | CGFloat _spacing; 14 | CGFloat _buttonPadding; 15 | } 16 | 17 | - (instancetype)initWithSize:(CGSize)size target:(id)target addAction:(SEL)add removeAction:(SEL)remove selectAction:(SEL)select { 18 | if ((self = [super initWithFrame:CGRectMake(0, 0, size.width, size.height)])) { 19 | _target = target; 20 | _addAction = add; 21 | _selectAction = select; 22 | _removeAction = remove; 23 | [self commonInit]; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | - (void)commonInit { 30 | _buttonPadding = 2.5; 31 | 32 | _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)]; 33 | _scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 34 | _scrollView.showsVerticalScrollIndicator = NO; 35 | _scrollView.showsHorizontalScrollIndicator = NO; 36 | _scrollView.hidden = YES; 37 | 38 | _gradient = [CAGradientLayer layer]; 39 | _gradient.frame = CGRectMake(0, self.bounds.size.height - 15, self.bounds.size.width, 15); 40 | _gradient.colors = @[(id)UIColor.clearColor.CGColor, (id)UIColor.clearColor.CGColor]; 41 | _gradient.startPoint = CGPointMake(0, 0.5); 42 | _gradient.endPoint = CGPointMake(1, 0.5); 43 | _gradient.hidden = YES; 44 | 45 | [self.layer addSublayer:self.gradient]; 46 | 47 | [self addSubview:_scrollView]; 48 | 49 | _height = self.bounds.size.height; 50 | _spacing = 5; 51 | } 52 | 53 | - (void)layoutSubviews { 54 | [super layoutSubviews]; 55 | _height = self.bounds.size.height; 56 | _scrollView.contentSize = CGSizeMake(_scrollView.contentSize.width, _height); 57 | self.gradient.frame = CGRectMake(0, self.bounds.size.height - 15, self.bounds.size.width, 15); 58 | } 59 | 60 | - (void)setBackgroundColor:(UIColor *)color { 61 | [super setBackgroundColor:color]; 62 | _scrollView.backgroundColor = color; 63 | } 64 | 65 | - (void)generateButtons { 66 | 67 | [_scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; 68 | _buttons = [NSMutableArray new]; 69 | 70 | __block CGFloat size = 0; 71 | void (^addButton)(UIButton *) = ^void(UIButton *button) { 72 | [self->_scrollView addSubview:button]; 73 | 74 | CGRect frame = button.frame; 75 | frame.origin.x = size + self->_spacing; 76 | frame.origin.y = self->_buttonPadding; 77 | 78 | button.frame = frame; 79 | size += button.bounds.size.width + self->_spacing; 80 | }; 81 | 82 | if ((self.colors.count)) { 83 | [self.colors enumerateObjectsUsingBlock:^(UIColor *color, NSUInteger index, BOOL *stop) { 84 | UIButton *button = [self accessoryButtonWithColor:color index:index]; 85 | [_buttons addObject:button]; 86 | addButton(button); 87 | }]; 88 | 89 | addButton([self.class accessoryButtonSpace]); 90 | } 91 | 92 | addButton([self.class accessoryButtonWithTitle:@"Add" target:self.target action:self.addAction color:UIColor.whiteColor index:-1]); 93 | 94 | if (self.colors.count > 2) { 95 | addButton([self.class accessoryButtonWithTitle:@"Remove" target:self.target action:self.removeAction color:UIColor.whiteColor index:-1]); 96 | } 97 | 98 | _scrollView.contentSize = CGSizeMake(size + _spacing, _height); 99 | _scrollView.hidden = NO; 100 | } 101 | 102 | - (void)updateGradient { 103 | self.gradient.hidden = _colors.count ? NO : YES; 104 | NSMutableArray *gradientColors = [NSMutableArray new]; 105 | for (UIColor *color in _colors) { 106 | [gradientColors addObject:(id)color.CGColor]; 107 | } 108 | self.gradient.colors = gradientColors; 109 | } 110 | 111 | - (void)animateOffsetAndGradient { 112 | [UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 113 | _scrollView.contentOffset = CGPointMake(_scrollView.contentSize.width > _scrollView.bounds.size.width ? _scrollView.contentSize.width - _scrollView.bounds.size.width : 0, 0); 114 | [self updateGradient]; 115 | } completion:nil]; 116 | } 117 | 118 | - (void)addColor:(UIColor *)color { 119 | if (!_colors) _colors = [NSMutableArray new]; 120 | 121 | [_colors addObject:color]; 122 | [self generateButtons]; 123 | [self animateOffsetAndGradient]; 124 | } 125 | 126 | - (void)addColors:(NSArray *)colors { 127 | if (!_colors) _colors = [NSMutableArray new]; 128 | 129 | [_colors addObjectsFromArray:colors]; 130 | [self generateButtons]; 131 | [self animateOffsetAndGradient]; 132 | } 133 | 134 | - (void)removeColorAtIndex:(NSInteger)index { 135 | if (!_colors || _colors.count < index) return; 136 | 137 | [_colors removeObjectAtIndex:index]; 138 | [self generateButtons]; 139 | [self animateOffsetAndGradient]; 140 | } 141 | 142 | - (void)setColor:(UIColor *)color atIndex:(NSInteger)index { 143 | if ((!_colors || _colors.count < index) || (!_buttons || _buttons.count < index)) return; 144 | 145 | _colors[index] = color; 146 | 147 | UIColor *titleColor = (!color.cscp_light && color.cscp_alpha > 0.5) ? UIColor.whiteColor : UIColor.blackColor; 148 | UIColor *shadowColor = titleColor == UIColor.blackColor ? UIColor.whiteColor : UIColor.blackColor; 149 | UIButton *button = _buttons[index]; 150 | [button.layer setBackgroundColor:color.CGColor]; 151 | [button.titleLabel.layer setShadowColor:shadowColor.CGColor]; 152 | [button setTitle:color.cscp_hexString forState:UIControlStateNormal]; 153 | [button setTitleColor:titleColor forState:UIControlStateNormal]; 154 | [button setTitleColor:[titleColor colorWithAlphaComponent:0.5] forState:UIControlStateHighlighted]; 155 | 156 | [self updateGradient]; 157 | } 158 | 159 | - (UIButton *)accessoryButtonWithColor:(UIColor *)color index:(NSInteger)index { 160 | return [self.class accessoryButtonWithTitle:color.cscp_hexString target:self.target action:self.selectAction color:color index:index]; 161 | } 162 | 163 | + (UIButton *)accessoryButtonWithTitle:(NSString *)title target:(id)target action:(SEL)action color:(UIColor *)color index:(NSInteger)index { 164 | UIColor *titleColor = (!color.cscp_light && color.cscp_alpha > 0.5) ? UIColor.whiteColor : UIColor.blackColor; 165 | UIColor *shadowColor = titleColor == UIColor.blackColor ? UIColor.whiteColor : UIColor.blackColor; 166 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 167 | button.contentEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5); 168 | button.layer.backgroundColor = color.CGColor; 169 | button.layer.borderColor = UIColor.lightGrayColor.CGColor; 170 | button.layer.borderWidth = 1; 171 | button.layer.cornerRadius = 9.0; 172 | button.tag = 199; 173 | 174 | [button.titleLabel.layer setShadowOffset:CGSizeZero]; 175 | [button.titleLabel.layer setShadowRadius:1]; 176 | [button.titleLabel.layer setShadowOpacity:1]; 177 | [button.titleLabel.layer setShadowColor:shadowColor.CGColor]; 178 | 179 | [button.titleLabel setTag:index]; 180 | [button.titleLabel setFont:[UIFont boldSystemFontOfSize:14]]; 181 | [button.titleLabel setAdjustsFontSizeToFitWidth:YES]; 182 | [button setTitle:@"0000000" forState:UIControlStateNormal]; 183 | [button setTitleColor:titleColor forState:UIControlStateNormal]; 184 | [button setTitleColor:[titleColor colorWithAlphaComponent:0.5] forState:UIControlStateHighlighted]; 185 | 186 | [button sizeToFit]; 187 | [button setTitle:title forState:UIControlStateNormal]; 188 | 189 | [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; 190 | 191 | return button; 192 | } 193 | 194 | + (UIButton *)accessoryButtonSpace { 195 | UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; 196 | button.contentEdgeInsets = UIEdgeInsetsMake(-3.0f, 2.0f, -3.0f, 2.0f); 197 | button.tag = 199; 198 | 199 | [button setTitle:@"" forState:UIControlStateNormal]; 200 | [button setTitleColor:[[UIColor darkTextColor] colorWithAlphaComponent:0.2] forState:UIControlStateNormal]; 201 | 202 | [button sizeToFit]; 203 | 204 | return button; 205 | } 206 | 207 | @end --------------------------------------------------------------------------------