UIButton with GradientLayer obscures image and darkens gradient - ios

I have an UIButton here where I'd like to have a gradient as the background below the image (symbol with transparent background), but I'm facing two different problems.
First of the CAGradientLayer seems to overlay on top of the image no matter how I try to add it, obscuring the image completely.
Secondly the gradient itself seems to be darkened a lot, like the button was disabled, which it isn't.
Here's my code:
self.backButton = [[UIButton alloc] initWithFrame:CGRectMake(15, 35, 28, 28)];
[backButton addTarget:self
action:#selector(goBack)
forControlEvents:UIControlEventTouchUpInside];
[backButton setBackgroundColor:[UIColor clearColor]];
CAGradientLayer *buttonGradient = [CAGradientLayer layer];
buttonGradient.frame = backButton.bounds;
buttonGradient.colors = [NSArray arrayWithObjects:
(id)[[UIColor colorWithRed:.0
green:.166
blue:.255
alpha:1] CGColor],
(id)[[UIColor colorWithRed:.0
green:.113
blue:.255
alpha:1] CGColor],
nil];
[buttonGradient setCornerRadius:backButton.frame.size.width / 2];
[backButton.layer insertSublayer:buttonGradient
atIndex:0];
[backButton setImage:[UIImage imageNamed:#"backarrow.png"]
forState:UIControlStateNormal];
[backButton setEnabled:NO];
[topbarView addSubview:backButton];

So I managed to get around this by doing a [button bringSubviewToFront:button.imageView] after adding the gradient. Seems that no matter what I do the new layer will add on top of the imageView, so I need to push that to the front afterwards.
Objective-C:
[button bringSubviewToFront:button.imageView]
Swift 4.1:
button.bringSubview(toFront:button.imageView!)

Alternatively, you can insert the gradient layer below the image view's layer
CAGradientLayer* gradient = [CAGradientLayer layer];
// ...
[button.layer insertSublayer:gradient below:button.imageView.layer];

SWIFT 3
Here goes an example: (Based on Neil answer)
let layer = CAGradientLayer()
layer.frame = CGRect(x: 0, y: 0, width: myButtonOutlet.layer.frame.size.width, height: myButtonOutlet..layer.frame.size.height)
layer.colors = [UIColor.white.cgColor, pixelColorReceta.cgColor]
layer.masksToBounds = true
layer.cornerRadius = myButtonOutlet.layer.cornerRadius
myButtonOutlet.layer.insertSublayer(layer, below: myButtonOutlet..imageView?.layer)
Happy coding :)

Swift 3
You can move the image above the gradient:
button.imageView?.layer.zPosition = 1
or insert the gradient under the image:
button.layer.insertSublayer(gradientLayer, below: button.imageView?.layer)

Related

iOS linear gradient on UIButton not displaying properly

I am trying to add a gradient to my custom UIButton, but when I run it, the button appears transparent instead of showing the desired gradient.
Here is my code:
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.titleLabel.font = [UIFont fontWithName:#"SFUIDisplay-Medium" size:16];
[button setTitleColor:[UIColor whiteColor]
forState:UIControlStateNormal];
button.layer.cornerRadius = [UIButton cornerRadius];
button.contentEdgeInsets = UIEdgeInsetsMake(13, 0, 13, 0);
button.frame = CGRectMake(50, 50, 100, 40); //this line is new
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = button.layer.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:
(id)[UIColor lightGrayColor].CGColor,
(id)[UIColor blackColor].CGColor,
nil];
gradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
nil];
gradientLayer.cornerRadius = button.layer.cornerRadius;
gradientLayer.startPoint = CGPointMake(0, 0.5); //this line is new
gradientLayer.endPoint = CGPointMake(1, 0.5); //this line is new
[button.layer addSublayer:gradientLayer];
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:#"Test"
forState:UIControlStateNormal];
[button constrainHeight:56];
[button addTarget:self
action:#selector(doAction)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
If I add in this line:
button.backgroundColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1.0];
then the button looks like this.
Your code works fine after I have assigned frame, e.g.:
button.frame = CGRectMake(50, 50, 100, 40);
And added to view controller's view, e.g.:
[self.view addSubview:button];
First, try adding your button to the view. Then, it doesn't look like you are setting a start point and end point. For a linear gradient, try something like:
[self.view addSubview: button];
gradientLayer.startPoint = CGPointMake(x: 0, y: 0.5);
gradientLayer.endPoint = CGPointMake(x: 1, y: 0.5);
This configuration will ensure the gradient starts at the middle of the left side and end at the middle of the right side. You can tinker with the values for different types of gradients using the idea described below:
(0, 0) || (1, 0)
||
=========================
||
(1, 0) || (1, 1)

iOS How to set a gradient color to UITextField cursor

How to set a gradient color to UITextField cursor? The effect is just like the picture below:
here is the picture
you can ask disigner for an one pix picture ,then do like as follows:
UIColor *collor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"one"]];
self.textfield.tintColor = collor;
To change UITextField Cursor color, you have set it as Tint Color of Type UIColor.
textField.tintColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"gradient"]];
Do this for all text fields in your app using the UITextField appearance proxy:
[[UITextField appearance] setTintColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"gradient"]]];
BONUS:
If you need to set gradient color for UIView:
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = #[(id)[UIColor whiteColor].CGColor, (id)[UIColor blackColor].CGColor];
[view.layer insertSublayer:gradient atIndex:0];

change or remove my CAGradientLayer when button is disabled

