ChatGPT解决这个技术问题 Extra ChatGPT

Gradients on UIView and UILabels On iPhone [duplicate]

This question already has answers here: Closed 9 years ago.

Possible Duplicate: Manually drawing a gradient in iPhone apps?

My application needs to display text in either a UIView or UILabel but the back ground must be a gradient as opposed to a true UIColor. Using a graphics program to create desired look is no good as the text may vary depending on data returned from a server.

Does anyone know the quickest way to tackle this? Your thoughts are greatly appreciated.


M
Mirko Froehlich

I realize this is an older thread, but for future reference:

As of iPhone SDK 3.0, custom gradients can be implemented very easily, without subclassing or images, by using the new CAGradientLayer:

 UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)] autorelease];
 CAGradientLayer *gradient = [CAGradientLayer layer];
 gradient.frame = view.bounds;
 gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
 [view.layer insertSublayer:gradient atIndex:0];

Take a look at the CAGradientLayer docs. You can optionally specify start and end points (in case you don't want a linear gradient that goes straight from the top to the bottom), or even specific locations that map to each of the colors.


Just in case my comment catches anyone's eye as they consider implementing the other solutions above, DO THIS ONE. It couldn't be easier (but do remember that you'll need to add the QuartzCore framework and import #import <QuartzCore/QuartzCore.h> in your class).
Someone may apply additional effect e.g., cornerRadius. They must add more code here: view.layer.masksToBounds = YES; to make sure thing goes smooth.
My experimentation has shown that images with a fixed gradient image show better performance that using CAGradientLayer.
Hm. My label text disappears when I use this solution.
@HiveHicks I found that if I use a CAGradientLayer for a UILabel that I have to set the backgroundColor to [UIColor clearColor] (or equivalent)
p
pablasso

You can use Core Graphics to draw the gradient, as pointed to in Mike's response. As a more detailed example, you could create a UIView subclass to use as a background for your UILabel. In that UIView subclass, override the drawRect: method and insert code similar to the following:

- (void)drawRect:(CGRect)rect 
{
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    CGGradientRef glossGradient;
    CGColorSpaceRef rgbColorspace;
    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = { 1.0, 1.0, 1.0, 0.35,  // Start color
         1.0, 1.0, 1.0, 0.06 }; // End color

    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);

    CGRect currentBounds = self.bounds;
    CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
    CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
    CGContextDrawLinearGradient(currentContext, glossGradient, topCenter, midCenter, 0);

    CGGradientRelease(glossGradient);
    CGColorSpaceRelease(rgbColorspace); 
}

This particular example creates a white, glossy-style gradient that is drawn from the top of the UIView to its vertical center. You can set the UIView's backgroundColor to whatever you like and this gloss will be drawn on top of that color. You can also draw a radial gradient using the CGContextDrawRadialGradient function.

You just need to size this UIView appropriately and add your UILabel as a subview of it to get the effect you desire.

EDIT (4/23/2009): Per St3fan's suggestion, I have replaced the view's frame with its bounds in the code. This corrects for the case when the view's origin is not (0,0).


I tried this code but it is not correct. Instead of setting currentFrame to self.frame it should be set to self.bounds.
You're right, thanks for pointing it out. I had since corrected my own code, but forgot that this needed updating. The case that gets you is when the origin of the view has a different Y position than 0. The gradient then gets drawn offset from the vertical center.
Can be this applied to UIImage or UIImageView to have any image with gradient?
You can place this over a UIImageView to add a gradient. For altering the contents of an image, you might need to draw the image first to a context, draw the gradient above it in that context, and then save the context as a new image.
Can you tell me how you determine the colors when using the following: CGFloat components[8] = { 1.0, 1.0, 1.0, 0.35, 1.0, 1.0, 1.0, 0.06 }; How do you know what 1.0 and 0.35 are for example?
T
ThomasW

Note: The results below apply to older versions of iOS, but when testing on iOS 13 the stepping doesn't occur. I don't know for which version of iOS the stepping was removed.

https://i.stack.imgur.com/NlPaw.png

To get more attractive results it is better to use CGGradient.


Would you mind posting code equivalent to Mirko Froehlich's example with CGGradient?
Brad Larson's code above using CGGradient is good enough. I would have made my comment a comment on one of those answers, but I don't have enough points yet.
IMPORTANT: I deleted my CGGradient code recently and replaced it with CAGradientLayer. You know why? I was drawing it on like 30 Cells, and the performance impact was BIG. CAGradient was much better performance-wise (Explains the tiny quality difference).
If the height of the view you want to add gradient is small, CAGradientLayer is compromise you can afford, I guess. If the view height is significant, better use a CGGradient. As such, its highly unlikely that you will put a view of larger height in a scroll view.
To Mazyod's comment, if you find yourself generating the same gradient (or any other drawing operation) many times in the same way and that affects performance, consider doing it just once, caching the result into an UIImage and using that repeatedly. Works great for multi-layer drawings too.
K
Kendall Helmstetter Gelner

You could also use a graphic image one pixel wide as the gradient, and set the view property to expand the graphic to fill the view (assuming you are thinking of a simple linear gradient and not some kind of radial graphic).


With this approach, make sure to have two different gradients: one for regular displays and one for retina displays.
One important benefit of this solution, over the coded solutions, is that color gradients are normally a decoration property, and really shouldn't be expressed in code at all. This is what graphics guys are for. Writing code to express colors is a poor separation of logic from presentation. When the marketing team decides to change the colors, and a graphics guy asks how to do that, he's not going to like being told that you just adjust the color array on the CAGradientLayer in MyLabelView.m ... and nor should he. Doesn't matter much for a one person shop, but for teams it does.
I agree with Nate on that, it can be good to let designers change assets as easily as possible.
@Nate - Your code for your logic should be separate from your code for your presentation. That doesn't mean your only possible solution is to have a graphics file, and quite frankly, for each bitmap in your application, you're setting yourself up for more work in the future. Gradient code will always look great. Your bitmap will only look good until Apple decides to increase the resolution of the screen again.
Ditto for views with dynamic heights, like a cell whose height depends on how much text it has to hold.
J
Jeff Lambert

Mirko Froehlich's answer worked for me, except when i wanted to use custom colors. The trick is to specify UI color with Hue, saturation and brightness instead of RGB.

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = myView.bounds;
UIColor *startColour = [UIColor colorWithHue:.580555 saturation:0.31 brightness:0.90 alpha:1.0];
UIColor *endColour = [UIColor colorWithHue:.58333 saturation:0.50 brightness:0.62 alpha:1.0];
gradient.colors = [NSArray arrayWithObjects:(id)[startColour CGColor], (id)[endColour CGColor], nil];
[myView.layer insertSublayer:gradient atIndex:0];

To get the Hue, Saturation and Brightness of a color, use the in built xcode color picker and go to the HSB tab. Hue is measured in degrees in this view, so divide the value by 360 to get the value you will want to enter in code.


I can add this CAGradientLayer, but how can one remove it from the myView ? I would like to have the view as earlier before drawing the CAGradientLayer.Is there any for this ?
I would loop through myView.layer.sublayers until you get the CAGradientLayer, then call removeFromSuperlayer. for (CALayer *currentLayer in [self.view.layer sublayers]) { if([currentLayer isKindOfClass: [CAGradientLayer class]] ) { [currentLayer removeFromSuperlayer]; } }
Wtf, why not just store the gradient layer as an instance property and remove it when not needed anymore?
A
Anna Billstrom

This is what I got working- set UIButton in xCode's IB to transparent/clear, and no bg image.

UIColor *pinkDarkOp = [UIColor colorWithRed:0.9f green:0.53f blue:0.69f alpha:1.0];
UIColor *pinkLightOp = [UIColor colorWithRed:0.79f green:0.45f blue:0.57f alpha:1.0];

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = [[shareWordButton layer] bounds];
gradient.cornerRadius = 7;
gradient.colors = [NSArray arrayWithObjects:
                   (id)pinkDarkOp.CGColor,
                   (id)pinkLightOp.CGColor,
                   nil];
gradient.locations = [NSArray arrayWithObjects:
                      [NSNumber numberWithFloat:0.0f],
                      [NSNumber numberWithFloat:0.7],
                      nil];

[[recordButton layer] insertSublayer:gradient atIndex:0];

Ok change- I now subclass the UIButton and use the highlight method: "- (void)setHighlighted:(BOOL)highlight { if (highlight != self.highlighted){" inside this, do the layer switcheroo. works great.
How one can remove this CAGradientLayer from the View ?
Find it and remove it from the button's layer of views, if I recall.
m
mahboudz

I achieve this in a view with a subview that is an UIImageView. The image the ImageView is pointing to is a gradient. Then I set a background color in the UIView, and I have a colored gradient view. Next I use the view as I need to and everything I draw will be under this gradient view. By adding a second view on top of the ImageView, you can have some options whether your drawing will be below or above the gradient...