FLEXArgumentInputColorView.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. //
  2. // FLEXArgumentInputColorView.m
  3. // Flipboard
  4. //
  5. // Created by Ryan Olson on 6/30/14.
  6. // Copyright (c) 2014 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXArgumentInputColorView.h"
  9. #import "FLEXUtility.h"
  10. @protocol FLEXColorComponentInputViewDelegate;
  11. @interface FLEXColorComponentInputView : UIView
  12. @property (nonatomic, strong) UISlider *slider;
  13. @property (nonatomic, strong) UILabel *valueLabel;
  14. @property (nonatomic, weak) id <FLEXColorComponentInputViewDelegate> delegate;
  15. @end
  16. @protocol FLEXColorComponentInputViewDelegate <NSObject>
  17. - (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView;
  18. @end
  19. @implementation FLEXColorComponentInputView
  20. - (id)initWithFrame:(CGRect)frame
  21. {
  22. self = [super initWithFrame:frame];
  23. if (self) {
  24. self.slider = [[UISlider alloc] init];
  25. self.slider.backgroundColor = self.backgroundColor;
  26. [self.slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
  27. [self addSubview:self.slider];
  28. self.valueLabel = [[UILabel alloc] init];
  29. self.valueLabel.backgroundColor = self.backgroundColor;
  30. self.valueLabel.font = [FLEXUtility defaultFontOfSize:14.0];
  31. self.valueLabel.textAlignment = NSTextAlignmentRight;
  32. [self addSubview:self.valueLabel];
  33. [self updateValueLabel];
  34. }
  35. return self;
  36. }
  37. - (void)setBackgroundColor:(UIColor *)backgroundColor
  38. {
  39. [super setBackgroundColor:backgroundColor];
  40. self.slider.backgroundColor = backgroundColor;
  41. self.valueLabel.backgroundColor = backgroundColor;
  42. }
  43. - (void)layoutSubviews
  44. {
  45. [super layoutSubviews];
  46. const CGFloat kValueLabelWidth = 50.0;
  47. [self.slider sizeToFit];
  48. CGFloat sliderWidth = self.bounds.size.width - kValueLabelWidth;
  49. self.slider.frame = CGRectMake(0, 0, sliderWidth, self.slider.frame.size.height);
  50. [self.valueLabel sizeToFit];
  51. CGFloat valueLabelOriginX = CGRectGetMaxX(self.slider.frame);
  52. CGFloat valueLabelOriginY = floor((self.slider.frame.size.height - self.valueLabel.frame.size.height) / 2.0);
  53. self.valueLabel.frame = CGRectMake(valueLabelOriginX, valueLabelOriginY, kValueLabelWidth, self.valueLabel.frame.size.height);
  54. }
  55. - (void)sliderChanged:(id)sender
  56. {
  57. [self.delegate colorComponentInputViewValueDidChange:self];
  58. [self updateValueLabel];
  59. }
  60. - (void)updateValueLabel
  61. {
  62. self.valueLabel.text = [NSString stringWithFormat:@"%.3f", self.slider.value];
  63. }
  64. - (CGSize)sizeThatFits:(CGSize)size
  65. {
  66. CGFloat height = [self.slider sizeThatFits:size].height;
  67. return CGSizeMake(size.width, height);
  68. }
  69. @end
  70. @interface FLEXColorPreviewBox : UIView
  71. @property (nonatomic, strong) UIColor *color;
  72. @property (nonatomic, strong) UIView *colorOverlayView;
  73. @end
  74. @implementation FLEXColorPreviewBox
  75. - (id)initWithFrame:(CGRect)frame
  76. {
  77. self = [super initWithFrame:frame];
  78. if (self) {
  79. self.layer.borderWidth = 1.0;
  80. self.layer.borderColor = [[UIColor blackColor] CGColor];
  81. self.backgroundColor = [UIColor colorWithPatternImage:[[self class] backgroundPatternImage]];
  82. self.colorOverlayView = [[UIView alloc] initWithFrame:self.bounds];
  83. self.colorOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  84. self.colorOverlayView.backgroundColor = [UIColor clearColor];
  85. [self addSubview:self.colorOverlayView];
  86. }
  87. return self;
  88. }
  89. - (void)setColor:(UIColor *)color
  90. {
  91. self.colorOverlayView.backgroundColor = color;
  92. }
  93. - (UIColor *)color
  94. {
  95. return self.colorOverlayView.backgroundColor;
  96. }
  97. + (UIImage *)backgroundPatternImage
  98. {
  99. const CGFloat kSquareDimension = 5.0;
  100. CGSize squareSize = CGSizeMake(kSquareDimension, kSquareDimension);
  101. CGSize imageSize = CGSizeMake(2.0 * kSquareDimension, 2.0 * kSquareDimension);
  102. UIGraphicsBeginImageContextWithOptions(imageSize, YES, [[UIScreen mainScreen] scale]);
  103. [[UIColor whiteColor] setFill];
  104. UIRectFill(CGRectMake(0, 0, imageSize.width, imageSize.height));
  105. [[UIColor grayColor] setFill];
  106. UIRectFill(CGRectMake(squareSize.width, 0, squareSize.width, squareSize.height));
  107. UIRectFill(CGRectMake(0, squareSize.height, squareSize.width, squareSize.height));
  108. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  109. UIGraphicsEndImageContext();
  110. return image;
  111. }
  112. @end
  113. @interface FLEXArgumentInputColorView () <FLEXColorComponentInputViewDelegate>
  114. @property (nonatomic, strong) FLEXColorPreviewBox *colorPreviewBox;
  115. @property (nonatomic, strong) UILabel *hexLabel;
  116. @property (nonatomic, strong) FLEXColorComponentInputView *alphaInput;
  117. @property (nonatomic, strong) FLEXColorComponentInputView *redInput;
  118. @property (nonatomic, strong) FLEXColorComponentInputView *greenInput;
  119. @property (nonatomic, strong) FLEXColorComponentInputView *blueInput;
  120. @end
  121. @implementation FLEXArgumentInputColorView
  122. - (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
  123. {
  124. self = [super initWithArgumentTypeEncoding:typeEncoding];
  125. if (self) {
  126. self.colorPreviewBox = [[FLEXColorPreviewBox alloc] init];
  127. [self addSubview:self.colorPreviewBox];
  128. self.hexLabel = [[UILabel alloc] init];
  129. self.hexLabel.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.9];
  130. self.hexLabel.textAlignment = NSTextAlignmentCenter;
  131. self.hexLabel.font = [FLEXUtility defaultFontOfSize:12.0];
  132. [self addSubview:self.hexLabel];
  133. self.alphaInput = [[FLEXColorComponentInputView alloc] init];
  134. self.alphaInput.slider.minimumTrackTintColor = [UIColor blackColor];
  135. self.alphaInput.delegate = self;
  136. [self addSubview:self.alphaInput];
  137. self.redInput = [[FLEXColorComponentInputView alloc] init];
  138. self.redInput.slider.minimumTrackTintColor = [UIColor redColor];
  139. self.redInput.delegate = self;
  140. [self addSubview:self.redInput];
  141. self.greenInput = [[FLEXColorComponentInputView alloc] init];
  142. self.greenInput.slider.minimumTrackTintColor = [UIColor greenColor];
  143. self.greenInput.delegate = self;
  144. [self addSubview:self.greenInput];
  145. self.blueInput = [[FLEXColorComponentInputView alloc] init];
  146. self.blueInput.slider.minimumTrackTintColor = [UIColor blueColor];
  147. self.blueInput.delegate = self;
  148. [self addSubview:self.blueInput];
  149. }
  150. return self;
  151. }
  152. - (void)setBackgroundColor:(UIColor *)backgroundColor
  153. {
  154. [super setBackgroundColor:backgroundColor];
  155. self.alphaInput.backgroundColor = backgroundColor;
  156. self.redInput.backgroundColor = backgroundColor;
  157. self.greenInput.backgroundColor = backgroundColor;
  158. self.blueInput.backgroundColor = backgroundColor;
  159. }
  160. - (void)layoutSubviews
  161. {
  162. [super layoutSubviews];
  163. CGFloat runningOriginY = 0;
  164. CGSize constrainSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
  165. self.colorPreviewBox.frame = CGRectMake(0, runningOriginY, self.bounds.size.width, [[self class] colorPreviewBoxHeight]);
  166. runningOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) + [[self class] inputViewVerticalPadding];
  167. [self.hexLabel sizeToFit];
  168. const CGFloat kLabelVerticalOutsetAmount = 0.0;
  169. const CGFloat kLabelHorizonalOutsetAmount = 2.0;
  170. UIEdgeInsets labelOutset = UIEdgeInsetsMake(-kLabelVerticalOutsetAmount, -kLabelHorizonalOutsetAmount, -kLabelVerticalOutsetAmount, -kLabelHorizonalOutsetAmount);
  171. self.hexLabel.frame = UIEdgeInsetsInsetRect(self.hexLabel.frame, labelOutset);
  172. CGFloat hexLabelOriginX = self.colorPreviewBox.layer.borderWidth;
  173. CGFloat hexLabelOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) - self.colorPreviewBox.layer.borderWidth - self.hexLabel.frame.size.height;
  174. self.hexLabel.frame = CGRectMake(hexLabelOriginX, hexLabelOriginY, self.hexLabel.frame.size.width, self.hexLabel.frame.size.height);
  175. NSArray *colorComponentInputViews = @[self.alphaInput, self.redInput, self.greenInput, self.blueInput];
  176. for (FLEXColorComponentInputView *inputView in colorComponentInputViews) {
  177. CGSize fitSize = [inputView sizeThatFits:constrainSize];
  178. inputView.frame = CGRectMake(0, runningOriginY, fitSize.width, fitSize.height);
  179. runningOriginY = CGRectGetMaxY(inputView.frame) + [[self class] inputViewVerticalPadding];
  180. }
  181. }
  182. - (void)setInputValue:(id)inputValue
  183. {
  184. if ([inputValue isKindOfClass:[UIColor class]]) {
  185. [self updateWithColor:inputValue];
  186. } else if ([inputValue isKindOfClass:[NSValue class]]) {
  187. const char *type = [inputValue objCType];
  188. if (strcmp(type, @encode(CGColorRef)) == 0) {
  189. CGColorRef colorRef;
  190. [inputValue getValue:&colorRef];
  191. UIColor *color = [[UIColor alloc] initWithCGColor:colorRef];
  192. [self updateWithColor:color];
  193. }
  194. }
  195. }
  196. - (id)inputValue
  197. {
  198. return [UIColor colorWithRed:self.redInput.slider.value green:self.greenInput.slider.value blue:self.blueInput.slider.value alpha:self.alphaInput.slider.value];
  199. }
  200. - (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView
  201. {
  202. [self updateColorPreview];
  203. }
  204. - (void)updateWithColor:(UIColor *)color
  205. {
  206. CGFloat red, green, blue, white, alpha;
  207. if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) {
  208. self.alphaInput.slider.value = alpha;
  209. [self.alphaInput updateValueLabel];
  210. self.redInput.slider.value = red;
  211. [self.redInput updateValueLabel];
  212. self.greenInput.slider.value = green;
  213. [self.greenInput updateValueLabel];
  214. self.blueInput.slider.value = blue;
  215. [self.blueInput updateValueLabel];
  216. } else if ([color getWhite:&white alpha:&alpha]) {
  217. self.alphaInput.slider.value = alpha;
  218. [self.alphaInput updateValueLabel];
  219. self.redInput.slider.value = white;
  220. [self.redInput updateValueLabel];
  221. self.greenInput.slider.value = white;
  222. [self.greenInput updateValueLabel];
  223. self.blueInput.slider.value = white;
  224. [self.blueInput updateValueLabel];
  225. }
  226. [self updateColorPreview];
  227. }
  228. - (void)updateColorPreview
  229. {
  230. self.colorPreviewBox.color = self.inputValue;
  231. unsigned char redByte = self.redInput.slider.value * 255;
  232. unsigned char greenByte = self.greenInput.slider.value * 255;
  233. unsigned char blueByte = self.blueInput.slider.value * 255;
  234. self.hexLabel.text = [NSString stringWithFormat:@"#%02X%02X%02X", redByte, greenByte, blueByte];
  235. [self setNeedsLayout];
  236. }
  237. - (CGSize)sizeThatFits:(CGSize)size
  238. {
  239. CGFloat height = 0;
  240. height += [[self class] colorPreviewBoxHeight];
  241. height += [[self class] inputViewVerticalPadding];
  242. height += [self.alphaInput sizeThatFits:size].height;
  243. height += [[self class] inputViewVerticalPadding];
  244. height += [self.redInput sizeThatFits:size].height;
  245. height += [[self class] inputViewVerticalPadding];
  246. height += [self.greenInput sizeThatFits:size].height;
  247. height += [[self class] inputViewVerticalPadding];
  248. height += [self.blueInput sizeThatFits:size].height;
  249. return CGSizeMake(size.width, height);
  250. }
  251. + (CGFloat)inputViewVerticalPadding
  252. {
  253. return 10.0;
  254. }
  255. + (CGFloat)colorPreviewBoxHeight
  256. {
  257. return 40.0;
  258. }
  259. + (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
  260. {
  261. return strcmp(type, @encode(CGColorRef)) == 0 || strcmp(type, "@\"UIColor\"") == 0 || [value isKindOfClass:[UIColor class]];
  262. }
  263. @end