iPhone Layer confusion - iphone

I am trying to do something that should be really simple. I want to add a gradient to one of my views. I can add it to self.view, but not to viewWithGradient. Here is the code:
CAGradientLayer *gradient = [CAGradientLayer layer];
UIColor *colorFirst = [UIColor colorWithWhite:0.10 alpha:0.15];
UIColor *colorLast = [UIColor colorWithWhite:0.535 alpha:0.8];
NSArray *colors = [NSArray arrayWithObjects:(id)colorFirst.CGColor, colorLast.CGColor, nil];
gradient.colors = colors;
NSNumber *stopFirst = [NSNumber numberWithFloat:0.00];
NSNumber *stopLast = [NSNumber numberWithFloat:1.00];
NSArray *locations = [NSArray arrayWithObjects:stopFirst, stopLast, nil];
gradient.locations = locations;
gradient.frame = CGRectMake(0.0, viewWithGradient.frame.origin.y, viewWithGradient.frame.size.width, height_of_gradient);
[self.view.layer addSublayer:gradient]; // this works. I get a nice gradient at
// the bottom of the view, where I want it,
// but I need the gradient further down in
// my view hierarchy.
// [viewWithGradient.layer addSublayer:gradient]; // this doesn't. There
// is no visually noticeable change
// if this is uncommented and the
// above line is.
viewWithGradient is a small view inside my main view inside a viewController (self.view). There is only one other view over this viewWithGradient. It is a UILabel, that only takes up about one half of the area of viewWithGradient. It has a transparent background, but the label draws its text in white. I need to have the gradient be under the UILabel, not on self.view, over the UILabel.
I currently suspect that my frame/bounds may put the gradient offscreen. Does anyone see anything that I am missing? This seems like the simplest of the CALayer usages, and I have spent way too much time on it.

The y coordinate of your gradient's frame is viewWithGradient.frame.origin.y. Did you actually want 0? If viewWithGradient is more than halfway down the screen, your gradient will draw offscreen.

Related

Place an image behind my labels and in front of my plot within my coreplot graph

