Skip to content

Commit

Permalink
Made the gradient work between a color and transparency
Browse files Browse the repository at this point in the history
  • Loading branch information
jernejstrasner committed Jul 28, 2015
1 parent 75bd746 commit 2752e14
Showing 5 changed files with 86 additions and 32 deletions.
11 changes: 10 additions & 1 deletion SmoothGradient/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="PT5-y6-YdV">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="PT5-y6-YdV">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
@@ -32,6 +32,12 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="color" keyPath="startColor">
<color key="value" red="1" green="0.0" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="reverse" value="YES"/>
</userDefinedRuntimeAttributes>
</view>
<navigationItem key="navigationItem" id="39M-QE-wSc">
<nil key="title"/>
@@ -47,6 +53,9 @@
</connections>
</segmentedControl>
</navigationItem>
<connections>
<outlet property="gradientView" destination="d4d-5p-x2B" id="1YP-DB-9es"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="g7c-fJ-Nvs" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
4 changes: 3 additions & 1 deletion SmoothGradient/JSTGradientView.h
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@

@interface JSTGradientView : UIView

- (void)redrawSmooth:(BOOL)smooth;
@property (nonatomic, assign) IBInspectable BOOL smooth;
@property (nonatomic, assign) IBInspectable BOOL reverse;
@property (nonatomic, retain) IBInspectable UIColor *startColor;

@end
97 changes: 68 additions & 29 deletions SmoothGradient/JSTGradientView.m
Original file line number Diff line number Diff line change
@@ -8,42 +8,86 @@

#import "JSTGradientView.h"

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

- (void)redrawSmooth:(BOOL)smooth
@implementation JSTGradientView

- (instancetype)init
{
_smooth = smooth;
[self setNeedsDisplay];
self = [super init];
[self setup];
return self;
}

- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
[self setup];
return self;
}

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

- (void)setup
{
self.contentMode = UIViewContentModeRedraw;
}

SyntesizeRedrawableProperty(setSmooth, _smooth, BOOL)
SyntesizeRedrawableProperty(setReverse, _reverse, BOOL)
SyntesizeRedrawableProperty(setStartColor, _startColor, UIColor *)

- (void)drawRect:(CGRect)rect
{
// Draw the gradient background
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

if (_smooth) {
// Get color components
CGColorRef color = _startColor.CGColor;
const CGFloat *startColorComponents = CGColorGetComponents(color);
CGFloat *colorComponents = calloc(8, sizeof(CGFloat));
memset(colorComponents, 1, 8*sizeof(CGFloat)); // Set to white by default
memcpy(colorComponents, startColorComponents, CGColorGetNumberOfComponents(color)*sizeof(CGFloat)); // Copy first color
memcpy(colorComponents+4, startColorComponents, CGColorGetNumberOfComponents(color)*sizeof(CGFloat)); // Copy second color
colorComponents[3] = _reverse ? 1.0f : 0.0f; // Alpha value for the start color
colorComponents[7] = _reverse ? 0.0f : 1.0f; // Alpha value for the end color

if (_smooth) {
// Define the shading callbacks
CGFunctionCallbacks callbacks = {0, blackShade, NULL};
CGFunctionCallbacks callbacks = {0, shadingFunction, NULL};

// As input to our function we want 1 value in the range [0.0, 1.0].
// This is our position within the 'gradient'.
size_t domainDimension = 1;
CGFloat domain[2] = {0.0f, 1.0f};

// The output of our function is 2 values, each in the range [0.0, 1.0].
// This is our selected color for the input position.
// The 2 values are the white and alpha components.
size_t rangeDimension = 2;
CGFloat range[8] = {0.0f, 1.0f, 0.0f, 1.0f};

size_t rangeDimension = 4;
CGFloat range[8] = {
0.0f, 1.0f,
0.0f, 1.0f,
0.0f, 1.0f,
0.0f, 1.0f
};

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

// Create the shading object
CGShadingRef shading = CGShadingCreateAxial(colorSpace, CGPointMake(1, rect.size.height), CGPointMake(1, rect.size.height*0.75f), function, YES, YES);
CGShadingRef shading = CGShadingCreateAxial(colorSpace, CGPointMake(1, rect.size.height), CGPointMake(1, 0), function, YES, YES);

// Draw the shading
CGContextDrawShading(context, shading);
@@ -53,41 +97,36 @@ - (void)drawRect:(CGRect)rect
CGShadingRelease(shading);
}
else {
// Color components
CGFloat colorComponents[4] = {
0.0f, 0.5f,
0.0f, 0.0f
};

// Color locations
CGFloat locations[2] = {
0.0f,
1.0f
};
CGFloat locations[2] = {0.0f, 1.0f};

// The gradient
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colorComponents, locations, 2);

// Draw the gradient
CGContextDrawLinearGradient(context, gradient, CGPointMake(1, rect.size.height), CGPointMake(1, rect.size.height*0.75f), kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation);
CGContextDrawLinearGradient(context, gradient, CGPointMake(1, rect.size.height), CGPointMake(1, 0), kCGGradientDrawsAfterEndLocation | kCGGradientDrawsBeforeStartLocation);

// Clean up
CGGradientRelease(gradient);
}

// Clean up
CGColorSpaceRelease(colorSpace);
free(colorComponents);
}

// This is the callback of our shading function.
// info: not needed
// 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 blackShade(void *info, const CGFloat *inData, CGFloat *outData)
static void shadingFunction(void *info, const CGFloat *inData, CGFloat *outData)
{
float p = inData[0];
outData[0] = 0.0f;
outData[1] = (1.0f-slope(p, 2.0f)) * 0.5f;
CGFloat *color = info; // Pointer to the components of the 2 colors we're interpolating (only using one color for now)
float p = inData[0]; // Position in gradient
outData[0] = color[0];
outData[1] = color[1];
outData[2] = color[2];
outData[3] = fabs(color[3]-slope(p, 2.0f)); // Alpha channel interpolation
}

// Distributes values on a slope aka. ease-in ease-out
4 changes: 4 additions & 0 deletions SmoothGradient/JSTViewController.h
Original file line number Diff line number Diff line change
@@ -8,8 +8,12 @@

#import <UIKit/UIKit.h>

@class JSTGradientView;

@interface JSTViewController : UIViewController

@property (weak, nonatomic) IBOutlet JSTGradientView *gradientView;

- (IBAction)switchMode:(UISegmentedControl *)sender;

@end
2 changes: 1 addition & 1 deletion SmoothGradient/JSTViewController.m
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ @implementation JSTViewController

- (IBAction)switchMode:(UISegmentedControl *)sender
{
[(JSTGradientView *)self.view redrawSmooth:sender.selectedSegmentIndex];
self.gradientView.smooth = sender.selectedSegmentIndex;
}

@end

0 comments on commit 2752e14

Please sign in to comment.