Skip to content

Commit

Permalink
Added more properties. Fixed color mixup. Refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
jernejstrasner committed Aug 1, 2015
1 parent 326cd12 commit 67337fe
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 45 deletions.
4 changes: 2 additions & 2 deletions SampleApp/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="color" keyPath="startColor">
<color key="value" red="0.0" green="0.0" blue="0.50196081399917603" alpha="1" colorSpace="calibratedRGB"/>
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="endColor">
<color key="value" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="value" red="0.0" green="0.0" blue="0.50196081399917603" alpha="1" colorSpace="calibratedRGB"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
Expand Down
13 changes: 8 additions & 5 deletions SampleApp/JSTViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,29 @@ - (IBAction)reset:(id)sender

- (IBAction)reverse:(id)sender
{
self.gradientView.reverse = !self.gradientView.reverse;
CGPoint startPoint = self.gradientView.startPoint;
self.gradientView.startPoint = self.gradientView.endPoint;
self.gradientView.endPoint = startPoint;
}

- (IBAction)slopeFactorChanged:(UISlider *)sender
{
self.gradientView.slopeFactor = log(sender.value);
self.gradientView.interpolationFactor = log(sender.value);
[self updateLabel];
}

- (void)reset
{
self.gradientView.reverse = NO;
self.gradientView.slopeFactor = 2.0f;
self.gradientView.startPoint = CGPointMake(0.5, 0);
self.gradientView.endPoint = CGPointMake(0.5, 1);
self.gradientView.interpolationFactor = 2.0f;
self.slider.value = pow(M_E, 2.0f);
[self updateLabel];
}

- (void)updateLabel
{
self.factorLabel.text = [NSString stringWithFormat:@"Slope factor: %0.4fx", self.gradientView.slopeFactor];
self.factorLabel.text = [NSString stringWithFormat:@"Slope factor: %0.4fx", self.gradientView.interpolationFactor];
}

@end
20 changes: 18 additions & 2 deletions SmoothGradient/JSTGradientView.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,25 @@

@interface JSTGradientView : UIView

@property (nonatomic, assign) IBInspectable CGFloat slopeFactor;
@property (nonatomic, assign) IBInspectable BOOL reverse;
/// The starting color of the gradient.
@property (nonatomic, retain, nonnull) IBInspectable UIColor *startColor;

/// End ending color of the gradient.
@property (nonatomic, retain, nonnull) IBInspectable UIColor *endColor;

/// The interpolation factor of the gradient. This defines how smooth the color transition is.
@property (nonatomic, assign) IBInspectable CGFloat interpolationFactor;

/// If YES the gradient is drawn before the start point.
@property (nonatomic, assign) IBInspectable BOOL drawsBeforeStart;

/// If YES the gradient is drawn past the end point.
@property (nonatomic, assign) IBInspectable BOOL drawsAfterEnd;

/// The location of the gradient drawing start relative to the view's coordinate space (0.0 - 1.0).
@property (nonatomic, assign) IBInspectable CGPoint startPoint;

/// The location of the gradient drawing end relative to the view's coordinate space (0.0 - 1.0).
@property (nonatomic, assign) IBInspectable CGPoint endPoint;

@end
98 changes: 62 additions & 36 deletions SmoothGradient/JSTGradientView.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@

#import "JSTGradientView.h"

#define SyntesizeRedrawableProperty(setter, ivar, type) \
- (void)setter:(type)value { \
if (ivar == value) return; \
ivar = value; \
[self setNeedsDisplay]; \
}

@implementation JSTGradientView {
CGColorSpaceRef colorSpace;
CGFloat startColorComps[4];
Expand All @@ -36,9 +29,7 @@ @implementation JSTGradientView {

- (instancetype)init
{
self = [super init];
[self setup];
return self;
return [self initWithFrame:CGRectZero];
}

- (instancetype)initWithCoder:(NSCoder *)coder
Expand All @@ -61,22 +52,29 @@ - (void)setup
colorSpace = CGColorSpaceCreateDeviceRGB();
self.startColor = [UIColor whiteColor];
self.endColor = [UIColor blackColor];
self.startPoint = CGPointMake(0.5, 0);
self.endPoint = CGPointMake(1.5, 1);
}

- (void)dealloc
{
CGColorSpaceRelease(colorSpace);
}

SyntesizeRedrawableProperty(setReverse, _reverse, BOOL)
SyntesizeRedrawableProperty(setSlopeFactor, _slopeFactor, CGFloat)
- (void)setInterpolationFactor:(CGFloat)interpolationFactor
{
if (_interpolationFactor == interpolationFactor) return;
_interpolationFactor = interpolationFactor;
[self setNeedsDisplay];
}

- (void)setStartColor:(UIColor *)startColor
{
if (_startColor == startColor) return;
_startColor = startColor;

GetColorComponents(startColor.CGColor, startColorComps);
[startColor getRed:startColorComps green:startColorComps+1 blue:startColorComps+2 alpha:startColorComps+3];

[self setNeedsDisplay];
}

Expand All @@ -85,15 +83,37 @@ - (void)setEndColor:(UIColor *)endColor
if (_endColor == endColor) return;
_endColor = endColor;

GetColorComponents(endColor.CGColor, endColorComps);
[endColor getRed:endColorComps green:endColorComps+1 blue:endColorComps+2 alpha:endColorComps+3];

[self setNeedsDisplay];
}