How can I get an image behind my labels within my graph. I succeeded with putting an image in front of the graph with:
CPTPlotSpaceAnnotation *imageAnnotation;
CGRect imageRect = CGRectMake(0, 0 , 480, 320);
CPTBorderedLayer * ImageLayer = [[CPTBorderedLayer alloc] initWithFrame:imageRect];
ImageLayer.fill = [CPTFill fillWithImage:[CPTImage imageWithCGImage:[[UIImage imageNamed:#"Overlay_3_digits.png"] CGImage]]];
imageAnnotation = [[CPTPlotSpaceAnnotation alloc] initWithPlotSpace:plotSpace anchorPlotPoint:nil];
imageAnnotation.contentLayer = ImageLayer;
[graph addAnnotation:imageAnnotation];
[graph addSublayer:ImageLayer];
[newImagelLayer release];
[imageAnnotation release];
adding the image with [graph insertSubLayer:ImageLayer below:y]; didn't worked, this also puts the image in front of the graphs and axislabels. Hope someone can help me out.
EDIT:
I added a screenshot from the simulator. The three black rectangles needs to be in front of my plots and behind my labels. The labels will be readible more easily.
I also editted the hierarchy of layers from my graph by putting the axislabels at the first index:
NSArray *chartLayers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:CPTGraphLayerTypeAxisLabels],
[NSNumber numberWithInt:CPTGraphLayerTypePlots],
[NSNumber numberWithInt:CPTGraphLayerTypeMajorGridLines],
[NSNumber numberWithInt:CPTGraphLayerTypeMinorGridLines],
[NSNumber numberWithInt:CPTGraphLayerTypeAxisLines],
[NSNumber numberWithInt:CPTGraphLayerTypeAxisTitles],
nil];
graph.topDownLayerOrder = chartLayers;
I also found a better way of putting my image inside the graph. I did this by means of a new hostlayer:
CPTAnnotationHostLayer *newHostLayer = [[CPTAnnotationHostLayer alloc]
initWithFrame:CGRectMake(0, 0, 480, 220)];
UIImage * annotationBG = [UIImage imageNamed:#"Overlay_3_digits.png"];
newHostLayer.backgroundColor = [UIColor colorWithPatternImage:annotationBG].CGColor;
Any ideas how I can put the three black triangle image at the back of my labels and in front of the plots?
The -addAnnotation: method takes care of updating the layer tree; you should remove your call to -addSublayer:.
Do you want the image to completely fill the background behind the graph? If so, just set the fill on the graph instead of making an annotation.
graph.fill = [CPTFill fillWithImage:[CPTImage imageWithCGImage:[[UIImage imageNamed:#"Overlay_3_digits.png"] CGImage]]];
To put the box behind each label, you can use custom labels. Build a CPTBorderedLayer the same way you did for the annotation. Add a CPTTextLayer with the label text as a sublayer of the bordered layer. Make the bordered layer the contentLayer of the custom label. This should remove the need to change the topDownLayerOrder, too.

How to do vignette effect on iOS?

When UIAlertViews pop up, there is a vignette effect in the background. That is, the edges are darker and the center is lighter.
I was wondering if this vignette effect was built into Cocoa Touch. I would like to show the vignette behind one of my custom views.
It is built into UIKit (as UIAlertView is part of UIKit), but it's not public.
It shouldn't be too hard to create the same effect, though. It's just a radial gradient, which you can draw in code or Photoshop.
UPDATE: If you must know, the background is a class called _UIAlertNormalizingOverlayWindow with the following class hierarchy:
_UIAlertNormalizingOverlayWindow
_UIAlertOverlayWindow
UIWindow
Create a class called VignetteEffect as a subclass of UIView
Add this code to your -drawRect: method:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colSp = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colSp, (__bridge CFArrayRef)[NSArray arrayWithObjects:(id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:0] CGColor], (id)[[UIColor colorWithRed:0 green:0 blue:0 alpha:0.5] CGColor], nil], 0);
CGContextDrawRadialGradient(context, gradient, self.center, 0, self.center, self.frame.size.height+self.frame.size.height/4, 0);
CGColorSpaceRelease(colSp);
CGGradientRelease(gradient);
}
Tweak the values to your liking.
Add it to any view you have. Et voila. A nice vignette effect.
In fact this effect is achieved by an extra image - a separate window with an imageview is shown underneath a uialertview. That window makes it so you can't select or touch any other views. If you want that image it can be found right here
SVProgressHud does this type of effect, look at the code where SVProgressHUDMaskTypeGradient is detailed.
I contest the picked answer is not correct and is potentially dangerous. Here's my solution:
I copied the gradient drawing code from SVProgressHud into my fork of SSGradientView:
SSGradientView *vignette = [SSGradientView new];
vignette.frame = [UIScreen mainScreen].currentBounds;
vignette.backgroundColor = [UIColor clearColor];
vignette.direction = SSGradientViewDirectionRadial;
UIColor *startColor = // We just need the colorspace.
UIColor *endColor = // Visible vignette color.
vignette.colors = #[
[startColor colorWithAlphaComponent:0.0f],
[endColor colorWithAlphaComponent:1.0f] ];
vignette.locations = #[ #0.4f, #1.0f ];
[view insertSubview:vignette atIndex:0]; // Or equivalent.

creating a bar with core animation

So I am trying to create an animated bar graph using apple core animation. The bar is just basically a rectangular figure, which have a value of 0-100%. When it first appears I wanted it to show an animation going from 0 to x %. How can I draw a rectangular form like this?
UPDATE:
Most probably I will have a bar as an image, so I need to animate this image to a certain height...
If your requirements are really that simple, you could create a view, set its background color and adjust (or animate) its frame.width (or height) as needed.
Of course there are more elaborate ways to do this, but no need to over-engineer for a simple problem.
This should do exactly what you want:
- (UIImageView*)createNewBarWithValue:(float)percent atLocation:(CGPoint)location
{
UIImageView *newBar = [[[UIImageView alloc] initWithFrame:CGRectMake(location.x, location.y, 50, 200)] autorelease];
newBar.image = [UIImage imageNamed:#"bar.png"];
CABasicAnimation *scaleToValue = [CABasicAnimation animationWithKeyPath:#"transform.scale.y"];
scaleToValue.toValue = [NSNumber numberWithFloat:percent];
scaleToValue.fromValue = [NSNumber numberWithFloat:0];
scaleToValue.duration = 1.0f;
scaleToValue.delegate = self;
newBar.layer.anchorPoint = CGPointMake(0.5, 1);
[newBar.layer addAnimation:scaleToValue forKey:#"scaleUp"];
CGAffineTransform scaleTo = CGAffineTransformMakeScale( 1.0f, percent );
newBar.transform = scaleTo;
return newBar;
}

CAGradientLayer not smooth enough?

I'm doing a CAGradientLayer for every background of my UIViews, but I have a small problem. The gradient doesn't seem that smooth enough. The transition between the colors are to present. You can see it in this picture.
This is how I implemented this gradient in the initializer of my UIView.
CAGradientLayer *layer = [CAGradientLayer layer];
layer.colors = [NSArray arrayWithObjects:
(id)[[UIColor darkKinepolisColor] CGColor],
(id)[[UIColor lightKinepolisColor] CGColor],
(id)[[UIColor lightKinepolisColor] CGColor],
(id)[[UIColor darkKinepolisColor] CGColor],
nil];
layer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:0.4],
[NSNumber numberWithFloat:0.6],
[NSNumber numberWithFloat:1],
nil];
layer.startPoint = CGPointMake(0, 0);
layer.frame = self.layer.bounds;
layer.endPoint = CGPointMake(1, 1);
layer.contentsGravity = kCAGravityResize;
[self.layer addSublayer:layer];
Could you, fine developers, help me with this problem? Thanks!
From what I've seen CAGradientLayer does not support dithering, but CGGradient does. See my answer here. Also see the other answers for example on using CGGradient.
Isn't this just a consequence of 8-bit colour values? The steps between the colours are as small as they can possibly be.
i.e. if you want a gradient between rgb(100,100,100) and rgb(109,109,109) it can only be perfectly smooth across a gradient width of 10 pixels. If you tried to draw this gradient across a width of 100 pixels you would get 10 blocks of colour 10px wide and it would look very blocky. What else could happen?
You can make it appear smoother by dithering and adding noise but there is no built in function for this.
So, either, choose colours farther apart, draw the gradient over a shorter distance or make the graphic in Photoshop and apply noise and dithering to make it appear smoother.
I'm developing an app that needs a base gradient, and various other gradients are faded into it, depending on various factors (eg: the time of day). I've found that I can continue to use CAGradientLayer for all but the base gradient. For the base gradient, i created it in photoshop and added 0.554% noise. Almost imperceptible, but it prevents the banding effect when I animate the alpha of CAGradientLayers above it.
So I guess the answer would be that you need dithering/noise on at least one of the gradients to avoid the banding effect. It may also be possible to add this programmatically, or by loading a pre-rendered black image with noise over a CAGradientLayer with a low alpha (~0.1f) on the noise layer.
Rasterize with your device's screen scale, will be very smooth and crisp!
[layer setShouldRasterize:YES];
[layer setRasterizationScale:[UIScreen mainScreen].scale];

iphone - Focus effect (just like UIAlertView)

I know title of my question is so bad, but I don't know how to describe it.
When an UIAlertView pops up, anything else on the screen (except the UIAlertView) becomes a bit darker but can be seen. I call this as Focus effect, because you will know clearly and directly that now the UIAlertView is the focus.
So how can I implement such a focus effect?
thanks
Just add a translucent view below the view you want to "focus" on.
Simple example:
UIView *shieldView = [[[UIView alloc] initWithFrame:myView.bounds] autorelease];
shieldView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.7];
[myView.superview insertSubview:shieldView belowSubview:myView];
UIAlertView actually uses an image with a radial gradient instead of a simple color, in order to highlight the center of the view.
I know this post is a bit old but I thought it might help someone.
Use this code to generate the radial gradient background:
- (UIImage *)radialGradientImage:(CGSize)size start:(float)start end:(float)end centre:(CGPoint)centre radius:(float)radius{
UIGraphicsBeginImageContextWithOptions(size, YES, 1);
size_t count = 2;
CGFloat locations[2] = {0.0, 1.0};
CGFloat components[8] = {start, start, start, 1.0, end, end, end, 1.0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef grad = CGGradientCreateWithColorComponents (colorSpace, components, locations, count);
CGColorSpaceRelease(colorSpace);
CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), grad, centre, 0, centre, radius, kCGGradientDrawsAfterEndLocation);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
CGGradientRelease(grad);
UIGraphicsEndImageContext();
return image;}
Define gradient in the .h file like so:
UIImageView *gradient;
Call your gradient like so:
- (void)addGradient{
CGSize size = self.view.bounds.size;
CGPoint centre = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
float startColor = 1.0f;
float endColor = 0.0f;
float radius = MIN(self.view.bounds.size.width/4, self.view.bounds.size.height/4);
gradient = [[UIImageView alloc] initWithImage:[self radialGradientImage:size
start:startColor
end:endColor
centre:centre
radius:radius]];
[gradient setBackgroundColor:[UIColor clearColor]];
[gradient setUserInteractionEnabled:YES];
[gradient setAlpha:0.6f];
[self.view addSubview:gradient];}
UIAlertView works like this. It fades in an alpha mask image to dim out the background. Once that animation is finished it starts the "bounce in" animation of the dialog.
So to reproduce it you need first to generate an alpha mask with a "bright spot" where your dialog will end up and fade that in. Then use a (few) frame animation(s) to get the bounce effect.
More info here: Creating a Pop animation similar to the presentation of UIAlertView
To make it better than "not good" you could ...
create a UIView in a nib (easiest if the part of your code where you need the effect is already utilising a nib) and then add a translucent graphic (with a 'focus' effect) to that view.
connect the UIView in the nib to an IBOutlet
fade in the graphic using an animation into view hierarchy (omz example shows this)