Просмотр исходного кода

Add color argument input view to support UIColor and CGColorRef properties.

Ryan Olson лет назад: 12
Родитель
Сommit
71c453b1d3

+ 13 - 0
Classes/Editing/Argument Input Views/FLEXArgumentInputColorView.h

@@ -0,0 +1,13 @@
+//
+//  FLEXArgumentInputColorView.h
+//  Flipboard
+//
+//  Created by Ryan Olson on 6/30/14.
+//  Copyright (c) 2014 Flipboard. All rights reserved.
+//
+
+#import "FLEXArgumentInputView.h"
+
+@interface FLEXArgumentInputColorView : FLEXArgumentInputView
+
+@end

+ 328 - 0
Classes/Editing/Argument Input Views/FLEXArgumentInputColorView.m

@@ -0,0 +1,328 @@
+//
+//  FLEXArgumentInputColorView.m
+//  Flipboard
+//
+//  Created by Ryan Olson on 6/30/14.
+//  Copyright (c) 2014 Flipboard. All rights reserved.
+//
+
+#import "FLEXArgumentInputColorView.h"
+#import "FLEXUtility.h"
+
+@protocol FLEXColorComponentInputViewDelegate;
+
+@interface FLEXColorComponentInputView : UIView
+
+@property (nonatomic, strong) UISlider *slider;
+@property (nonatomic, strong) UILabel *valueLabel;
+
+@property (nonatomic, weak) id <FLEXColorComponentInputViewDelegate> delegate;
+
+@end
+
+@protocol FLEXColorComponentInputViewDelegate <NSObject>
+
+- (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView;
+
+@end
+
+
+@implementation FLEXColorComponentInputView
+
+- (id)initWithFrame:(CGRect)frame
+{
+    self = [super initWithFrame:frame];
+    if (self) {
+        self.slider = [[UISlider alloc] init];
+        self.slider.backgroundColor = self.backgroundColor;
+        [self.slider addTarget:self action:@selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
+        [self addSubview:self.slider];
+        
+        self.valueLabel = [[UILabel alloc] init];
+        self.valueLabel.backgroundColor = self.backgroundColor;
+        self.valueLabel.font = [FLEXUtility defaultFontOfSize:14.0];
+        self.valueLabel.textAlignment = NSTextAlignmentRight;
+        [self addSubview:self.valueLabel];
+        
+        [self updateValueLabel];
+    }
+    return self;
+}
+
+- (void)setBackgroundColor:(UIColor *)backgroundColor
+{
+    [super setBackgroundColor:backgroundColor];
+    self.slider.backgroundColor = backgroundColor;
+    self.valueLabel.backgroundColor = backgroundColor;
+}
+
+- (void)layoutSubviews
+{
+    [super layoutSubviews];
+    
+    const CGFloat kValueLabelWidth = 50.0;
+    
+    [self.slider sizeToFit];
+    CGFloat sliderWidth = self.bounds.size.width - kValueLabelWidth;
+    self.slider.frame = CGRectMake(0, 0, sliderWidth, self.slider.frame.size.height);
+    
+    [self.valueLabel sizeToFit];
+    CGFloat valueLabelOriginX = CGRectGetMaxX(self.slider.frame);
+    CGFloat valueLabelOriginY = floor((self.slider.frame.size.height - self.valueLabel.frame.size.height) / 2.0);
+    self.valueLabel.frame = CGRectMake(valueLabelOriginX, valueLabelOriginY, kValueLabelWidth, self.valueLabel.frame.size.height);
+}
+
+- (void)sliderChanged:(id)sender
+{
+    [self.delegate colorComponentInputViewValueDidChange:self];
+    [self updateValueLabel];
+}
+
+- (void)updateValueLabel
+{
+    self.valueLabel.text = [NSString stringWithFormat:@"%.3f", self.slider.value];
+}
+
+- (CGSize)sizeThatFits:(CGSize)size
+{
+    CGFloat height = [self.slider sizeThatFits:size].height;
+    return CGSizeMake(size.width, height);
+}
+
+@end
+
+@interface FLEXColorPreviewBox : UIView
+
+@property (nonatomic, strong) UIColor *color;
+
+@property (nonatomic, strong) UIView *colorOverlayView;
+
+@end
+
+@implementation FLEXColorPreviewBox
+
+- (id)initWithFrame:(CGRect)frame
+{
+    self = [super initWithFrame:frame];
+    if (self) {
+        self.layer.borderWidth = 1.0;
+        self.layer.borderColor = [[UIColor blackColor] CGColor];
+        self.backgroundColor = [UIColor colorWithPatternImage:[[self class] backgroundPatternImage]];
+        
+        self.colorOverlayView = [[UIView alloc] initWithFrame:self.bounds];
+        self.colorOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+        self.colorOverlayView.backgroundColor = [UIColor clearColor];
+        [self addSubview:self.colorOverlayView];
+    }
+    return self;
+}
+
+- (void)setColor:(UIColor *)color
+{
+    self.colorOverlayView.backgroundColor = color;
+}
+
+- (UIColor *)color
+{
+    return self.colorOverlayView.backgroundColor;
+}
+
++ (UIImage *)backgroundPatternImage
+{
+    const CGFloat kSquareDimension = 5.0;
+    CGSize squareSize = CGSizeMake(kSquareDimension, kSquareDimension);
+    CGSize imageSize = CGSizeMake(2.0 * kSquareDimension, 2.0 * kSquareDimension);
+    
+    UIGraphicsBeginImageContextWithOptions(imageSize, YES, [[UIScreen mainScreen] scale]);
+    
+    [[UIColor whiteColor] setFill];
+    UIRectFill(CGRectMake(0, 0, imageSize.width, imageSize.height));
+    
+    [[UIColor grayColor] setFill];
+    UIRectFill(CGRectMake(squareSize.width, 0, squareSize.width, squareSize.height));
+    UIRectFill(CGRectMake(0, squareSize.height, squareSize.width, squareSize.height));
+    
+    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+    UIGraphicsEndImageContext();
+    
+    return image;
+}
+
+@end
+
+@interface FLEXArgumentInputColorView () <FLEXColorComponentInputViewDelegate>
+
+@property (nonatomic, strong) FLEXColorPreviewBox *colorPreviewBox;
+@property (nonatomic, strong) UILabel *hexLabel;
+@property (nonatomic, strong) FLEXColorComponentInputView *alphaInput;
+@property (nonatomic, strong) FLEXColorComponentInputView *redInput;
+@property (nonatomic, strong) FLEXColorComponentInputView *greenInput;
+@property (nonatomic, strong) FLEXColorComponentInputView *blueInput;
+
+@end
+
+@implementation FLEXArgumentInputColorView
+
+- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding
+{
+    self = [super initWithArgumentTypeEncoding:typeEncoding];
+    if (self) {
+        self.colorPreviewBox = [[FLEXColorPreviewBox alloc] init];
+        [self addSubview:self.colorPreviewBox];
+        
+        self.hexLabel = [[UILabel alloc] init];
+        self.hexLabel.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.9];
+        self.hexLabel.textAlignment = NSTextAlignmentCenter;
+        self.hexLabel.font = [FLEXUtility defaultFontOfSize:12.0];
+        [self addSubview:self.hexLabel];
+        
+        self.alphaInput = [[FLEXColorComponentInputView alloc] init];
+        self.alphaInput.slider.minimumTrackTintColor = [UIColor blackColor];
+        self.alphaInput.delegate = self;
+        [self addSubview:self.alphaInput];
+        
+        self.redInput = [[FLEXColorComponentInputView alloc] init];
+        self.redInput.slider.minimumTrackTintColor = [UIColor redColor];
+        self.redInput.delegate = self;
+        [self addSubview:self.redInput];
+        
+        self.greenInput = [[FLEXColorComponentInputView alloc] init];
+        self.greenInput.slider.minimumTrackTintColor = [UIColor greenColor];
+        self.greenInput.delegate = self;
+        [self addSubview:self.greenInput];
+        
+        self.blueInput = [[FLEXColorComponentInputView alloc] init];
+        self.blueInput.slider.minimumTrackTintColor = [UIColor blueColor];
+        self.blueInput.delegate = self;
+        [self addSubview:self.blueInput];
+    }
+    return self;
+}
+
+- (void)setBackgroundColor:(UIColor *)backgroundColor
+{
+    [super setBackgroundColor:backgroundColor];
+    self.alphaInput.backgroundColor = backgroundColor;
+    self.redInput.backgroundColor = backgroundColor;
+    self.greenInput.backgroundColor = backgroundColor;
+    self.blueInput.backgroundColor = backgroundColor;
+}
+
+- (void)layoutSubviews
+{
+    [super layoutSubviews];
+    
+    CGFloat runningOriginY = 0;
+    CGSize constrainSize = CGSizeMake(self.bounds.size.width, CGFLOAT_MAX);
+    
+    self.colorPreviewBox.frame = CGRectMake(0, runningOriginY, self.bounds.size.width, [[self class] colorPreviewBoxHeight]);
+    runningOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) + [[self class] inputViewVerticalPadding];
+    
+    [self.hexLabel sizeToFit];
+    const CGFloat kLabelVerticalOutsetAmount = 0.0;
+    const CGFloat kLabelHorizonalOutsetAmount = 2.0;
+    UIEdgeInsets labelOutset = UIEdgeInsetsMake(-kLabelVerticalOutsetAmount, -kLabelHorizonalOutsetAmount, -kLabelVerticalOutsetAmount, -kLabelHorizonalOutsetAmount);
+    self.hexLabel.frame = UIEdgeInsetsInsetRect(self.hexLabel.frame, labelOutset);
+    CGFloat hexLabelOriginX = self.colorPreviewBox.layer.borderWidth;
+    CGFloat hexLabelOriginY = CGRectGetMaxY(self.colorPreviewBox.frame) - self.colorPreviewBox.layer.borderWidth - self.hexLabel.frame.size.height;
+    self.hexLabel.frame = CGRectMake(hexLabelOriginX, hexLabelOriginY, self.hexLabel.frame.size.width, self.hexLabel.frame.size.height);
+    
+    NSArray *colorComponentInputViews = @[self.alphaInput, self.redInput, self.greenInput, self.blueInput];
+    for (FLEXColorComponentInputView *inputView in colorComponentInputViews) {
+        CGSize fitSize = [inputView sizeThatFits:constrainSize];
+        inputView.frame = CGRectMake(0, runningOriginY, fitSize.width, fitSize.height);
+        runningOriginY = CGRectGetMaxY(inputView.frame) + [[self class] inputViewVerticalPadding];
+    }
+}
+
+- (void)setInputValue:(id)inputValue
+{
+    if ([inputValue isKindOfClass:[UIColor class]]) {
+        [self updateWithColor:inputValue];
+    } else if ([inputValue isKindOfClass:[NSValue class]]) {
+        const char *type = [inputValue objCType];
+        if (strcmp(type, @encode(CGColorRef)) == 0) {
+            CGColorRef colorRef;
+            [inputValue getValue:&colorRef];
+            UIColor *color = [[UIColor alloc] initWithCGColor:colorRef];
+            [self updateWithColor:color];
+        }
+    }
+}
+
+- (id)inputValue
+{
+    return [UIColor colorWithRed:self.redInput.slider.value green:self.greenInput.slider.value blue:self.blueInput.slider.value alpha:self.alphaInput.slider.value];
+}
+
+- (void)colorComponentInputViewValueDidChange:(FLEXColorComponentInputView *)colorComponentInputView
+{
+    [self updateColorPreview];
+}
+
+- (void)updateWithColor:(UIColor *)color
+{
+    CGFloat red, green, blue, white, alpha;
+    if ([color getRed:&red green:&green blue:&blue alpha:&alpha]) {
+        self.alphaInput.slider.value = alpha;
+        [self.alphaInput updateValueLabel];
+        self.redInput.slider.value = red;
+        [self.redInput updateValueLabel];
+        self.greenInput.slider.value = green;
+        [self.greenInput updateValueLabel];
+        self.blueInput.slider.value = blue;
+        [self.blueInput updateValueLabel];
+    } else if ([color getWhite:&white alpha:&alpha]) {
+        self.alphaInput.slider.value = alpha;
+        [self.alphaInput updateValueLabel];
+        self.redInput.slider.value = white;
+        [self.redInput updateValueLabel];
+        self.greenInput.slider.value = white;
+        [self.greenInput updateValueLabel];
+        self.blueInput.slider.value = white;
+        [self.blueInput updateValueLabel];
+    }
+    [self updateColorPreview];
+}
+
+- (void)updateColorPreview
+{
+    self.colorPreviewBox.color = self.inputValue;
+    unsigned char redByte = self.redInput.slider.value * 255;
+    unsigned char greenByte = self.greenInput.slider.value * 255;
+    unsigned char blueByte = self.blueInput.slider.value * 255;
+    self.hexLabel.text = [NSString stringWithFormat:@"#%02X%02X%02X", redByte, greenByte, blueByte];
+    [self setNeedsLayout];
+}
+
+- (CGSize)sizeThatFits:(CGSize)size
+{
+    CGFloat height = 0;
+    height += [[self class] colorPreviewBoxHeight];
+    height += [[self class] inputViewVerticalPadding];
+    height += [self.alphaInput sizeThatFits:size].height;
+    height += [[self class] inputViewVerticalPadding];
+    height += [self.redInput sizeThatFits:size].height;
+    height += [[self class] inputViewVerticalPadding];
+    height += [self.greenInput sizeThatFits:size].height;
+    height += [[self class] inputViewVerticalPadding];
+    height += [self.blueInput sizeThatFits:size].height;
+    return CGSizeMake(size.width, height);
+}
+
++ (CGFloat)inputViewVerticalPadding
+{
+    return 10.0;
+}
+
++ (CGFloat)colorPreviewBoxHeight
+{
+    return 40.0;
+}
+
++ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value
+{
+    return strcmp(type, @encode(CGColorRef)) == 0 || strcmp(type, "@\"UIColor\"") == 0 || [value isKindOfClass:[UIColor class]];
+}
+
+@end