- (void)setDrawsBeforeStart:(BOOL)drawsBeforeStart
{
if (_drawsBeforeStart == drawsBeforeStart) return;
_drawsBeforeStart = drawsBeforeStart;
[self setNeedsDisplay];
}

- (void)setDrawsAfterEnd:(BOOL)drawsAfterEnd
{
if (_drawsAfterEnd == drawsAfterEnd) return;
_drawsAfterEnd = drawsAfterEnd;
[self setNeedsDisplay];
}

- (void)setStartPoint:(CGPoint)startPoint
{
if (CGPointEqualToPoint(_startPoint, startPoint)) return;
_startPoint = startPoint;
[self setNeedsDisplay];
}

void GetColorComponents(CGColorRef color, CGFloat *outComponents) {
size_t numberOfComponents = CGColorGetNumberOfComponents(color);
if (numberOfComponents != 4) assert("Only RGBA colors supported!");
const CGFloat *components = CGColorGetComponents(color);
memcpy(outComponents, components, sizeof(CGFloat)*numberOfComponents);
- (void)setEndPoint:(CGPoint)endPoint
{
if (CGPointEqualToPoint(_endPoint, endPoint)) return;
_endPoint = endPoint;
[self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
Expand All @@ -102,13 +122,10 @@ - (void)drawRect:(CGRect)rect
CGContextRef context = UIGraphicsGetCurrentContext();

// Shading function info
CGFloat functionInfo[9];
// First color
memcpy(functionInfo, (_reverse ? endColorComps : startColorComps), sizeof(CGFloat)*4);
// Second color
memcpy(functionInfo+4, (_reverse ? startColorComps : endColorComps), sizeof(CGFloat)*4);
// Slope factor
functionInfo[8] = _slopeFactor;
JSTFunctionInfo functionInfo;
memcpy(functionInfo.startColor, startColorComps, sizeof(CGFloat)*4);
memcpy(functionInfo.endColor, endColorComps, sizeof(CGFloat)*4);
functionInfo.slopeFactor = self.interpolationFactor;

// Define the shading callbacks
CGFunctionCallbacks callbacks = {0, shadingFunction, NULL};
Expand All @@ -129,10 +146,12 @@ - (void)drawRect:(CGRect)rect
};

// Create the shading function
CGFunctionRef function = CGFunctionCreate(functionInfo, domainDimension, domain, rangeDimension, range, &callbacks);
CGFunctionRef function = CGFunctionCreate(&functionInfo, domainDimension, domain, rangeDimension, range, &callbacks);

// Create the shading object
CGShadingRef shading = CGShadingCreateAxial(colorSpace, CGPointMake(1, rect.size.height), CGPointMake(1, 0), function, YES, YES);
CGPoint startPoint = CGPointMake(self.startPoint.x * rect.size.width, self.startPoint.y * rect.size.height);
CGPoint endPoint = CGPointMake(self.endPoint.x * rect.size.width, self.endPoint.y * rect.size.height);
CGShadingRef shading = CGShadingCreateAxial(colorSpace, startPoint, endPoint, function, self.drawsBeforeStart, self.drawsAfterEnd);

// Draw the shading
CGContextDrawShading(context, shading);
Expand All @@ -146,16 +165,15 @@ - (void)drawRect:(CGRect)rect
// info: color and slope information
// inData: contains a single float that gives is the current position within the gradient
// outData: we fill this with the color to display at the given position
static void shadingFunction(void *info, const CGFloat *inData, CGFloat *outData)
static void shadingFunction(void *infoPtr, const CGFloat *inData, CGFloat *outData)
{
CGFloat *colors = info; // Pointer to the components of the 2 colors we're interpolating
CGFloat slopeFactor = colors[8]; // Slope factor stored in the colors array
JSTFunctionInfo info = *(JSTFunctionInfo*)infoPtr; // Info struct with colors and parameters
float p = inData[0]; // Position in gradient
float q = slope(p, slopeFactor); // Slope value
outData[0] = colors[0] + (colors[4] - colors[0])*q;
outData[1] = colors[1] + (colors[5] - colors[1])*q;
outData[2] = colors[2] + (colors[6] - colors[2])*q;
outData[3] = colors[3] + (colors[7] - colors[3])*q;
float q = slope(p, info.slopeFactor); // Slope value
outData[0] = info.startColor[0] + (info.endColor[0] - info.startColor[0])*q;
outData[1] = info.startColor[1] + (info.endColor[1] - info.startColor[1])*q;
outData[2] = info.startColor[2] + (info.endColor[2] - info.startColor[2])*q;
outData[3] = info.startColor[3] + (info.endColor[3] - info.startColor[3])*q;
}

// Distributes values on a slope aka. ease-in ease-out
Expand All @@ -165,4 +183,12 @@ static float slope(float x, float A)
return p/(p + powf(1.0f-x, A));
}

// Info struct to pass to shading function
struct _JSTFunctionInfo {
CGFloat startColor[4];
CGFloat endColor[4];
CGFloat slopeFactor;
};
typedef struct _JSTFunctionInfo JSTFunctionInfo;

@end

0 comments on commit 67337fe

Please sign in to comment.