diff --git a/DKVerticalColorPicker/DKVerticalColorPicker.h b/DKVerticalColorPicker/DKVerticalColorPicker.h index d697642..de2ff52 100644 --- a/DKVerticalColorPicker/DKVerticalColorPicker.h +++ b/DKVerticalColorPicker/DKVerticalColorPicker.h @@ -22,22 +22,36 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - + #import +typedef NS_ENUM(NSInteger, DKColorPickerTouchType) { + DKColorPickerTouchTypeTouchesBegan, + DKColorPickerTouchTypeTouchesMoved, + DKColorPickerTouchTypeTouchesEnded +}; + +static const CGFloat END_OF_GRAYSCALE_SECTION = 30; + +static const CGFloat END_OF_WHITE_SECTION = END_OF_GRAYSCALE_SECTION + 5; + +static const CGFloat COLOR_SATURATION = 0.8; + /*! A delegate that gets notifications when the color picked changes. */ @protocol DKVerticalColorPickerDelegate @optional --(void)colorPicked:(UIColor *)color; +- (void)colorPicked:(UIColor *)aColor withTouchType:(DKColorPickerTouchType)aTouchType; @end IB_DESIGNABLE @interface DKVerticalColorPicker : UIView -@property (nonatomic, weak) IBOutlet id delegate; //set after inited -@property (nonatomic) IBInspectable UIColor *selectedColor; //setting this will update the UI & notify the delegate +@property(nonatomic, weak) IBOutlet id delegate; //set after inited +@property(nonatomic) IBInspectable UIColor *selectedColor; //setting this will update the UI & notify the delegate +- (UIColor *)getColor:(CGFloat)aY; +-(CGFloat) getYFromColor: (UIColor *) aColor; @end diff --git a/DKVerticalColorPicker/DKVerticalColorPicker.m b/DKVerticalColorPicker/DKVerticalColorPicker.m index 519682f..c26911c 100644 --- a/DKVerticalColorPicker/DKVerticalColorPicker.m +++ b/DKVerticalColorPicker/DKVerticalColorPicker.m @@ -27,7 +27,7 @@ of this software and associated documentation files (the "Software"), to deal @interface DKVerticalColorPicker () -@property (nonatomic) CGFloat currentSelectionY; +@property(nonatomic) CGFloat currentSelectionY; @end @@ -36,7 +36,8 @@ @implementation DKVerticalColorPicker - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; - if (self) { + if (self) + { self.currentSelectionY = 0.0; self.backgroundColor = [UIColor clearColor]; } @@ -47,36 +48,125 @@ - (instancetype)initWithFrame:(CGRect)frame - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; - if (self) { + if (self) + { self.currentSelectionY = 0.0; self.backgroundColor = [UIColor clearColor]; } return self; } -- (void)drawRect:(CGRect)rect { +- (void)drawRect:(CGRect)rect +{ // Drawing code [super drawRect:rect]; - + //draw wings [[UIColor blackColor] set]; CGFloat tempYPlace = self.currentSelectionY; - if (tempYPlace < 0.0) { + if (tempYPlace < 0.0) + { tempYPlace = 0.0; - } else if (tempYPlace >= self.frame.size.height) { - tempYPlace = self.frame.size.height - 1.0; + } else if (tempYPlace >= self.frame.size.height) + { + tempYPlace = self.frame.size.height - 1.0f; } CGRect temp = CGRectMake(0.0, tempYPlace, self.frame.size.width, 1.0); UIRectFill(temp); - + //draw central bar over it - CGFloat cbxbegin = self.frame.size.width * 0.2; - CGFloat cbwidth = self.frame.size.width * 0.6; - for (int y = 0; y < self.frame.size.height; y++) { - [[UIColor colorWithHue:(y/self.frame.size.height) saturation:1.0 brightness:1.0 alpha:1.0] set]; - CGRect temp = CGRectMake(cbxbegin, y, cbwidth, 1.0); - UIRectFill(temp); + CGFloat cbxbegin = self.frame.size.width * 0.2f; + CGFloat cbwidth = self.frame.size.width * 0.6f; + for (int y = 0; y < self.frame.size.height; y++) + { + UIColor *theColor = [self getColor:y]; + [theColor set]; + CGRect theColorRect = CGRectMake(cbxbegin, y, cbwidth, 1.0); + UIRectFill(theColorRect); + } +} + + +CGFloat projectNormal(CGFloat n, CGFloat start, CGFloat end) +{ + return start + (n * (end - start)); +} + +CGFloat normalize(CGFloat value, CGFloat startValue, CGFloat endValue) +{ + return (value - startValue) / (endValue - startValue); +} + +CGFloat mapInputToRange(CGFloat input, CGFloat startValue, CGFloat endValue, CGFloat outputStart, CGFloat outputEnd) +{ + return projectNormal(MAX(0, MIN(1, normalize(input, startValue, endValue))), outputStart, outputEnd); +} + +- (UIColor *)getColor:(CGFloat)aY +{ + CGFloat theMinY = 0; + CGFloat theMaxY = [self getMaxY]; + + if (aY < END_OF_GRAYSCALE_SECTION) + { + return [self getGrayscaleColor:aY + fromMinY:theMinY + toMaxY:END_OF_GRAYSCALE_SECTION]; + } + else if (aY < END_OF_WHITE_SECTION) + { + return [self getWhiteColor]; + } + else + { + return [self getRainbowHueColor:aY fromMinY:END_OF_WHITE_SECTION toMaxY:theMaxY]; + } +} + +- (CGFloat)getMaxY +{ + return self.frame.size.height; +} + +-(CGFloat) getYFromColor: (UIColor *) aColor +{ + CGFloat hue = 0.0, saturation = 0.0, brightness = 0.0, alpha = 0.0; + if ([aColor getHue:&hue saturation:&saturation brightness:&brightness alpha:&alpha]) + { + if( saturation == 0 ) + { + return brightness*END_OF_GRAYSCALE_SECTION; + } + else + { + return END_OF_WHITE_SECTION + hue*([ self getMaxY] - END_OF_WHITE_SECTION); + } } + + return 0; +} + +- (UIColor *)getWhiteColor +{ + return [UIColor colorWithHue:0 saturation:0 brightness:1 alpha:1.0]; +} + +- (UIColor *)getGrayscaleColor:(CGFloat)aY fromMinY:(CGFloat)aMinY toMaxY:(CGFloat)aMaxY +{ + CGFloat hue = 0; + CGFloat s = 0; + CGFloat b = mapInputToRange(aY, aMinY, aMaxY, 0, 1); + UIColor *theColor = [UIColor colorWithHue:hue saturation:s brightness:b alpha:1.0]; + return theColor; +} + +- (UIColor *)getRainbowHueColor:(CGFloat)aY fromMinY:(CGFloat)aMinY toMaxY:(CGFloat)aMaxY +{ + CGFloat hue = mapInputToRange(aY, aMinY, aMaxY, 0, 1); + CGFloat s = COLOR_SATURATION; + CGFloat b = 1; + UIColor *theColor = [UIColor colorWithHue:hue saturation:s brightness:b alpha:1.0]; + return theColor; } /*! @@ -86,16 +176,13 @@ - (void)setSelectedColor:(UIColor *)selectedColor { if (selectedColor != _selectedColor) { - CGFloat hue = 0.0, temp = 0.0; - if ([selectedColor getHue:&hue saturation:&temp brightness:&temp alpha:&temp]) - { - self.currentSelectionY = floorf(hue * self.frame.size.height); - [self setNeedsDisplay]; - } + self.currentSelectionY = [ self getYFromColor: selectedColor ]; + [self setNeedsDisplay]; + _selectedColor = selectedColor; - if([self.delegate respondsToSelector:@selector(colorPicked:)]) + if ([self.delegate respondsToSelector:@selector(colorPicked:withTouchType:)]) { - [self.delegate colorPicked:_selectedColor]; + [self.delegate colorPicked:_selectedColor withTouchType: DKColorPickerTouchTypeTouchesEnded ]; } } } @@ -104,42 +191,42 @@ - (void)setSelectedColor:(UIColor *)selectedColor - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - //update color - self.currentSelectionY = [((UITouch *)[touches anyObject]) locationInView:self].y; - _selectedColor = [UIColor colorWithHue:(self.currentSelectionY / self.frame.size.height) saturation:1.0 brightness:1.0 alpha:1.0]; - //notify delegate - if([self.delegate respondsToSelector:@selector(colorPicked:)]) - { - [self.delegate colorPicked:self.selectedColor]; - } + [self updateColor:touches]; + [self notifyDelegate:DKColorPickerTouchTypeTouchesBegan]; [self setNeedsDisplay]; } + +- (void)updateColor:(const NSSet *)touches +{ + self.currentSelectionY = [((UITouch *) [touches anyObject]) locationInView:self].y; + _selectedColor = [self getColor:self.currentSelectionY]; +} + - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - //update color - self.currentSelectionY = [((UITouch *)[touches anyObject]) locationInView:self].y; - _selectedColor = [UIColor colorWithHue:(self.currentSelectionY / self.frame.size.height) saturation:1.0 brightness:1.0 alpha:1.0]; - //notify delegate - if([self.delegate respondsToSelector:@selector(colorPicked:)]) - { - [self.delegate colorPicked:self.selectedColor]; - } + [self updateColor:touches]; + [self notifyDelegate:DKColorPickerTouchTypeTouchesMoved]; [self setNeedsDisplay]; } + - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - //update color - self.currentSelectionY = [((UITouch *)[touches anyObject]) locationInView:self].y; - _selectedColor = [UIColor colorWithHue:(self.currentSelectionY / self.frame.size.height) saturation:1.0 brightness:1.0 alpha:1.0]; - //notify delegate - if([self.delegate respondsToSelector:@selector(colorPicked:)]) + [self updateColor:touches]; + + [self notifyDelegate:DKColorPickerTouchTypeTouchesEnded]; + [self setNeedsDisplay]; +} + +- (void)notifyDelegate:(DKColorPickerTouchType)aTouchType +{ + if ([self.delegate respondsToSelector:@selector(colorPicked:withTouchType:)]) { - [self.delegate colorPicked:self.selectedColor]; + [self.delegate colorPicked:self.selectedColor withTouchType:aTouchType]; } - [self setNeedsDisplay]; } + - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - + } @end diff --git a/DKVerticalColorPicker/ViewController.h b/DKVerticalColorPicker/ViewController.h index a18e1d6..90e1a0a 100644 --- a/DKVerticalColorPicker/ViewController.h +++ b/DKVerticalColorPicker/ViewController.h @@ -11,7 +11,7 @@ @interface ViewController : UIViewController --(void)colorPicked:(UIColor *)color; +- (void)colorPicked:(UIColor *)aColor withTouchType:(DKColorPickerTouchType)aTouchType; @end diff --git a/DKVerticalColorPicker/ViewController.m b/DKVerticalColorPicker/ViewController.m index 08dc347..fc72da2 100644 --- a/DKVerticalColorPicker/ViewController.m +++ b/DKVerticalColorPicker/ViewController.m @@ -27,9 +27,9 @@ - (void)didReceiveMemoryWarning { // Dispose of any resources that can be recreated. } --(void)colorPicked:(UIColor *)color +- (void)colorPicked:(UIColor *)aColor withTouchType:(DKColorPickerTouchType)aTouchType { - self.sampleView.backgroundColor = color; + self.sampleView.backgroundColor = aColor; } @end diff --git a/DKVerticalColorPickerTests/DKVerticalColorPickerTests.m b/DKVerticalColorPickerTests/DKVerticalColorPickerTests.m index 21be05f..c6189fe 100644 --- a/DKVerticalColorPickerTests/DKVerticalColorPickerTests.m +++ b/DKVerticalColorPickerTests/DKVerticalColorPickerTests.m @@ -8,6 +8,7 @@ #import #import +#import "DKVerticalColorPicker.h" @interface DKVerticalColorPickerTests : XCTestCase @@ -15,26 +16,69 @@ @interface DKVerticalColorPickerTests : XCTestCase @implementation DKVerticalColorPickerTests -- (void)setUp { +- (void)setUp +{ [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. } -- (void)tearDown { +- (void)tearDown +{ // Put teardown code here. This method is called after the invocation of each test method in the class. [super tearDown]; } -- (void)testExample { - // This is an example of a functional test case. - XCTAssert(YES, @"Pass"); +- (void)testBlack +{ + DKVerticalColorPicker* thePicker = [self createTestColorPicker]; + CGFloat theY = 0; + UIColor *theBlackColor = [UIColor colorWithHue:0 saturation:0 brightness:0 alpha:1]; + XCTAssertTrue([self isColor:theBlackColor + equalToColor:[thePicker getColor:theY]], @"should start with black"); } -- (void)testPerformanceExample { - // This is an example of a performance test case. - [self measureBlock:^{ - // Put the code you want to measure the time of here. - }]; +- (void)testWhite +{ + DKVerticalColorPicker* thePicker = [self createTestColorPicker]; + CGFloat theY = END_OF_GRAYSCALE_SECTION + 2.0f; + UIColor *theWhiteColor = [UIColor colorWithHue:0 saturation:0 brightness:1 alpha:1]; + XCTAssertTrue([self isColor:theWhiteColor + equalToColor:[thePicker getColor:theY]], @"should start with white"); +} + +- (void)testColor +{ + DKVerticalColorPicker* thePicker = [self createTestColorPicker]; + CGFloat theY = 100; + UIColor *theColor = [UIColor colorWithHue:1 saturation: 0.8 brightness:1 alpha:1]; + XCTAssertTrue([self isColor:theColor + equalToColor:[thePicker getColor:theY]], @"should end with color"); +} + +- (void)testGetYFromColor +{ + DKVerticalColorPicker* thePicker = [self createTestColorPicker]; + + UIColor *theBlackColor = [UIColor colorWithHue:0 saturation:0 brightness:0 alpha:1]; + XCTAssertEqual( 0, [ thePicker getYFromColor: theBlackColor], @"should start with black at zero"); + + UIColor *theWhiteColor = [UIColor colorWithHue:0 saturation:0 brightness:1 alpha:1]; + XCTAssertEqual( END_OF_GRAYSCALE_SECTION, [ thePicker getYFromColor: theWhiteColor], @"should show white"); + + UIColor *theColor = [UIColor colorWithHue:1 saturation: 0.8 brightness:1 alpha:1]; + XCTAssertEqual( 100, [ thePicker getYFromColor: theColor], @"should end with red"); +} + +- (DKVerticalColorPicker*)createTestColorPicker +{ + int theHeight = 100; + return [[DKVerticalColorPicker alloc] initWithFrame:CGRectMake(0, 0, 50, theHeight)]; +} + + +- (BOOL)isColor:(UIColor *)aColor equalToColor:(UIColor *)otherColor +{ + return CGColorEqualToColor(aColor.CGColor, otherColor.CGColor); } @end