I'm using a CAGradientLayer as the background for my button.
cell.showOnMap.clipsToBounds = YES;
cell.showOnMap.layer.cornerRadius = 10.0f;
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, cell.showOnMap.frame.size.width, cell.showOnMap.frame.size.height)];
CAGradientLayer *gradient2 = [CAGradientLayer layer];
gradient2.frame = view2.bounds;
gradient2.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:0.00 green:0.66 blue:0.71 alpha:1.0] CGColor], (id)[[UIColor colorWithRed:0.18 green:0.27 blue:0.75 alpha:1.0] CGColor], nil];
[cell.showOnMap.layer insertSublayer:gradient2 atIndex:0];
[cell.showOnMap bringSubviewToFront:cell.showOnMap.imageView];
There are cases in which my button, showOnMap will be disabled. In this case, I would like the CAGradient layer to change from lightGrayColor to grayColor OR remove the layer altogether. Here's the enable/disable code.
if(entry.address == nil)
{ [cell.showOnMap setEnabled:NO];
cell.showOnMap.layer.sublayers = nil;
}
else
[cell.showOnMap setEnabled:YES];
So far I have tried putting the entire gradient code within the else portion, then placing the same code in the if(entry.address == nil) but with gray colors as the gradients. This didn't work; the button was always the initial blue gradient.
I also tried cell.showOnMap.layer.sublayers = nil; but this removes the text and button image I have, leaving only a background with rounded edges.
[[cell.showOnMap.layer.sublayers objectAtIndex:0] removeFromSuperlayer]; didn't produce any change and [[cell.showOnMap.layer.sublayers objectAtIndex:0] removeFromSuperview]; caused a crash
How can I reference my CAgradient layer and remove it when my button is disabled?
Create a gradientLayer property inside your cell. Then init your gradientLayer to this property. And then you can safely reference it later to either modify it or removeFromSuperlayer.

UIButton border that changes thickness when selected

I have a subclassed UIButton
- (void)configureButton
{
self.titleLabel.font = [UIFont systemFontOfSize:18.0f];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
CALayer *border = [CALayer layer];
border.backgroundColor = [UIColor whiteColor].CGColor;
border.frame = CGRectMake(0, self.frame.size.height, self.frame.size.width, 1.0);
[self.layer addSublayer:border];
}
- (void)setSelected:(BOOL)selected
{
[super setSelected:selected];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];
// remove all layers ?
if (selected) {
CALayer *border = [CALayer layer];
border.backgroundColor = [UIColor whiteColor].CGColor;
border.frame = CGRectMake(0, self.frame.size.height, self.frame.size.width,3.0);
[self.layer addSublayer:border];
self.titleLabel.font = [UIFont boldSystemFontOfSize:18.0f];
} else {
CALayer *border = [CALayer layer];
border.backgroundColor = [UIColor whiteColor].CGColor;
border.frame = CGRectMake(0, self.frame.size.height, self.frame.size.width,1.0);
[self.layer addSublayer:border];
self.titleLabel.font = [UIFont systemFontOfSize:18.0f];
}
}
The default state of the button has a 1.0 bottom border. When it is selected, the border should become 3.0. How can I clear the previously added layer and re add a 1.0 border when a user toggles a button?
I believe there is an easier way to do what you need. If you subclass the UIButton just because you just want to have different types button border, you can try a different way but a lot easier.
Use 2 different button background image, one with border 1 and another with border 3.
[self.button setBackgroundImage:[UIImage imageNamed:#"ImageWithBorder1"] forState:UIControlStateNormal];
[self.button setBackgroundImage:[UIImage imageNamed:#"ImageWithBorder3"] forState:UIControlStateHighlighted];
[self.button setBackgroundImage:[UIImage imageNamed:#"ImageWithBorder3"] forState:UIControlStateSelected];
With an additional button image, it might just take extra 10 kb on your project.

How do I set UIImage bounds?

I am having some trouble setting the correct location of the image for a UIImageView, which I am adding as a subview to a UIButton. I start by creating the button and adding some sublayers:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:bounds];
//add gradient background
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = button.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[UIColor colorWithRed:.5 green:.5 blue:.5 alpha:1.0].CGColor, (id)[[UIColor blackColor] CGColor], nil];
[button.layer insertSublayer:gradient atIndex:0];
//set green background
CALayer *layer = [CALayer layer];
layer.frame = CGRectInset(button.bounds, 5, 5);
layer.backgroundColor = [UIColor colorWithRed:.55 green:.8 blue:.5 alpha:1.0].CGColor;
[button.layer insertSublayer:layer above:gradient];
I then use two methods to set the image. The first (which I use if no image is saved) uses an image resource that is scaled to be the right size, and is part of the app bundle. I set this with the following code:
UIImageView *v = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"profile.png"]];
[v setBounds:CGRectInset(button.bounds, 8, 8)];
[button addSubview:v];
The trouble comes from when I use an image taken through the camera, and saved to the file system. I am setting this to the background using the following code:
//path is an NSString set the the documents location of the image.
UIImageView *v = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:path]];
[v setBounds:CGRectInset(button.bounds, 8, 8)];
[v setContentMode:UIViewContentModeScaleToFill];
[button addSubview:v];
The output to the screen now shows the image way offset from where it should be:
What am I doing wrong? How do I correctly scale/fit the image in its parent view? I have tried simply setting the button image, but nothing appeared (perhaps the image was behind the sublayers?).
Use -setFrame method to set the frame of imageview instead of bounds
[v setFrame:CGRectInset(button.bounds, 8, 8)];
Thus the code become
UIImageView *v = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:path]];
[v setFrame:CGRectInset(button.bounds, 8, 8)];
[v setContentMode:UIViewContentModeScaleToFill];
[button addSubview:v];

Resources