+ 4 - 1
Classes/Editing/Argument Input Views/FLEXArgumentInputViewFactory.m

@@ -15,6 +15,7 @@
 #import "FLEXArgumentInputNotSupportedView.h"
 #import "FLEXArgumentInputStringView.h"
 #import "FLEXArgumentInputFontView.h"
+#import "FLEXArgumentInputColorView.h"
 
 @implementation FLEXArgumentInputViewFactory
 
@@ -36,7 +37,9 @@
     // Note that order is important here since multiple subclasses may support the same type.
     // An example is the number subclass and the bool subclass for the type @encode(BOOL).
     // Both work, but we'd prefer to use the bool subclass.
-    if ([FLEXArgumentInputFontView supportsObjCType:typeEncoding withCurrentValue:currentValue]) {
+    if ([FLEXArgumentInputColorView supportsObjCType:typeEncoding withCurrentValue:currentValue]) {
+        argumentInputViewSubclass = [FLEXArgumentInputColorView class];
+    } else if ([FLEXArgumentInputFontView supportsObjCType:typeEncoding withCurrentValue:currentValue]) {
         argumentInputViewSubclass = [FLEXArgumentInputFontView class];
     } else if ([FLEXArgumentInputStringView supportsObjCType:typeEncoding withCurrentValue:currentValue]) {
         argumentInputViewSubclass = [FLEXArgumentInputStringView class];