I used the following code to set gradient color:
NSArray *locations = #[#0.0, #0.1f];
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors = colors;
headerLayer.locations = locations;
How can I make radial gradient color?
I think I have to change locations, but I didn't guess the correct locations. Also, I tried this:
headerLayer.anchorPoint = CGPointMake(0.5, 1.0);
But it doesn't work.
Thanks.
By 2019, the accepted answer is erroneous and you can make a radial gradient by setting its type property to .radial.
On thing important is that the startPoint is the coordinate of the center of the ellipse or circle and the endPoint are the dimensions of the outer eclipse or circle. All coordinates have to be expressed in unit-based (aka 1.0 is 100%).
The following Swift example will draw an ellipse positioned at the center of the frame and the radiuses set to match the frame:
let g = CAGradientLayer()
// We want a radial gradient
g.type = .radial
g.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
let center = CGPoint(x: 0.5, y: 0.5)
g.startPoint = center
let radius = 1.5
g.endPoint = CGPoint(x: radius, y: radius)
The above example can be used to add a vignette to a view for example (see the image).
Note that the radius can be larger than the frame, and the center can be placed outside the frame as well by specifying coordinates greater than 1 or lower than -1.
you can't init radial CAGradientLayer, BUT you can easily modify current layer style to radial
layer.type = .radial
Update:
Seems like it does, other answers show the method.
Unfortunately, CAGradientLayer doesn't support radial gradients. The only way to do that is to subclass CALayer and do the drawing yourself.
I have answered the question here, but since this is an older question, the other one might be a duplicate.
Related
I am new to ios development. i want different animation styles like flipboard animations.can any one give me some sample examples.
Thanks in advance
I don't have any experience in this, but after reading the docs, I think you're going to need a library for that. A quick Google search suggested:
Canvas (provides animations: bounce, slide, fade, fun)
MZFormSheetController
Pop
If you want to code your own custom animation, it appears that the apple documentation explains how.
You can use a library, but it is possible to transform the layer of a UIView in many ways, quite easily.
Say the view you want to animate is called view and is of type UIView
Here is how to animate it similar to a flip animation, where it rotates around the y-axis:
CALayer *layer = view.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform = CATransform3DTranslate(rotationAndPerspectiveTransform, 0, 0, 20);
rotationAndPerspectiveTransform.m34 = 5.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, angle* M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
The key function here is CATransform3DRotate which rotates the layer in 3d.
You specify the axis around which to rotate with the last 3 parameters (x, y, z) which is (0,1,0) in this case, i.e. the y-axis.
Note that this won't produce an animation, but rather, will orient the layer using the provided axis and angle.
To animate the layer, you will have to incrementally change the angle, in another function (e.g. using NSTimer).
NB: This is for a Cocoa app on OS X, NOT iOS.
I have a layer-backed NSButton (subclass of NSView). What I want to do is rotate that button using Core Animation. I'm using the following code to do it:
CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
a.fromValue = [NSNumber numberWithFloat:0];
a.toValue = [NSNumber numberWithFloat:-M_PI*2];
[_refreshButton.layer setAnchorPoint:CGPointMake(0.5, 0.5)];
a.duration = 1.8; // seconds
a.repeatCount = HUGE_VAL;
[_refreshButton.layer addAnimation:a forKey:nil];
This works, EXCEPT that when it runs, the layer jumps down and to the left so that its center point is at the origin point of the NSView, which is the lower-left corner at (0,0). The layer then rotates about its center, but obviously the jumping-to-the-lower-left-corner is not acceptable.
So, after much reading, I found this line in the 10.8 API release notes:
On 10.8, AppKit will control the following properties on a CALayer
(both when "layer-hosted" or "layer-backed"): geometryFlipped, bounds,
frame (implied), position, anchorPoint, transform, shadow*, hidden,
filters, and compositingFilter. Use the appropriate NSView cover methods
to change these properties.
This means that AppKit is "ignoring" my call to -setAnchorPoint in the code above and, instead, is setting that anchor point to the NSView's origin (0,0).
My question is: how do I solve this? What is the "appropriate NSView cover method" to set the anchorPoint for the layer (I can't find such a method on NSView). At the end of the day, I just want my button to rotate around its center point, indefinitely.
I don't see any methods on NSView that are a direct “cover” for anchorPoint.
What I do see in the 10.8 release notes, besides what you quoted, is this:
The anchorPoint is also always set to be (0,0), …
The anchorPoint controls which point of the layer is at position in the superlayer's coordinate system. NSView sets self.layer.anchorPoint to (0,0), which means the layer's lower-left corner is at self.layer.position.
When you set anchorPoint to (0.5,0.5), that means the center of the layer should be at the layer's position. Since you didn't modify position, this has the effect of moving the layer down and to the left, as you are seeing.
You need to compute the position you want the layer to have when its anchorPoint is (0.5,0.5), like this:
CGRect frame = _refreshButton.layer.frame;
CGPoint center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
_refreshButton.layer.position = center;
_refreshButton.layer.anchorPoint = CGPointMake(0.5, 0.5);
I was experiencing the exact same problem as #Bryan in Swift: the object would jump away from its original position during animation. Here's the code for a pulsing NSButton for macOS:
let frame : CGRect = startButton.layer!.frame
let center : CGPoint = CGPoint(x: frame.midX, y: frame.midY)
startButton.layer?.position = center;
startButton.layer?.anchorPoint = CGPoint(x: 0.5, y: 0.5)
let pulse = CASpringAnimation(keyPath: "transform.scale")
pulse.duration = 0.2
pulse.fromValue = 0.95
pulse.toValue = 1.0
pulse.autoreverses = true
pulse.repeatCount = 10
pulse.initialVelocity = 0.5
pulse.damping = 1.0
startButton.layer?.add(pulse, forKey: "pulse")
I'm building an iPad app that includes several bar graphs (30+ bars per graph) and a pull-up menu from the bottom of the screen. The design calls for these bars to have rounded corners on the top left and top right corner, but square corners on the bottom left and bottom right.
Right now, I am rounding the top corners of each individual graph bar by using a mask layer:
#import "UIView+RoundedCorners.h"
#import <QuartzCore/QuartzCore.h>
#implementation UIView (RoundedCorners)
-(void)setRoundedCorners:(UIRectCorner)corners radius:(CGFloat)radius {
CGRect rect = self.bounds;
// Create the path
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:rect
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
// Create the shape layer and set its path
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = rect;
maskLayer.path = maskPath.CGPath;
// Set the newly created shape layer as the mask for the view's layer
self.layer.mask = maskLayer;
self.layer.shouldRasterize = YES;
}
#end
This works, but there is serious frame loss / lag when I pull up the pull-up menu (Note that the menu overlaps, appearing on top of the graph). When I take away the rounded corners, the menu pulls up beautifully. From what I gather, I need a way to round the corners by directly modifying the view, not by using a mask layer. Does anyone know how I could pull this off or have another possible solution? Any help greatly appreciated
From what I gather, I need a way to round the corners by directly
modifying the view, not by using a mask layer. Does anyone know how I
could pull this off or have another possible solution?
The usual way is to get the view's layer and set the cornerRadius property:
myView.layer.cornerRadius = 10.0;
That way there's no need for a mask layer or a bezier path. In the context of your method, it'd be:
-(void)setRoundedCorners:(UIRectCorner)corners radius:(CGFloat)radius
{
self.layer.cornerRadius = radius;
}
Update: Sorry -- I missed this part:
The design calls for these bars to have rounded corners on the top
left and top right corner, but square corners on the bottom left and
bottom right
If you only want some of the corners rounded, you can't do that by setting the layer's cornerRadius property. Two approaches you could take:
Create a bezier path with two corners rounded and two square, and then fill it. No need for a mask layer to do that.
Use a resizable image. See UIImage's -resizableImageWithCapInsets: and -resizableImageWithCapInsets:resizingMode: methods. There are even some methods for animating reizable images, so you could easily have your bars grow to the right length.
Try setting shouldRasterize on your layers. Should help.
I have the following drawing, which renders a circle with full color at the center fading to 0 alpha at the edges. When drawing this to the screen, it looks perfect. However, when I draw the same thing in a PDF context (CGPDFContextCreate), the whole circle comes out opaque. If I draw any other regular path in the PDF, then alpha renders fines. So just the gradient doesn't work. Is this a bug or am I missing something?
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
size_t num_locations = 2;
CGFloat locations[2] = { 1.0, 0.0 };
CGColorRef color = [[UIColor redColor]CGColor];
CGFloat *k = (CGFloat *)CGColorGetComponents(color);
CGFloat components[8] = { k[0], k[1], k[2], 0.0, k[0], k[1], k[2], 1.0 };
CGGradientRef myGradient = CGGradientCreateWithColorComponents(myColorspace, components, locations, num_locations);
CGPoint c = CGPointMake(160, 160);
CGContextDrawRadialGradient(pdfContext, myGradient, c, 0, c, 60, 0);
Official response from Apple tech support:
Quartz ignores the alpha value of colors in gradients (or shadings)
when capturing a gradient (or shading) to a PDF document and instead
treats all colors as if they are completely opaque. In addition,
Quartz ignores the global alpha in the context when it records
gradients (or shadings) into a PDF document. One possible work-around
is to capture a shading as bits using a bitmap context and use the
resulting bits to create a CGImage that you draw through the clipping
area. This produces pre-rendered gradients (or shadings) but does
capture the alpha content into a PDF document. You should not perform
this pre-rendering for gradients (or shadings) that don't contain
alpha.
I have a really annoying issue trying to draw into a bitmap CGContext. What I am trying to do is I have a couple of images to draw into the full size of the image. One can come in at any UIImageOrientation and I've written the code to correctly rotate that properly, but I'm struggling with the second bit which is trying to draw another view at an arbitrary rotation about its centre.
The other view comprises an image drawn possibly outside of its bounds. What I am having a problem with is drawing these at a rotated angle as though it was a UIView that had an affine transform applied to it. e.g. imagine a UIView at {100, 300} of size {20, 20} and an affine transform rotating it by 45 degrees. It would be rotated about {110, 310}.
What I have tried is this:
- (void)drawOtherViewInContext:(CGContextRef)context atRect:(CGRect)rect withRotation:(CGFloat)rotation contextSize:(CGSize)contextSize {
CGRect thisFrame = <SOLVED_FEATURE_FRAME_RELATIVE_TO_RECT_SIZE>;
thisFrame.origin.y = contextSize.height - thisFrame.origin.y - thisFrame.size.height;
CGRect rotatedRect = CGRectApplyAffineTransform(CGRectMake(0.0f, 0.0f, rect.size.width, rect.size.height), CGAffineTransformMakeRotation(-rotation));
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, rect.origin.x, contextSize.height - rect.origin.y - rect.size.height);
transform = CGAffineTransformTranslate(transform,
+(rotatedRect.size.width/2.0f),
+(rotatedRect.size.height/2.0f));
transform = CGAffineTransformRotate(transform, -rotation);
transform = CGAffineTransformTranslate(transform,
-(rect.size.width/2.0f),
-(rect.size.height/2.0f));
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, thisFrame, theCGImageToDraw);
CGContextConcatCTM(context, CGAffineTransformInvert(transform));
}
So what I am doing there, I think, is this:
Translate to the bottom left of rect which is where this view is meant to be drawn.
Translate by half the rotated size in x and y.
Rotate by the required angle.
Translate back half the original size in x and y.
I thought that this would be what I wanted to do because the first step translates the coordinate system to be such that thisFrame is drawn correctly relative to where we're being told to draw (by the rect method parameter). Then it's a pretty normal rotate about the centre of a rectangle.
The problem is that when rotated by say 45 degrees, the image is drawn slightly out of place. It's almost correct, but just not quite. When at 0, 90, 180 or 270 degrees then the position is pretty much spot on, maybe a few pixels out but when at 45, 135, 225, 315 degrees the position is too far up and to the right.
Can anyone see what I'm doing wrong here?
Update:
Silly me, it's bigger because I was passing in the wrong rect! Edited to get rid of references to it being the wrong size. It's still not quite in the right place though.
OK I have fixed it. The first point was that I was passing in the wrong rect at first as I was grabbing the frame from a UIView which had an affine transform applied to it, and as we all know the frame in that case is undefined. More likely it's the CGRect that comes from CGRectApplyAffineTransform(bounds, transform) but anyway, I fixed that one.
Then the main problem of drawing offset was fixed by changing my transform to this:
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, rect.origin.x, contextSize.height - rect.origin.y - rect.size.height);
transform = CGAffineTransformTranslate(transform,
+(rect.size.width/2.0f),
+(rect.size.height/2.0f));
transform = CGAffineTransformRotate(transform, -rotation);
transform = CGAffineTransformTranslate(transform,
-(rect.size.width/2.0f),
-(rect.size.height/2.0f));
That's what I had originally thought I should be doing, but for some reason I changed it to use the rotated CGRect.