Related
I want make UI like the below image. Now I want to make it by QuartzCore.
I had implemented all the Graphics including circles,Vertical and horizontal lines.
I am little confuse how to make that center point with animation effect shown in Image.
The key to the approach used to draw the image shown above is using CGContextDrawRadialGradient to draw the dot. We can then use this dot image as the contents of a CALayer, allowing us to reposition it and animate it as we see fit.
In your original question you asked...
how to make that center point with animation effect shown in Image?
...but as a still image it doesn't show any animation. I've made an educated guess as to how you envisioned the dot be animated.
The following code creates a gradated dot with a pulsating animation, fading in and out repeatedly:
Code
#import <QuartzCore/QuartzCore.h>
...
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat dotDiameter = 30.f;
CGFloat dotRadius = dotDiameter * 0.5;
CGRect dotRect = CGRectMake(CGRectGetMidX(self.view.frame) - dotRadius,
CGRectGetMidY(self.view.frame) - dotRadius,
dotDiameter, dotDiameter);
CALayer *dotLayer = [CALayer layer];
dotLayer.contents = (id)[self dotImageOfDiameter:dotDiameter].CGImage;
dotLayer.cornerRadius = dotRadius;
dotLayer.frame = dotRect;
dotLayer.masksToBounds = YES;
// Animate the dot to make it appear to pulsate.
CABasicAnimation *pulseAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
pulseAnimation.autoreverses = YES;
pulseAnimation.duration = 0.8;
pulseAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pulseAnimation.repeatCount = INFINITY;
pulseAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[dotLayer addAnimation:pulseAnimation forKey:#"opacity"];
[self.view.layer addSublayer:dotLayer];
}
- (UIImage *)dotImageOfDiameter:(CGFloat)diameter
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), NO, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat radius = diameter * 0.5;
CGColorSpaceRef baseColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat colours[8] = { 0.56f, 0.78f, 0.94f, 1.0f, // Opaque dot colour.
0.56f, 0.78f, 0.94f, 0.0f }; // Transparent dot colour.
CGGradientRef gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, NULL, 2);
CGContextDrawRadialGradient(context, gradient, CGPointMake(radius, radius), 0.0f, CGPointMake(radius, radius), radius, kCGGradientDrawsAfterEndLocation);
CGImageRef dotImageRef = CGBitmapContextCreateImage(context);
UIImage *dotImage = [UIImage imageWithCGImage:dotImageRef];
CGColorSpaceRelease(baseColorSpace);
CGGradientRelease(gradient);
CGImageRelease(dotImageRef);
UIGraphicsEndImageContext();
return dotImage;
}
This one is from apple, shows how to add shadows in Quarrtz. Here it is explained
For shadow you can use the right color values nearly equal to cyan color in ur case,
void MyDrawWithShadows (CGContextRef myContext, // 1
CGFloat wd, CGFloat ht);
{
CGSize myShadowOffset = CGSizeMake (-15, 20);// 2
CGFloat myColorValues[] = {1, 0, 0, .6};// 3
CGColorRef myColor;// 4
CGColorSpaceRef myColorSpace;// 5
CGContextSaveGState(myContext);// 6
CGContextSetShadow (myContext, myShadowOffset, 5); // 7
// Your drawing code here// 8
CGContextSetRGBFillColor (myContext, 0, 1, 0, 1);
CGContextFillRect (myContext, CGRectMake (wd/3 + 75, ht/2 , wd/4, ht/4));
myColorSpace = CGColorSpaceCreateDeviceRGB ();// 9
myColor = CGColorCreate (myColorSpace, myColorValues);// 10
CGContextSetShadowWithColor (myContext, myShadowOffset, 5, myColor);// 11
// Your drawing code here// 12
CGContextSetRGBFillColor (myContext, 0, 0, 1, 1);
CGContextFillRect (myContext, CGRectMake (wd/3-75,ht/2-100,wd/4,ht/4));
CGColorRelease (myColor);// 13
CGColorSpaceRelease (myColorSpace); // 14
CGContextRestoreGState(myContext);// 15
}
You can draw radial gradient to some CALayer and then just animate that CALayer
Use CGContextDrawRadialGradient to draw gradient layer
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
[anim setFromValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]];
// Scale x and y up
[anim setToValue:[NSValue valueWithCATransform3D:
CATransform3DMakeScale (1.0f, 1.0f,0.0f)]];
[anim setAutoreverses:YES];
[anim setRepeatCount:HUGE_VALF];
[gradientViewlayer addAnimation:anim];
Hope this helps, I havent tested this, but may be this can give you some directions.
Other layout you can complete with CGContext
I am trying to draw an image like below with libraries in iOS; but i couldn't.
I think it is very easy to draw but i couldn't achieve.
After i accomplish to draw i will place a label over it.
Use this as your drawRect method:
- (void)drawRect:(CGRect)rect
//// General Declarations
CGContextRef context = UIGraphicsGetCurrentContext();
//// Shadow Declarations
UIColor* shadow = [UIColor blackColor];
CGSize shadowOffset = CGSizeMake(1, 1);
CGFloat shadowBlurRadius = 2;
//// Frames
CGRect frame = rect;
//// Abstracted Graphic Attributes
CGRect shadowBoxRect = CGRectMake(CGRectGetMinX(frame) + 0, CGRectGetMinY(frame) + 0, 40, 40);
CGFloat shadowBoxCornerRadius = 4;
//// ShadowBox Drawing
UIBezierPath* shadowBoxPath = [UIBezierPath bezierPathWithRoundedRect: shadowBoxRect cornerRadius: shadowBoxCornerRadius];
[[UIColor lightGrayColor] setFill];
[shadowBoxPath fill];
////// ShadowBox Inner Shadow
CGRect shadowBoxBorderRect = CGRectInset([shadowBoxPath bounds], -shadowBlurRadius, -shadowBlurRadius);
shadowBoxBorderRect = CGRectOffset(shadowBoxBorderRect, -shadowOffset.width, -shadowOffset.height);
shadowBoxBorderRect = CGRectInset(CGRectUnion(shadowBoxBorderRect, [shadowBoxPath bounds]), -1, -1);
UIBezierPath* shadowBoxNegativePath = [UIBezierPath bezierPathWithRect: shadowBoxBorderRect];
[shadowBoxNegativePath appendPath: shadowBoxPath];
shadowBoxNegativePath.usesEvenOddFillRule = YES;
CGContextSaveGState(context);
{
CGFloat xOffset = shadowOffset.width + round(shadowBoxBorderRect.size.width);
CGFloat yOffset = shadowOffset.height;
CGContextSetShadowWithColor(context,
CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
shadowBlurRadius,
shadow.CGColor);
[shadowBoxPath addClip];
CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(shadowBoxBorderRect.size.width), 0);
[shadowBoxNegativePath applyTransform: transform];
[[UIColor grayColor] setFill];
[shadowBoxNegativePath fill];
}
CGContextRestoreGState(context);
}
Inner shadows are hard to do with CoreGraphics -- basically, you need to negate your path and draw a drop shadow below it, clipped to your original path.
You can take a look at PaintCode and it will show you the code. It has a 15-min demo mode if you don't want to purchase it, that should be enough for your needs.
You could try this:
#import <QuartzCore/QuartzCore.h>
and in your code , after making the your view set these:
self.layer.cornerRadius = x;
self.layer.masksToBounds = TRUE;
This allows you to have rounded corners on your view. And if you calculate the radius to match your view , you should get the desired look.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor grayColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context =UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
// And draw with a blue fill color
CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
CGContextAddRect(context, self.bounds);
CGContextStrokePath(context);
// Close the path
CGContextClosePath(context);
// Fill & stroke the path
CGContextDrawPath(context, kCGPathFillStroke);
self.layer.cornerRadius = self.bounds.size.width/12;
self.layer.masksToBounds = TRUE;
}
I think it will be helpful to you.
Try the below code where myView is the UIView to which you want to set the shadow.
myView.layer.cornerRadius = 5.0f;
myView.layer.masksToBounds = YES;
[myView.layer setShadowColor:[[UIColor blackColor] colorWithAlphaComponent: 0.5]];
[myView.layer setShadowOffset:CGSizeMake(0, -1)];
Hope this helps.-
I got a requirements to display a UILabel with background split between two colors, like in this image:
(colors here are black at the bottom and 50% gray at the top - but this is not important). I tried setting the label's background colour to 50% grey in the interface builder and then do this in the code:
CALayer *sl1 = [[[CALayer alloc] init] autorelease];
sl1.frame = CGRectMake(0, lbl.frame.size.height / 2, lbl.frame.size.width, score1.frame.size.height/2);
sl1.backgroundColor = [[UIColor blackColor] CGColor];
[lbl.layer insertSublayer:sl1 atIndex:0];
Unfortunately, this resulted in the black part being drawn over the text, so the label looks like this:
which is, needless to say, is not something I need. So how can I get this background without turning to custom images? The issue is I need to have UILabel's like this in several places, different sizes - so I would need to create multiple versions of the background image.
Any ideas? Thanks.
this works:
UILabel* myLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
myLabel.text = #"Ciao";
myLabel.textColor = [UIColor greenColor];
UIGraphicsBeginImageContext(CGSizeMake(100, 50));
CGContextRef context = UIGraphicsGetCurrentContext();
// drawing with a gray fill color
CGContextSetRGBFillColor(context, 0.4, 0.4, 0.4, 1.0);
// Add Filled Rectangle,
CGContextFillRect(context, CGRectMake(0.0, 0.0, 100, 50));
// drawing with a black fill color
CGContextSetRGBFillColor(context, 0., 0., 0., .9);
// Add Filled Rectangle,
CGContextFillRect(context, CGRectMake(0.0, 25, 100, 25));
UIImage* resultingImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
myLabel.backgroundColor = [UIColor colorWithPatternImage:resultingImage];
[self.view addSubview:myLabel];
Use UIColor's +colorWithPatternImage:. Pass in a 1px by the UILabel's height image and it will be "tiled" across the the width of the view.
myLabel.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"label-background.png"]];
first you have to subclass the UILabel and override it's drawRect: method like this for gradient background
- (void)drawRect:(CGRect)rect
{
//////////////GET REFERENCE TO CURRENT GRAPHICS CONTEXT
CGContextRef context = UIGraphicsGetCurrentContext();
//////////////CREATE BASE SHAPE WITH ROUNDED CORNERS FROM BOUNDS
CGRect activeBounds = self.bounds;
CGFloat cornerRadius = 10.0f;
CGFloat inset = 6.5f;
CGFloat originX = activeBounds.origin.x + inset;
CGFloat originY = activeBounds.origin.y + inset;
CGFloat width = activeBounds.size.width - (inset*2.0f);
CGFloat height = activeBounds.size.height - (inset*2.0f);
CGRect bPathFrame = CGRectMake(originX, originY, width, height);
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:bPathFrame cornerRadius:cornerRadius].CGPath;
//////////////CREATE BASE SHAPE WITH FILL AND SHADOW
CGContextAddPath(context, path);
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:210.0f/255.0f green:210.0f/255.0f blue:210.0f/255.0f alpha:1.0f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.0f, 1.0f), 6.0f, [UIColor colorWithRed:0.0f/255.0f green:0.0f/255.0f blue:0.0f/255.0f alpha:1.0f].CGColor);
CGContextDrawPath(context, kCGPathFill);
//////////////CLIP STATE
CGContextSaveGState(context); //Save Context State Before Clipping To "path"
CGContextAddPath(context, path);
CGContextClip(context);
//////////////DRAW GRADIENT
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
size_t count = 3;
CGFloat locations[3] = {0.0f, 0.57f, 1.0f};
CGFloat components[12] =
{ 0.0f/255.0f, 0.0f/255.0f, 0.0f/255.0f, 1.0f, //1
5.0f/255.0f, 5.0f/255.0f, 5.0f/255.0f, 1.0f, //2
10.0f/255.0f, 10.0f/255.0f, 10.0f/255.0f, 1.0f}; //3
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, count);
CGPoint startPoint = CGPointMake(activeBounds.size.width * 0.5f, 0.0f);
CGPoint endPoint = CGPointMake(activeBounds.size.width * 0.5f, activeBounds.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
This will draw black to white gradient background
May this will help you
Happy Codding :)
the above code is from this site http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-uialertview-custom-graphics/
I have a UIView subclass that instantiates three sibling sublayers of its layer. Two of the sublayers have their content set to images files; and the third has a graphic drawn in the
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
method. The problem is that my drawing layer always appears behind the image layers. I've tried reordering the sublayers array and altering the zPosition properties of the sublayers to no avail. What am I missing?
- (void)setupLayers
{
CALayer *rootLayer = [self layer];
_meterLayer = [CALayer layer];
[_meterLayer setContents:(id)[[UIImage imageNamed:#"HealthMeterRadial60x60.png"] CGImage]];
_pointerLayer = [CALayer layer];
[_pointerLayer setContents:(id)[[UIImage imageNamed:#"HealthMeterRadialPointer60x60.png"] CGImage]];
_labelLayer = [CALayer layer];
[self set_labelText:#"Health"];
[rootLayer addSublayer:_meterLayer];
[rootLayer insertSublayer:_pointerLayer above:_meterLayer];
[rootLayer insertSublayer:_labelLayer above:_pointerLayer];
}
And the drawLayerInContext method:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
if( layer = _labelLayer) {
//draw graphics ... omitted for clarity; works, but always behind images
}
}
The solution to the problem is to draw the content of _labelLayer to an image context and set it's content property to the resulting CGImage. When setting up the layers:
// create a layer for the name of the meter
_labelLayer = [CALayer layer];
[_labelLayer setFrame:CGRectMake(0, 0, rootLayer.frame.size.width, rootLayer.frame.size.height)];
[self set_labelText:#"Health"];
[_labelLayer setContents:(id)[[self labelImageContent] CGImage]];
Then to draw the contents to a CGImage:
- (UIImage *)labelImageContent {
NSAssert( _labelText != nil, #"No label set for radial meter");
// get a frame for our layer and start a context to make an image
CGRect rect = _labelLayer.frame;
UIGraphicsBeginImageContext(rect.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// measure the size of our text first to know where to position
CGSize textsize = [_labelText sizeWithFont:[UIFont systemFontOfSize:10]];
const char *text = [_labelText cStringUsingEncoding:NSUTF8StringEncoding];
size_t textlen = strlen(text);
// this is just a testing rectangle
CGContextSetRGBFillColor(ctx, 1, 0, 0, 0.5);
CGContextFillRect(ctx,rect);
/* save our context before writing text */
CGContextSaveGState(ctx);
CGContextSelectFont(ctx, "Helvetica", 9, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(ctx, kCGTextFill);
/* this will flip our text so that it is readable */
CGAffineTransform flipTransform = CGAffineTransformMakeTranslation(0.0f, rect.size.height);
flipTransform = CGAffineTransformScale(flipTransform, 1.0f, -1.0f);
// write our title in black
float opaqueBlack[] = {0,0,0,1};
CGContextSetFillColor(ctx, opaqueBlack);
CGContextConcatCTM(ctx, flipTransform);
CGContextShowTextAtPoint(ctx, rect.origin.x + 0.5*(60-textsize.width), rect.origin.y + 40 - 0.5 * textsize.height - 5, text, textlen);
/* get our graphics state back */
CGContextRestoreGState(ctx);
UIImage *labelPic = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return labelPic;
}
df
Is there a way to make the background of a UIView a gradient without subclassing it? I'd rather not use an image file to accomplish this either. It just seems obtuse to have to subclass UIView just to draw a gradient for the background.
You can use +[UIColor colorWithPatternImage:] to produce a patterned background. Example (bring your own CGGradient):
// Allocate color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Allocate bitmap context
CGContextRef bitmapContext = CGBitmapContextCreate(NULL, 320, 480, 8, 4 * 320, colorSpace, kCGImageAlphaNoneSkipFirst);
//allocate myGradient
CGFloat locationList[] = {0.0f, 1.0f};
CGFloat colorList[] = {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f};
CGGradientRef myGradient = CGGradientCreateWithColorComponents(colorSpace, colorList, locationList, 2);
// Draw Gradient Here
CGContextDrawLinearGradient(bitmapContext, myGradient, CGPointMake(0.0f, 0.0f), CGPointMake(320.0f, 480.0f), 0);
// Create a CGImage from context
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
// Create a UIImage from CGImage
UIImage *uiImage = [UIImage imageWithCGImage:cgImage];
// Release the CGImage
CGImageRelease(cgImage);
// Release the bitmap context
CGContextRelease(bitmapContext);
// Release the color space
CGColorSpaceRelease(colorSpace);
// Create the patterned UIColor and set as background color
[targetView setBackgroundColor:[UIColor colorWithPatternImage:image]];
It will probably be simpler to just create a UIView subclass though. It will use less memory as well.
You could do:
Swift >= 4.0:
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [UIColor.red.cgColor, UIColor.white.cgColor]
view.layer.insertSublayer(gradient, at: 0)
Swift 3.0:
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [UIColor.red().cgColor, UIColor.white().cgColor]
view.layer.insertSublayer(gradient, at: 0)
Swift <= 2.3:
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [UIColor.redColor().CGColor, UIColor.whiteColor().CGColor]
view.layer.insertSublayer(gradient, atIndex: 0)
Objective-C:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[self.view.layer insertSublayer:gradient atIndex:0];
Make sure to add the QuartzCore framework to your project as well (at least for Objective-C)...
#import <QuartzCore/QuartzCore.h>
I agree with rpetrich, it would be cleaner to just do the UIView subclass. For an example of how to do this, see my response in this question. If you wanted, you could create a generic gradient UIView subclass and simply place that behind the views you want to have gradient backgrounds.
I change the adopted answer to my edition
+(void) setBlackGradientToView:(UIView*)targetView
{
CGRect frame = targetView.frame;
// Allocate color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//allocate myGradient
CGFloat locationList[] = {0.0f, 1.0f};
CGFloat colorList[] = {0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f};
CGGradientRef myGradient = CGGradientCreateWithColorComponents(colorSpace, colorList, locationList, 2);
// Allocate bitmap context
CGContextRef bitmapContext = CGBitmapContextCreate(NULL, frame.size.width, frame.size.height, 8, 4 * frame.size.width, colorSpace, kCGImageAlphaPremultipliedLast);
//Draw Gradient Here
CGContextDrawLinearGradient(bitmapContext, myGradient, CGPointMake(0.0f, 0.0f), CGPointMake(0, frame.size.height), 0);
// Create a CGImage from context
CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);
// Create a UIImage from CGImage
UIImage *uiImage = [UIImage imageWithCGImage:cgImage];
// Release the CGImage
CGImageRelease(cgImage);
// Release the bitmap context
CGContextRelease(bitmapContext);
// Release the color space
CGColorSpaceRelease(colorSpace);
//Create the patterned UIColor and set as background color
[targetView setBackgroundColor:[UIColor colorWithPatternImage:uiImage]];
//return uiImage;
}