on master help sign in
Initial commit
commited4077e9a8d0cacde53b6c004ed2d30d7a0338da authorLouis D'hauwe <louisdhauwe@silverfox.be> dateMon 2 Apr 2018, 15:09 (9 years ago)
21 files changed+1638
.editorconfig+5 .gitignore+20 .swift-version+1 HueKit.xcodeproj/project.pbxproj+397 HueKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata+7 HueKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist+8 HueKit/HueKit.h+7 HueKit/Info.plist+24 HueKit/Model/HSBComponent.swift+15 HueKit/Model/HSV.swift+97 HueKit/Model/RGB.swift+114 HueKit/Util/CGFloat+Pin.swift+25 HueKit/Util/HSBGen.swift+143 HueKit/Util/UIColor+Values.swift+57 HueKit/View/ColorBarPicker.swift+297 HueKit/View/ColorBarView.swift+36 HueKit/View/ColorIndicatorView.swift+69 HueKit/View/ColorSquarePicker.swift+170 HueKit/View/ColorSquareView.swift+36 HueKit/View/SourceColorView.swift+89 LICENSE+21
.editorconfignew file+5
@@ -0,0 +1,5 @@
1+root = true
2+
3+[*]
4+indent_style = tab
5+indent_size = 4
.gitignorenew file+20
@@ -0,0 +1,20 @@
1+# Xcode
2+#
3+.DS_Store
4+build/
5+*.pbxuser
6+!default.pbxuser
7+*.mode1v3
8+!default.mode1v3
9+*.mode2v3
10+!default.mode2v3
11+*.perspectivev3
12+!default.perspectivev3
13+xcuserdata
14+*.xccheckout
15+*.moved-aside
16+DerivedData
17+*.hmap
18+*.ipa
19+*.xcuserstate
20+
.swift-versionnew file+1
@@ -0,0 +1 @@
1+4.0
HueKit.xcodeproj/project.pbxprojnew file+397
@@ -0,0 +1,397 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 46;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ BE5C1E1C1F3266B600B0080C /* HueKit.h in Headers */ = {isa = PBXBuildFile; fileRef = BE5C1E1A1F3266B600B0080C /* HueKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
11+ BE5C1E361F326E2500B0080C /* RGB.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E351F326E2500B0080C /* RGB.swift */; };
12+ BE5C1E381F326E3300B0080C /* HSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E371F326E3300B0080C /* HSV.swift */; };
13+ BE5C1E3B1F326EE600B0080C /* UIColor+Values.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E3A1F326EE600B0080C /* UIColor+Values.swift */; };
14+ BE5C1E3D1F3270CE00B0080C /* CGFloat+Pin.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E3C1F3270CE00B0080C /* CGFloat+Pin.swift */; };
15+ BE5C1E3F1F32710B00B0080C /* HSBComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E3E1F32710B00B0080C /* HSBComponent.swift */; };
16+ BE5C1E471F3271D600B0080C /* ColorBarPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E401F3271D600B0080C /* ColorBarPicker.swift */; };
17+ BE5C1E481F3271D600B0080C /* ColorBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E411F3271D600B0080C /* ColorBarView.swift */; };
18+ BE5C1E491F3271D600B0080C /* ColorIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E421F3271D600B0080C /* ColorIndicatorView.swift */; };
19+ BE5C1E4A1F3271D600B0080C /* ColorSquarePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E431F3271D600B0080C /* ColorSquarePicker.swift */; };
20+ BE5C1E4B1F3271D600B0080C /* ColorSquareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E441F3271D600B0080C /* ColorSquareView.swift */; };
21+ BE5C1E4D1F3271D600B0080C /* SourceColorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E461F3271D600B0080C /* SourceColorView.swift */; };
22+ BE5C1E4F1F3271E900B0080C /* HSBGen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE5C1E4E1F3271E900B0080C /* HSBGen.swift */; };
23+/* End PBXBuildFile section */
24+
25+/* Begin PBXFileReference section */
26+ BE5C1E171F3266B600B0080C /* HueKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HueKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
27+ BE5C1E1A1F3266B600B0080C /* HueKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HueKit.h; sourceTree = "<group>"; };
28+ BE5C1E1B1F3266B600B0080C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
29+ BE5C1E351F326E2500B0080C /* RGB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RGB.swift; path = Model/RGB.swift; sourceTree = "<group>"; };
30+ BE5C1E371F326E3300B0080C /* HSV.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HSV.swift; path = Model/HSV.swift; sourceTree = "<group>"; };
31+ BE5C1E3A1F326EE600B0080C /* UIColor+Values.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIColor+Values.swift"; path = "Util/UIColor+Values.swift"; sourceTree = "<group>"; };
32+ BE5C1E3C1F3270CE00B0080C /* CGFloat+Pin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CGFloat+Pin.swift"; path = "Util/CGFloat+Pin.swift"; sourceTree = "<group>"; };
33+ BE5C1E3E1F32710B00B0080C /* HSBComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HSBComponent.swift; path = Model/HSBComponent.swift; sourceTree = "<group>"; };
34+ BE5C1E401F3271D600B0080C /* ColorBarPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ColorBarPicker.swift; path = View/ColorBarPicker.swift; sourceTree = "<group>"; };
35+ BE5C1E411F3271D600B0080C /* ColorBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ColorBarView.swift; path = View/ColorBarView.swift; sourceTree = "<group>"; };
36+ BE5C1E421F3271D600B0080C /* ColorIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ColorIndicatorView.swift; path = View/ColorIndicatorView.swift; sourceTree = "<group>"; };
37+ BE5C1E431F3271D600B0080C /* ColorSquarePicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ColorSquarePicker.swift; path = View/ColorSquarePicker.swift; sourceTree = "<group>"; };
38+ BE5C1E441F3271D600B0080C /* ColorSquareView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ColorSquareView.swift; path = View/ColorSquareView.swift; sourceTree = "<group>"; };
39+ BE5C1E461F3271D600B0080C /* SourceColorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SourceColorView.swift; path = View/SourceColorView.swift; sourceTree = "<group>"; };
40+ BE5C1E4E1F3271E900B0080C /* HSBGen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HSBGen.swift; path = Util/HSBGen.swift; sourceTree = "<group>"; };
41+/* End PBXFileReference section */
42+
43+/* Begin PBXFrameworksBuildPhase section */
44+ BE5C1E131F3266B600B0080C /* Frameworks */ = {
45+ isa = PBXFrameworksBuildPhase;
46+ buildActionMask = 2147483647;
47+ files = (
48+ );
49+ runOnlyForDeploymentPostprocessing = 0;
50+ };
51+/* End PBXFrameworksBuildPhase section */
52+
53+/* Begin PBXGroup section */
54+ BE5C1E0D1F3266B600B0080C = {
55+ isa = PBXGroup;
56+ children = (
57+ BE5C1E191F3266B600B0080C /* HueKit */,
58+ BE5C1E181F3266B600B0080C /* Products */,
59+ );
60+ sourceTree = "<group>";
61+ };
62+ BE5C1E181F3266B600B0080C /* Products */ = {
63+ isa = PBXGroup;
64+ children = (
65+ BE5C1E171F3266B600B0080C /* HueKit.framework */,
66+ );
67+ name = Products;
68+ sourceTree = "<group>";
69+ };
70+ BE5C1E191F3266B600B0080C /* HueKit */ = {
71+ isa = PBXGroup;
72+ children = (
73+ BE5C1E391F326E9300B0080C /* Util */,
74+ BE5C1E221F326BF500B0080C /* Model */,
75+ BE5C1E231F326BFD00B0080C /* View */,
76+ BE5C1E1A1F3266B600B0080C /* HueKit.h */,
77+ BE5C1E1B1F3266B600B0080C /* Info.plist */,
78+ );
79+ path = HueKit;
80+ sourceTree = "<group>";
81+ };
82+ BE5C1E221F326BF500B0080C /* Model */ = {
83+ isa = PBXGroup;
84+ children = (
85+ BE5C1E351F326E2500B0080C /* RGB.swift */,
86+ BE5C1E371F326E3300B0080C /* HSV.swift */,
87+ BE5C1E3E1F32710B00B0080C /* HSBComponent.swift */,
88+ );
89+ name = Model;
90+ sourceTree = "<group>";
91+ };
92+ BE5C1E231F326BFD00B0080C /* View */ = {
93+ isa = PBXGroup;
94+ children = (
95+ BE5C1E401F3271D600B0080C /* ColorBarPicker.swift */,
96+ BE5C1E411F3271D600B0080C /* ColorBarView.swift */,
97+ BE5C1E421F3271D600B0080C /* ColorIndicatorView.swift */,
98+ BE5C1E431F3271D600B0080C /* ColorSquarePicker.swift */,
99+ BE5C1E441F3271D600B0080C /* ColorSquareView.swift */,
100+ BE5C1E461F3271D600B0080C /* SourceColorView.swift */,
101+ );
102+ name = View;
103+ sourceTree = "<group>";
104+ };
105+ BE5C1E391F326E9300B0080C /* Util */ = {
106+ isa = PBXGroup;
107+ children = (
108+ BE5C1E4E1F3271E900B0080C /* HSBGen.swift */,
109+ BE5C1E3A1F326EE600B0080C /* UIColor+Values.swift */,
110+ BE5C1E3C1F3270CE00B0080C /* CGFloat+Pin.swift */,
111+ );
112+ name = Util;
113+ sourceTree = "<group>";
114+ };
115+/* End PBXGroup section */
116+
117+/* Begin PBXHeadersBuildPhase section */
118+ BE5C1E141F3266B600B0080C /* Headers */ = {
119+ isa = PBXHeadersBuildPhase;
120+ buildActionMask = 2147483647;
121+ files = (
122+ BE5C1E1C1F3266B600B0080C /* HueKit.h in Headers */,
123+ );
124+ runOnlyForDeploymentPostprocessing = 0;
125+ };
126+/* End PBXHeadersBuildPhase section */
127+
128+/* Begin PBXNativeTarget section */
129+ BE5C1E161F3266B600B0080C /* HueKit */ = {
130+ isa = PBXNativeTarget;
131+ buildConfigurationList = BE5C1E1F1F3266B600B0080C /* Build configuration list for PBXNativeTarget "HueKit" */;
132+ buildPhases = (
133+ BE5C1E121F3266B600B0080C /* Sources */,
134+ BE5C1E131F3266B600B0080C /* Frameworks */,
135+ BE5C1E141F3266B600B0080C /* Headers */,
136+ BE5C1E151F3266B600B0080C /* Resources */,
137+ );
138+ buildRules = (
139+ );
140+ dependencies = (
141+ );
142+ name = HueKit;
143+ productName = HueKit;
144+ productReference = BE5C1E171F3266B600B0080C /* HueKit.framework */;
145+ productType = "com.apple.product-type.framework";
146+ };
147+/* End PBXNativeTarget section */
148+
149+/* Begin PBXProject section */
150+ BE5C1E0E1F3266B600B0080C /* Project object */ = {
151+ isa = PBXProject;
152+ attributes = {
153+ LastUpgradeCheck = 0900;
154+ ORGANIZATIONNAME = "Silver Fox";
155+ TargetAttributes = {
156+ BE5C1E161F3266B600B0080C = {
157+ CreatedOnToolsVersion = 8.3.3;
158+ DevelopmentTeam = 6G5LMQ72D8;
159+ LastSwiftMigration = 0900;
160+ ProvisioningStyle = Automatic;
161+ };
162+ };
163+ };
164+ buildConfigurationList = BE5C1E111F3266B600B0080C /* Build configuration list for PBXProject "HueKit" */;
165+ compatibilityVersion = "Xcode 3.2";
166+ developmentRegion = English;
167+ hasScannedForEncodings = 0;
168+ knownRegions = (
169+ en,
170+ );
171+ mainGroup = BE5C1E0D1F3266B600B0080C;
172+ productRefGroup = BE5C1E181F3266B600B0080C /* Products */;
173+ projectDirPath = "";
174+ projectRoot = "";
175+ targets = (
176+ BE5C1E161F3266B600B0080C /* HueKit */,
177+ );
178+ };
179+/* End PBXProject section */
180+
181+/* Begin PBXResourcesBuildPhase section */
182+ BE5C1E151F3266B600B0080C /* Resources */ = {
183+ isa = PBXResourcesBuildPhase;
184+ buildActionMask = 2147483647;
185+ files = (
186+ );
187+ runOnlyForDeploymentPostprocessing = 0;
188+ };
189+/* End PBXResourcesBuildPhase section */
190+
191+/* Begin PBXSourcesBuildPhase section */
192+ BE5C1E121F3266B600B0080C /* Sources */ = {
193+ isa = PBXSourcesBuildPhase;
194+ buildActionMask = 2147483647;
195+ files = (
196+ BE5C1E3B1F326EE600B0080C /* UIColor+Values.swift in Sources */,
197+ BE5C1E471F3271D600B0080C /* ColorBarPicker.swift in Sources */,
198+ BE5C1E4F1F3271E900B0080C /* HSBGen.swift in Sources */,
199+ BE5C1E4D1F3271D600B0080C /* SourceColorView.swift in Sources */,
200+ BE5C1E4A1F3271D600B0080C /* ColorSquarePicker.swift in Sources */,
201+ BE5C1E491F3271D600B0080C /* ColorIndicatorView.swift in Sources */,
202+ BE5C1E381F326E3300B0080C /* HSV.swift in Sources */,
203+ BE5C1E3F1F32710B00B0080C /* HSBComponent.swift in Sources */,
204+ BE5C1E361F326E2500B0080C /* RGB.swift in Sources */,
205+ BE5C1E4B1F3271D600B0080C /* ColorSquareView.swift in Sources */,
206+ BE5C1E3D1F3270CE00B0080C /* CGFloat+Pin.swift in Sources */,
207+ BE5C1E481F3271D600B0080C /* ColorBarView.swift in Sources */,
208+ );
209+ runOnlyForDeploymentPostprocessing = 0;
210+ };
211+/* End PBXSourcesBuildPhase section */
212+
213+/* Begin XCBuildConfiguration section */
214+ BE5C1E1D1F3266B600B0080C /* Debug */ = {
215+ isa = XCBuildConfiguration;
216+ buildSettings = {
217+ ALWAYS_SEARCH_USER_PATHS = NO;
218+ CLANG_ANALYZER_NONNULL = YES;
219+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
220+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
221+ CLANG_CXX_LIBRARY = "libc++";
222+ CLANG_ENABLE_MODULES = YES;
223+ CLANG_ENABLE_OBJC_ARC = YES;
224+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
225+ CLANG_WARN_BOOL_CONVERSION = YES;
226+ CLANG_WARN_COMMA = YES;
227+ CLANG_WARN_CONSTANT_CONVERSION = YES;
228+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
229+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
230+ CLANG_WARN_EMPTY_BODY = YES;
231+ CLANG_WARN_ENUM_CONVERSION = YES;
232+ CLANG_WARN_INFINITE_RECURSION = YES;
233+ CLANG_WARN_INT_CONVERSION = YES;
234+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
235+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
236+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
237+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
238+ CLANG_WARN_STRICT_PROTOTYPES = YES;
239+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
240+ CLANG_WARN_UNREACHABLE_CODE = YES;
241+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
242+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
243+ COPY_PHASE_STRIP = NO;
244+ CURRENT_PROJECT_VERSION = 1;
245+ DEBUG_INFORMATION_FORMAT = dwarf;
246+ ENABLE_STRICT_OBJC_MSGSEND = YES;
247+ ENABLE_TESTABILITY = YES;
248+ GCC_C_LANGUAGE_STANDARD = gnu99;
249+ GCC_DYNAMIC_NO_PIC = NO;
250+ GCC_NO_COMMON_BLOCKS = YES;
251+ GCC_OPTIMIZATION_LEVEL = 0;
252+ GCC_PREPROCESSOR_DEFINITIONS = (
253+ "DEBUG=1",
254+ "$(inherited)",
255+ );
256+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
257+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
258+ GCC_WARN_UNDECLARED_SELECTOR = YES;
259+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
260+ GCC_WARN_UNUSED_FUNCTION = YES;
261+ GCC_WARN_UNUSED_VARIABLE = YES;
262+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
263+ MTL_ENABLE_DEBUG_INFO = YES;
264+ ONLY_ACTIVE_ARCH = YES;
265+ SDKROOT = iphoneos;
266+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
267+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
268+ TARGETED_DEVICE_FAMILY = "1,2";
269+ VERSIONING_SYSTEM = "apple-generic";
270+ VERSION_INFO_PREFIX = "";
271+ };
272+ name = Debug;
273+ };
274+ BE5C1E1E1F3266B600B0080C /* Release */ = {
275+ isa = XCBuildConfiguration;
276+ buildSettings = {
277+ ALWAYS_SEARCH_USER_PATHS = NO;
278+ CLANG_ANALYZER_NONNULL = YES;
279+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
280+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
281+ CLANG_CXX_LIBRARY = "libc++";
282+ CLANG_ENABLE_MODULES = YES;
283+ CLANG_ENABLE_OBJC_ARC = YES;
284+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
285+ CLANG_WARN_BOOL_CONVERSION = YES;
286+ CLANG_WARN_COMMA = YES;
287+ CLANG_WARN_CONSTANT_CONVERSION = YES;
288+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
289+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
290+ CLANG_WARN_EMPTY_BODY = YES;
291+ CLANG_WARN_ENUM_CONVERSION = YES;
292+ CLANG_WARN_INFINITE_RECURSION = YES;
293+ CLANG_WARN_INT_CONVERSION = YES;
294+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
295+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
296+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
297+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
298+ CLANG_WARN_STRICT_PROTOTYPES = YES;
299+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
300+ CLANG_WARN_UNREACHABLE_CODE = YES;
301+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
302+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
303+ COPY_PHASE_STRIP = NO;
304+ CURRENT_PROJECT_VERSION = 1;
305+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
306+ ENABLE_NS_ASSERTIONS = NO;
307+ ENABLE_STRICT_OBJC_MSGSEND = YES;
308+ GCC_C_LANGUAGE_STANDARD = gnu99;
309+ GCC_NO_COMMON_BLOCKS = YES;
310+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
311+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
312+ GCC_WARN_UNDECLARED_SELECTOR = YES;
313+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
314+ GCC_WARN_UNUSED_FUNCTION = YES;
315+ GCC_WARN_UNUSED_VARIABLE = YES;
316+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
317+ MTL_ENABLE_DEBUG_INFO = NO;
318+ SDKROOT = iphoneos;
319+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
320+ TARGETED_DEVICE_FAMILY = "1,2";
321+ VALIDATE_PRODUCT = YES;
322+ VERSIONING_SYSTEM = "apple-generic";
323+ VERSION_INFO_PREFIX = "";
324+ };
325+ name = Release;
326+ };
327+ BE5C1E201F3266B600B0080C /* Debug */ = {
328+ isa = XCBuildConfiguration;
329+ buildSettings = {
330+ CLANG_ENABLE_MODULES = YES;
331+ CODE_SIGN_IDENTITY = "";
332+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
333+ DEFINES_MODULE = YES;
334+ DEVELOPMENT_TEAM = 6G5LMQ72D8;
335+ DYLIB_COMPATIBILITY_VERSION = 1;
336+ DYLIB_CURRENT_VERSION = 1;
337+ DYLIB_INSTALL_NAME_BASE = "@rpath";
338+ INFOPLIST_FILE = HueKit/Info.plist;
339+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
340+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
341+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
342+ PRODUCT_BUNDLE_IDENTIFIER = be.silverfox.HueKit;
343+ PRODUCT_NAME = "$(TARGET_NAME)";
344+ SKIP_INSTALL = YES;
345+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
346+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
347+ SWIFT_VERSION = 4.0;
348+ };
349+ name = Debug;
350+ };
351+ BE5C1E211F3266B600B0080C /* Release */ = {
352+ isa = XCBuildConfiguration;
353+ buildSettings = {
354+ CLANG_ENABLE_MODULES = YES;
355+ CODE_SIGN_IDENTITY = "";
356+ DEFINES_MODULE = YES;
357+ DEVELOPMENT_TEAM = 6G5LMQ72D8;
358+ DYLIB_COMPATIBILITY_VERSION = 1;
359+ DYLIB_CURRENT_VERSION = 1;
360+ DYLIB_INSTALL_NAME_BASE = "@rpath";
361+ INFOPLIST_FILE = HueKit/Info.plist;
362+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
363+ IPHONEOS_DEPLOYMENT_TARGET = 10.0;
364+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
365+ PRODUCT_BUNDLE_IDENTIFIER = be.silverfox.HueKit;
366+ PRODUCT_NAME = "$(TARGET_NAME)";
367+ SKIP_INSTALL = YES;
368+ SWIFT_SWIFT3_OBJC_INFERENCE = Default;
369+ SWIFT_VERSION = 4.0;
370+ };
371+ name = Release;
372+ };
373+/* End XCBuildConfiguration section */
374+
375+/* Begin XCConfigurationList section */
376+ BE5C1E111F3266B600B0080C /* Build configuration list for PBXProject "HueKit" */ = {
377+ isa = XCConfigurationList;
378+ buildConfigurations = (
379+ BE5C1E1D1F3266B600B0080C /* Debug */,
380+ BE5C1E1E1F3266B600B0080C /* Release */,
381+ );
382+ defaultConfigurationIsVisible = 0;
383+ defaultConfigurationName = Release;
384+ };
385+ BE5C1E1F1F3266B600B0080C /* Build configuration list for PBXNativeTarget "HueKit" */ = {
386+ isa = XCConfigurationList;
387+ buildConfigurations = (
388+ BE5C1E201F3266B600B0080C /* Debug */,
389+ BE5C1E211F3266B600B0080C /* Release */,
390+ );
391+ defaultConfigurationIsVisible = 0;
392+ defaultConfigurationName = Release;
393+ };
394+/* End XCConfigurationList section */
395+ };
396+ rootObject = BE5C1E0E1F3266B600B0080C /* Project object */;
397+}
HueKit.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:HueKit.xcodeproj">
6+ </FileRef>
7+</Workspace>
HueKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plistnew file+8
@@ -0,0 +1,8 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>IDEDidComputeMac32BitWarning</key>
6+ <true/>
7+</dict>
8+</plist>
HueKit/HueKit.hnew file+7
@@ -0,0 +1,7 @@
1+//
2+// HueKit.h
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 02/08/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
HueKit/Info.plistnew file+24
@@ -0,0 +1,24 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>CFBundleDevelopmentRegion</key>
6+ <string>en</string>
7+ <key>CFBundleExecutable</key>
8+ <string>$(EXECUTABLE_NAME)</string>
9+ <key>CFBundleIdentifier</key>
10+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11+ <key>CFBundleInfoDictionaryVersion</key>
12+ <string>6.0</string>
13+ <key>CFBundleName</key>
14+ <string>$(PRODUCT_NAME)</string>
15+ <key>CFBundlePackageType</key>
16+ <string>FMWK</string>
17+ <key>CFBundleShortVersionString</key>
18+ <string>1.0</string>
19+ <key>CFBundleVersion</key>
20+ <string>$(CURRENT_PROJECT_VERSION)</string>
21+ <key>NSPrincipalClass</key>
22+ <string></string>
23+</dict>
24+</plist>
HueKit/Model/HSBComponent.swiftnew file+15
@@ -0,0 +1,15 @@
1+//
2+// HSBComponent.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 02/08/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+
11+enum HSBComponent: Int {
12+ case hue = 0
13+ case saturation = 1
14+ case brightness = 2
15+}
HueKit/Model/HSV.swiftnew file+97
@@ -0,0 +1,97 @@
1+//
2+// HSV.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 02/08/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import CoreGraphics
11+
12+public struct HSV {
13+ /// In degrees (range 0...360)
14+ public var h: CGFloat
15+
16+ /// Percentage in range 0...1
17+ public var s: CGFloat
18+
19+ /// Percentage in range 0...1
20+ /// Also known as "brightness" (B)
21+ public var v: CGFloat
22+}
23+
24+extension HSV {
25+
26+ /// These functions convert between an RGB value with components in the
27+ /// 0.0..1.0 range to HSV where Hue is 0 .. 360 and Saturation and
28+ /// Value (aka Brightness) are percentages expressed as 0.0..1.0.
29+ //
30+ /// Note that HSB (B = Brightness) and HSV (V = Value) are interchangeable
31+ /// names that mean the same thing. I use V here as it is unambiguous
32+ /// relative to the B in RGB, which is Blue.
33+ func toRGB() -> RGB {
34+
35+ var rgb = self.hueToRGB()
36+
37+ let c = v * s
38+ let m = v - c
39+
40+ rgb.r = rgb.r * c + m
41+ rgb.g = rgb.g * c + m
42+ rgb.b = rgb.b * c + m
43+
44+ return rgb
45+ }
46+
47+ func hueToRGB() -> RGB {
48+
49+ let hPrime = h / 60.0
50+
51+ let x = 1.0 - abs(hPrime.truncatingRemainder(dividingBy: 2.0) - 1.0)
52+
53+ let r: CGFloat
54+ let g: CGFloat
55+ let b: CGFloat
56+
57+ if hPrime < 1.0 {
58+
59+ r = 1
60+ g = x
61+ b = 0
62+
63+ } else if hPrime < 2.0 {
64+
65+ r = x
66+ g = 1
67+ b = 0
68+
69+ } else if hPrime < 3.0 {
70+
71+ r = 0
72+ g = 1
73+ b = x
74+
75+ } else if hPrime < 4.0 {
76+
77+ r = 0
78+ g = x
79+ b = 1
80+
81+ } else if hPrime < 5.0 {
82+
83+ r = x
84+ g = 0
85+ b = 1
86+
87+ } else {
88+
89+ r = 1
90+ g = 0
91+ b = x
92+
93+ }
94+
95+ return RGB(r: r, g: g, b: b)
96+ }
97+}
HueKit/Model/RGB.swiftnew file+114
@@ -0,0 +1,114 @@
1+//
2+// RGB.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 02/08/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import CoreGraphics
11+
12+public struct RGB {
13+ /// In range 0...1
14+ public var r: CGFloat
15+
16+ /// In range 0...1
17+ public var g: CGFloat
18+
19+ /// In range 0...1
20+ public var b: CGFloat
21+}
22+
23+public extension RGB {
24+
25+ func toHSV(preserveHS: Bool, h: CGFloat = 0, s: CGFloat = 0) -> HSV {
26+
27+ var h = h
28+ var s = s
29+ var v: CGFloat = 0
30+
31+ var max = r
32+
33+ if max < g {
34+ max = g
35+ }
36+
37+ if max < b {
38+ max = b
39+ }
40+
41+ var min = r
42+
43+ if min > g {
44+ min = g
45+ }
46+
47+ if min > b {
48+ min = b
49+ }
50+
51+ // Brightness (aka Value)
52+
53+ v = max
54+
55+ // Saturation
56+
57+ var sat: CGFloat = 0.0
58+
59+ if max != 0.0 {
60+
61+ sat = (max - min) / max
62+ s = sat
63+
64+ } else {
65+
66+ sat = 0.0
67+
68+ // Black, so sat is undefined, use 0
69+ if !preserveHS {
70+ s = 0.0
71+ }
72+ }
73+
74+ // Hue
75+
76+ var delta: CGFloat = 0
77+
78+ if sat == 0.0 {
79+
80+ // No color, so hue is undefined, use 0
81+ if !preserveHS {
82+ h = 0.0
83+ }
84+
85+ } else {
86+
87+ delta = max - min
88+
89+ var hue: CGFloat = 0
90+
91+ if r == max {
92+ hue = (g - b) / delta
93+ } else if g == max {
94+ hue = 2 + (b - r) / delta
95+ } else {
96+ hue = 4 + (r - g) / delta
97+ }
98+
99+ hue /= 6.0
100+
101+ if hue < 0.0 {
102+ hue += 1.0
103+ }
104+
105+ // 0.0 and 1.0 hues are actually both the same (red)
106+ if !preserveHS || abs(hue - h) != 1.0 {
107+ h = hue
108+ }
109+ }
110+
111+ return HSV(h: h, s: s, v: v)
112+ }
113+
114+}
HueKit/Util/CGFloat+Pin.swiftnew file+25
@@ -0,0 +1,25 @@
1+//
2+// CGFloat+Pin.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 02/08/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import CoreGraphics
11+
12+extension CGFloat {
13+
14+ func pinned(between minValue: CGFloat, and maxValue: CGFloat) -> CGFloat {
15+
16+ if self < minValue {
17+ return minValue
18+ } else if self > maxValue {
19+ return maxValue
20+ } else {
21+ return self
22+ }
23+ }
24+
25+}
HueKit/Util/HSBGen.swiftnew file+143
@@ -0,0 +1,143 @@
1+//
2+// HSBGen.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 29/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import CoreGraphics
11+
12+class HSBGen {
13+
14+ static func createBGRxImageContext(w: Int, h: Int) -> CGContext? {
15+
16+ let colorSpace = CGColorSpaceCreateDeviceRGB()
17+
18+ // BGRA is the most efficient on the iPhone.
19+ var bitmapInfo = CGBitmapInfo(rawValue: CGImageByteOrderInfo.order32Little.rawValue)
20+
21+ let noneSkipFirst = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)
22+
23+ bitmapInfo.formUnion(noneSkipFirst)
24+
25+ let context = CGContext(data: nil, width: w, height: h, bitsPerComponent: 8, bytesPerRow: w * 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
26+
27+ return context
28+ }
29+
30+ /// Generates an image where the specified barComponentIndex (0=H, 1=S, 2=V)
31+ /// varies across the x-axis of the 256x1 pixel image and the other components
32+ /// remain at the constant value specified in the hsv array.
33+ static func createHSVBarContentImage(hsbComponent: HSBComponent, hsv: [CGFloat]) -> CGImage? {
34+
35+ var hsv = hsv
36+
37+ guard let context = createBGRxImageContext(w: 256, h: 1) else {
38+ return nil
39+ }
40+
41+ guard var ptr = context.data?.assumingMemoryBound(to: UInt8.self) else {
42+ return nil
43+ }
44+
45+ for x in 0..<256 {
46+
47+ hsv[hsbComponent.rawValue] = CGFloat(x) / 255.0
48+
49+ let hsvVal = HSV(h: hsv[0] * 360.0, s: hsv[1], v: hsv[2])
50+
51+ let rgb = hsvVal.toRGB()
52+
53+ ptr[0] = UInt8(rgb.b * 255.0)
54+ ptr[1] = UInt8(rgb.g * 255.0)
55+ ptr[2] = UInt8(rgb.r * 255.0)
56+
57+ ptr = ptr.advanced(by: 4)
58+ }
59+
60+ let image = context.makeImage()
61+
62+ return image
63+ }
64+
65+ static private func blend(_ value: UInt, _ percentIn255: UInt) -> UInt {
66+ return (value) * (percentIn255) / 255
67+ }
68+
69+ // Generates a 256x256 image with the specified constant hue where the
70+ // Saturation and value vary in the X and Y axes respectively.
71+ static func createSaturationBrightnessSquareContentImageWithHue(hue: CGFloat) -> CGImage? {
72+
73+ guard let context = createBGRxImageContext(w: 256, h: 256) else {
74+ return nil
75+ }
76+
77+ guard var dataPtr = context.data?.assumingMemoryBound(to: UInt8.self) else {
78+ return nil
79+ }
80+
81+ let rowBytes = context.bytesPerRow
82+
83+ let hsv = HSV(h: hue, s: 0, v: 0)
84+ let rgb = hsv.hueToRGB()
85+
86+ let r = rgb.r
87+ let g = rgb.g
88+ let b = rgb.b
89+
90+ let r_s = (UInt) ((1.0 - r) * 255)
91+ let g_s = (UInt) ((1.0 - g) * 255)
92+ let b_s = (UInt) ((1.0 - b) * 255)
93+
94+ // This doesn't use Swift ranges because those are pretty slow in debug builds
95+
96+ var s: UInt = 0
97+
98+ while true {
99+
100+ var ptr = dataPtr
101+
102+ let r_hs: UInt = 255 - blend(s, r_s)
103+ let g_hs: UInt = 255 - blend(s, g_s)
104+ let b_hs: UInt = 255 - blend(s, b_s)
105+
106+ var v: UInt = 255
107+
108+ while true {
109+
110+ // Really, these should all be of the form used in blend(),
111+ // which does a divide by 255. However, integer divide is
112+ // implemented in software on ARM, so a divide by 256
113+ // (done as a bit shift) will be *nearly* the same value,
114+ // and is faster. The more-accurate versions would look like:
115+ // ptr[0] = blend(v, b_hs);
116+
117+ ptr[0] = UInt8((v * b_hs) >> 8)
118+ ptr[1] = UInt8((v * g_hs) >> 8)
119+ ptr[2] = UInt8((v * r_hs) >> 8)
120+
121+ ptr = ptr.advanced(by: rowBytes)
122+
123+ if v == 0 {
124+ break
125+ }
126+
127+ v -= 1
128+ }
129+
130+ dataPtr = dataPtr.advanced(by: 4)
131+
132+ if s == 255 {
133+ break
134+ }
135+
136+ s += 1
137+ }
138+
139+ let image = context.makeImage()
140+
141+ return image
142+ }
143+}
HueKit/Util/UIColor+Values.swiftnew file+57
@@ -0,0 +1,57 @@
1+//
2+// UIColor+Values.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 02/08/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import UIKit
11+
12+extension UIColor {
13+
14+ public var rgbValue: RGB? {
15+
16+ guard let components = cgColor.components else {
17+ return nil
18+ }
19+
20+ let numComponents = cgColor.numberOfComponents
21+
22+ let r: CGFloat
23+ let g: CGFloat
24+ let b: CGFloat
25+
26+ if numComponents < 3 {
27+ r = components[0]
28+ g = components[0]
29+ b = components[0]
30+ } else {
31+ r = components[0]
32+ g = components[1]
33+ b = components[2]
34+ }
35+
36+ return RGB(r: r, g: g, b: b)
37+ }
38+
39+ public var hsvValue: HSV? {
40+
41+ guard let rgb = rgbValue else {
42+ return nil
43+ }
44+
45+ return rgb.toHSV(preserveHS: true)
46+ }
47+
48+ public func hsvValue(preservingHue hue: CGFloat, preservingSat sat: CGFloat) -> HSV? {
49+
50+ guard let rgb = rgbValue else {
51+ return nil
52+ }
53+
54+ return rgb.toHSV(preserveHS: true, h: hue, s: sat)
55+ }
56+
57+}
HueKit/View/ColorBarPicker.swiftnew file+297
@@ -0,0 +1,297 @@
1+//
2+// ColorBarPicker.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 30/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import UIKit
11+
12+@IBDesignable
13+public class ColorBarPicker: UIControl {
14+
15+ private var isVertical: Bool = false {
16+ didSet {
17+ if isVertical != oldValue {
18+ updateOrientation()
19+ }
20+ }
21+ }
22+
23+ private func updateVerticalState() {
24+
25+// let bounds = self.layer.presentation()?.bounds ?? self.bounds
26+ isVertical = bounds.height > bounds.width
27+
28+ }
29+
30+
31+ private let contentInset: CGFloat = 20.0
32+ private static let indicatorSizeInactive = CGSize(width: 24.0, height: 24.0)
33+ private static let indicatorSizeActive = CGSize(width: 40.0, height: 40.0)
34+
35+ @IBInspectable
36+ public var hue: CGFloat {
37+ get {
38+ if isVertical {
39+ return 1.0 - value
40+ } else {
41+ return value
42+ }
43+ }
44+ set {
45+ if isVertical {
46+ value = 1.0 - newValue
47+ } else {
48+ value = newValue
49+ }
50+ }
51+ }
52+
53+ private var value: CGFloat = 0.0 {
54+ didSet {
55+
56+ if oldValue != value {
57+
58+ self.sendActions(for: .valueChanged)
59+ self.setNeedsLayout()
60+ }
61+
62+ }
63+ }
64+
65+ private lazy var colorBarView: ColorBarView = {
66+ return ColorBarView()
67+ }()
68+
69+ private lazy var indicator: ColorIndicatorView = {
70+
71+ let frame = CGRect(origin: .zero, size: ColorBarPicker.indicatorSizeInactive)
72+ let indicator = ColorIndicatorView(frame: frame)
73+
74+ return indicator
75+
76+ }()
77+
78+ func updateOrientation() {
79+
80+ guard colorBarView.superview != nil else {
81+ return
82+ }
83+
84+ if isVertical {
85+
86+ colorBarView.transform = .identity
87+
88+ var rect = self.bounds
89+ rect.size.width = bounds.height - contentInset * 2
90+ rect.size.height = bounds.width
91+
92+ colorBarView.frame = rect
93+
94+ colorBarView.transform = CGAffineTransform(rotationAngle: -.pi / 2.0)
95+
96+ colorBarView.frame.origin = CGPoint(x: 0, y: contentInset)
97+
98+ } else {
99+
100+ var rect = self.bounds
101+ rect.size.width -= contentInset * 2
102+
103+ colorBarView.frame = rect
104+
105+ colorBarView.transform = .identity
106+
107+ colorBarView.frame.origin = CGPoint(x: contentInset, y: 0)
108+
109+ }
110+
111+ }
112+
113+ // MARK: - Drawing
114+
115+ override public func layoutSubviews() {
116+
117+ if colorBarView.superview == nil {
118+
119+ colorBarView.isUserInteractionEnabled = false
120+
121+ colorBarView.translatesAutoresizingMaskIntoConstraints = false
122+ self.addSubview(colorBarView)
123+
124+ updateOrientation()
125+
126+ }
127+
128+ if indicator.superview == nil {
129+ self.addSubview(indicator)
130+ }
131+
132+ let wasVertical = isVertical
133+
134+ updateVerticalState()
135+
136+ if wasVertical != isVertical {
137+
138+ value = 1.0 - value
139+
140+ }
141+
142+ updateOrientation()
143+// colorBarView.setNeedsDisplay()
144+
145+ indicator.color = UIColor(hue: hue,
146+ saturation: 1.0,
147+ brightness: 1.0,
148+ alpha: 1.0)
149+
150+ if isVertical {
151+
152+ let indicatorLoc = contentInset + (self.value * (self.bounds.size.height - 2 * contentInset))
153+ indicator.center = CGPoint(x: self.bounds.midX, y: indicatorLoc)
154+
155+ } else {
156+
157+ let indicatorLoc = contentInset + (self.value * (self.bounds.size.width - 2 * contentInset))
158+ indicator.center = CGPoint(x: indicatorLoc, y: self.bounds.midY)
159+
160+ }
161+
162+ }
163+
164+ // MARK: - Tracking
165+
166+ private func trackIndicator(with touch: UITouch) {
167+
168+ let touchLocation = touch.location(in: self)
169+
170+ let percent: CGFloat
171+
172+ if isVertical {
173+
174+ percent = (touchLocation.y - contentInset) / (self.bounds.size.height - 2 * contentInset)
175+
176+ } else {
177+
178+ percent = (touchLocation.x - contentInset) / (self.bounds.size.width - 2 * contentInset)
179+
180+ }
181+
182+ self.value = percent.pinned(between: 0, and: 1)
183+ }
184+
185+ override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
186+ self.trackIndicator(with: touch)
187+
188+ growIndicator()
189+ return true
190+ }
191+
192+ override public func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
193+ self.trackIndicator(with: touch)
194+
195+ return true
196+ }
197+
198+ override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
199+ super.endTracking(touch, with: event)
200+
201+ shrinkIndicator()
202+ }
203+
204+ override public func cancelTracking(with event: UIEvent?) {
205+ super.cancelTracking(with: event)
206+
207+ shrinkIndicator()
208+ }
209+
210+ private func changeIndicatorSize(to size: CGSize) {
211+
212+ let center = self.indicator.center
213+
214+ let indicatorRect = CGRect(origin: .zero, size: size)
215+
216+ self.indicator.frame = indicatorRect
217+ self.indicator.center = center
218+
219+ }
220+
221+ private func growIndicator() {
222+
223+ UIView.animate(withDuration: 0.15, delay: 0.0, options: [.curveEaseIn], animations: {
224+
225+ self.changeIndicatorSize(to: ColorBarPicker.indicatorSizeActive)
226+
227+ }) { (finished) in
228+
229+ }
230+
231+ }
232+
233+ private func shrinkIndicator() {
234+
235+ UIView.animate(withDuration: 0.15, delay: 0.0, options: [.curveEaseOut], animations: {
236+
237+ self.changeIndicatorSize(to: ColorBarPicker.indicatorSizeInactive)
238+ self.indicator.setNeedsDisplay()
239+
240+ }) { (finished) in
241+
242+ self.indicator.setNeedsDisplay()
243+
244+ }
245+
246+ }
247+
248+
249+ // MARK: - Accessibility
250+
251+ private let accessibilityInterval: CGFloat = 0.05
252+
253+ public override var accessibilityTraits: UIAccessibilityTraits {
254+ get {
255+ var t = super.accessibilityTraits
256+
257+ t |= UIAccessibilityTraitAdjustable
258+
259+ return t
260+ }
261+ set {
262+ super.accessibilityTraits = newValue
263+ }
264+ }
265+
266+ public override func accessibilityIncrement() {
267+
268+ var newValue = self.value + accessibilityInterval
269+
270+ if newValue > 1.0 {
271+ newValue -= 1.0
272+ }
273+
274+ self.value = newValue
275+ }
276+
277+ public override func accessibilityDecrement() {
278+
279+ var newValue = self.value - accessibilityInterval
280+
281+ if newValue < 0 {
282+ newValue += 1.0
283+ }
284+
285+ self.value = newValue
286+ }
287+
288+ public override var accessibilityValue: String? {
289+ get {
290+ return String(format: "%d degrees hue", (self.value * 360.0))
291+ }
292+ set {
293+ super.accessibilityValue = newValue
294+ }
295+ }
296+
297+}
HueKit/View/ColorBarView.swiftnew file+36
@@ -0,0 +1,36 @@
1+//
2+// ColorBarView.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 29/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import UIKit
11+
12+@IBDesignable
13+class ColorBarView: UIView {
14+
15+ private static func createContentImage() -> CGImage? {
16+
17+ let hsv: [CGFloat] = [0.0, 1.0, 1.0]
18+
19+ return HSBGen.createHSVBarContentImage(hsbComponent: .hue, hsv: hsv)
20+ }
21+
22+ override func draw(_ rect: CGRect) {
23+
24+ guard let context = UIGraphicsGetCurrentContext() else {
25+ return
26+ }
27+
28+ guard let image = ColorBarView.createContentImage() else {
29+ return
30+ }
31+
32+ context.draw(image, in: self.bounds)
33+
34+ }
35+
36+}
HueKit/View/ColorIndicatorView.swiftnew file+69
@@ -0,0 +1,69 @@
1+//
2+// ColorIndicatorView.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 30/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import UIKit
11+
12+@IBDesignable
13+public class ColorIndicatorView: UIView {
14+
15+ @IBInspectable
16+ public var color: UIColor = .black {
17+ didSet {
18+
19+ if oldValue != color {
20+ self.setNeedsDisplay()
21+ }
22+
23+ }
24+ }
25+
26+ override public init(frame: CGRect) {
27+ super.init(frame: frame)
28+
29+ self.isOpaque = false
30+ self.isUserInteractionEnabled = false
31+ }
32+
33+ required public init?(coder aDecoder: NSCoder) {
34+ fatalError("init(coder:) has not been implemented")
35+ }
36+
37+ override public func draw(_ rect: CGRect) {
38+
39+ guard let context = UIGraphicsGetCurrentContext() else {
40+ return
41+ }
42+
43+ let center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
44+ let radius = self.bounds.midX
45+
46+ // Fill it:
47+
48+ context.addArc(center: center, radius: radius - 1.0, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: true)
49+ self.color.setFill()
50+ context.fillPath()
51+
52+ // Stroke it (black transucent, inner):
53+
54+ context.addArc(center: center, radius: radius - 1.0, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: true)
55+
56+ context.setStrokeColor(gray: 0.0, alpha: 0.5)
57+ context.setLineWidth(2.0)
58+ context.strokePath()
59+
60+ // Stroke it (white, outer):
61+
62+ context.addArc(center: center, radius: radius - 2.0, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: true)
63+
64+ context.setStrokeColor(gray: 1.0, alpha: 1.0)
65+ context.setLineWidth(2.0)
66+ context.strokePath()
67+ }
68+
69+}
HueKit/View/ColorSquarePicker.swiftnew file+170
@@ -0,0 +1,170 @@
1+//
2+// ColorSquarePicker.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 30/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import UIKit
11+
12+@IBDesignable
13+public class ColorSquarePicker: UIControl {
14+
15+ private let contentInsetX: CGFloat = 20
16+ private let contentInsetY: CGFloat = 20
17+
18+ private let indicatorSizeInactive: CGFloat = 24
19+ private let indicatorSizeActive: CGFloat = 40
20+
21+ private lazy var colorSquareView: ColorSquareView = {
22+ return ColorSquareView()
23+ }()
24+
25+ private lazy var indicator: ColorIndicatorView = {
26+
27+ let size = CGSize(width: self.indicatorSizeInactive, height: self.indicatorSizeInactive)
28+ let indicatorRect = CGRect(origin: .zero, size: size)
29+
30+ return ColorIndicatorView(frame: indicatorRect)
31+ }()
32+
33+ @IBInspectable
34+ public var hue: CGFloat = 0.0 {
35+ didSet {
36+ if oldValue != hue {
37+ self.setIndicatorColor()
38+ }
39+ }
40+ }
41+
42+ @IBInspectable
43+ public var value: CGPoint = .zero {
44+ didSet {
45+ if oldValue != value {
46+
47+ self.sendActions(for: .valueChanged)
48+ self.setNeedsLayout()
49+ }
50+ }
51+ }
52+
53+ private func setIndicatorColor() {
54+
55+ colorSquareView.hue = hue
56+ indicator.color = UIColor(hue: hue, saturation: value.x, brightness: value.y, alpha: 1.0)
57+ }
58+
59+ public override func layoutSubviews() {
60+
61+ if colorSquareView.superview == nil {
62+
63+ colorSquareView.translatesAutoresizingMaskIntoConstraints = false
64+ self.addSubview(colorSquareView)
65+
66+ colorSquareView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: contentInsetX).isActive = true
67+ colorSquareView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -contentInsetX).isActive = true
68+ colorSquareView.topAnchor.constraint(equalTo: self.topAnchor, constant: contentInsetY).isActive = true
69+ colorSquareView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -contentInsetY).isActive = true
70+
71+ }
72+
73+ if indicator.superview == nil {
74+ self.addSubview(indicator)
75+ }
76+
77+ self.setIndicatorColor()
78+
79+ let indicatorX = contentInsetX + (self.value.x * (self.bounds.size.width - 2 * contentInsetX))
80+ let indicatorY = self.bounds.size.height - contentInsetY - (self.value.y * (self.bounds.size.height - 2 * contentInsetY))
81+
82+ indicator.center = CGPoint(x: indicatorX, y: indicatorY)
83+ }
84+
85+ // MARK: - Tracking
86+
87+ private func trackIndicator(with touch: UITouch) {
88+ let bounds = self.bounds
89+
90+ var touchValue = CGPoint(x: 0, y: 0)
91+
92+ touchValue.x = (touch.location(in: self).x - contentInsetX) / (bounds.size.width - 2 * contentInsetX)
93+
94+ touchValue.y = (touch.location(in: self).y - contentInsetY) / (bounds.size.height - 2 * contentInsetY)
95+
96+
97+ touchValue.x = touchValue.x.pinned(between: 0, and: 1)
98+ touchValue.y = 1.0 - touchValue.y.pinned(between: 0, and: 1)
99+
100+ self.value = touchValue
101+ }
102+
103+ override public func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
104+
105+ self.trackIndicator(with: touch)
106+
107+ growIndicator()
108+
109+ return true
110+ }
111+
112+ override public func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
113+
114+ self.trackIndicator(with: touch)
115+
116+ return true
117+ }
118+
119+ override public func endTracking(_ touch: UITouch?, with event: UIEvent?) {
120+ super.endTracking(touch, with: event)
121+
122+ shrinkIndicator()
123+ }
124+
125+ override public func cancelTracking(with event: UIEvent?) {
126+ super.cancelTracking(with: event)
127+
128+ shrinkIndicator()
129+ }
130+
131+ private func changeIndicatorSize(to size: CGFloat) {
132+
133+ let center = self.indicator.center
134+
135+ let size = CGSize(width: size, height: size)
136+ let indicatorRect = CGRect(origin: .zero, size: size)
137+
138+ self.indicator.frame = indicatorRect
139+ self.indicator.center = center
140+
141+ }
142+
143+ private func growIndicator() {
144+
145+ UIView.animate(withDuration: 0.15, delay: 0.0, options: [.curveEaseIn], animations: {
146+
147+ self.changeIndicatorSize(to: self.indicatorSizeActive)
148+
149+ }) { (finished) in
150+
151+ }
152+
153+ }
154+
155+ private func shrinkIndicator() {
156+
157+ UIView.animate(withDuration: 0.15, delay: 0.0, options: [.curveEaseOut], animations: {
158+
159+ self.changeIndicatorSize(to: self.indicatorSizeInactive)
160+ self.indicator.setNeedsDisplay()
161+
162+ }) { (finished) in
163+
164+ self.indicator.setNeedsDisplay()
165+
166+ }
167+
168+ }
169+
170+}
HueKit/View/ColorSquareView.swiftnew file+36
@@ -0,0 +1,36 @@
1+//
2+// ColorSquareView.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 25/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import UIKit
10+
11+@IBDesignable
12+class ColorSquareView: UIImageView {
13+
14+ private var drawnHue: CGFloat = 0.0
15+
16+ @IBInspectable
17+ var hue: CGFloat = 0.0 {
18+ didSet {
19+
20+ if abs(drawnHue - hue) <= 1e-10 {
21+ return
22+ }
23+
24+ let cgImage = HSBGen.createSaturationBrightnessSquareContentImageWithHue(hue: self.hue * 360.0)
25+
26+ if let cgImage = cgImage {
27+ self.image = UIImage(cgImage: cgImage)
28+ } else {
29+ assertionFailure("Expected CGImage")
30+ }
31+
32+ drawnHue = hue
33+ }
34+ }
35+
36+}
HueKit/View/SourceColorView.swiftnew file+89
@@ -0,0 +1,89 @@
1+//
2+// SourceColorView.swift
3+// HueKit
4+//
5+// Created by Louis D'hauwe on 30/07/2017.
6+// Copyright © 2017 Silver Fox. All rights reserved.
7+//
8+
9+import Foundation
10+import UIKit
11+
12+@IBDesignable
13+public class SourceColorView: UIControl {
14+
15+ @IBInspectable
16+ public var isTrackingInside: Bool = false {
17+ didSet {
18+ if oldValue != isTrackingInside {
19+ self.setNeedsDisplay()
20+ }
21+ }
22+ }
23+
24+ @IBInspectable
25+ public var dontShrinkWhenPressed: Bool = false {
26+ didSet {
27+ if oldValue != dontShrinkWhenPressed {
28+ self.setNeedsDisplay()
29+ }
30+ }
31+ }
32+
33+ public override func draw(_ rect: CGRect) {
34+ super.draw(rect)
35+
36+ guard isEnabled && isTrackingInside && !dontShrinkWhenPressed else {
37+ return
38+ }
39+
40+ guard let context = UIGraphicsGetCurrentContext() else {
41+ return
42+ }
43+
44+ let bounds = self.bounds
45+
46+ UIColor.white.set()
47+ context.stroke(bounds.insetBy(dx: 1, dy: 1), width: 2)
48+
49+ UIColor.black.set()
50+ UIRectFrame(bounds.insetBy(dx: 2, dy: 2))
51+ }
52+
53+ // MARK: - UIControl overrides
54+
55+ public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
56+
57+ guard self.isEnabled else {
58+ return false
59+ }
60+
61+ self.isTrackingInside = true
62+
63+ return super.beginTracking(touch, with: event)
64+ }
65+
66+ public override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
67+
68+ let isTrackingInside = self.bounds.contains(touch.location(in: self))
69+
70+ self.isTrackingInside = isTrackingInside
71+
72+ return super.continueTracking(touch, with: event)
73+ }
74+
75+ public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
76+
77+ self.isTrackingInside = false
78+
79+ super.endTracking(touch, with: event)
80+ }
81+
82+ public override func cancelTracking(with event: UIEvent?) {
83+
84+ self.isTrackingInside = false
85+
86+ super.cancelTracking(with: event)
87+ }
88+
89+}
LICENSEnew file+21
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2016 Louis D'hauwe
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.