adding 'click' effect on UIButton - ios

i have a UIButton that when I tap it, goes down giving effect of a click/tap.
My problem is that I have to add a black border around UIButton, but adding this keeps border static and it doesn't move down with the UIButton, the code that let me have click effect on UIButton is this:
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGSize size = self.bounds.size;
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect faceRect = CGRectMake(0, 0, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(faceRect.size, NO, 0.0);
[[self faceColorForState:self.state] set];
[self drawRoundedRect:faceRect radius:self.radius context:UIGraphicsGetCurrentContext()];
UIImage *faceImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[self sideColorForState:self.state] set];
CGRect sideRect = CGRectMake(0, size.height * 1.0 / 4.0, size.width, size.height * 3.0 / 4.0);
[self drawRoundedRect:sideRect radius:self.radius context:context];
CGRect faceShrinkedRect;
if(self.state == UIControlStateSelected || self.state == UIControlStateHighlighted) {
faceShrinkedRect = CGRectMake(0, self.depth, size.width, size.height - self.margin);
} else {
faceShrinkedRect = CGRectMake(0, 0, size.width, size.height - self.margin);
}
[faceImage drawInRect:faceShrinkedRect];
}

You're overcomplicating things using drawRect:. The easiest way to do this is to create 2 images for the background of your button, one with a black border. Then use:
[myButton setBackgroundImage:[UIImage imageNamed:<up-image>] forState:UIControlStateNormal];
[myButton setBackgroundImage:[UIImage imageNamed:<down-image>] forState:UIControlStateHighlighted];
You could re-use your code to create the images in code if necessary.
Maybe a category method on UIImage say:
+(UIImage *)buttonBackgroundImageWithColor:(UIColor *) color highlighted: (BOOL) highlighted

Related

How do a set an opaque 1px high shadow image for a UINavigationBar?

In my application delegate's didFinishLaunchingWithOptions function, I try to customize the appearance of my navigation bar.
[UINavigationBar appearance].translucent = NO;
[[UINavigationBar appearance]
setBackgroundImage:[UIImage
imageWithColor:[UIColor whiteColor]
size:CGSizeMake(1.0f, 1.0f)
]
forBarMetrics:UIBarMetricsDefault
];
[UINavigationBar appearance].shadowImage = [UIImage
imageWithColor:[UIColor redColor]
size:CGSizeMake(0.5f, 0.5f)
];
I expect a 1px tall opaque red shadow. Instead I am given a 2px tall translucent red shadow. How can make it appear exactly as I want it to? I've done the analogous appearance settings to UITabBar. It, on the other hand, behaves nicely.
The category function that creates dynamic images is defined as so:
+ (UIImage*)imageWithColor:(UIColor *)color size:(CGSize)size
{
CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
You need to create a graphics context that is retina-aware :
let pixelScale = UIGraphicsBeginImageContextWithOptions(fillRect.size, false, UIScreen.main.scale)
After that, your shadow image will become 1 physical pixel tall.
Here's full code:
extension UIImage {
static func imageWithColor(color: UIColor) -> UIImage {
let pixelScale = UIScreen.main.scale
let pixelSize = 1 / pixelScale
let fillSize = CGSize(width: pixelSize, height: pixelSize)
let fillRect = CGRect(origin: CGPoint.zero, size: fillSize)
UIGraphicsBeginImageContextWithOptions(fillRect.size, false, pixelScale)
let graphicsContext = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(graphicsContext, color.CGColor)
CGContextFillRect(graphicsContext, fillRect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
On GitHub:
https://github.com/salutis/swift-image-with-color
Uh...is this what you want?
In my view controller, I simply added a UIView with 1 pixel height and an offset of 44pixels:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
[self setupNavBar];
}
-(void)setupNavBar
{
self.navigationItem.title = #"Contacts";
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
UIView *bar = [[UIView alloc] initWithFrame:CGRectMake(0, 44, applicationFrame.size.width, 1.0)];
bar.backgroundColor = [UIColor redColor];
[self.navigationController.navigationBar addSubview:bar];
}
You can change the color to whatever you want.

IOS 6 and Auto Layout custom button height wrong

I've created a really simple custom flat button:
#implementation HAFlatButton
+ (id)buttonWithColor:(UIColor *)aColor
{
id button = [super buttonWithType:UIButtonTypeCustom];
[button setFlatColor:aColor];
CGRect frame = [button frame];
frame.size.height = 200;
[button setFrame:frame];
return button;
}
+ (id)defaultButton
{
return [HAFlatButton buttonWithColor:[HAColors buttonColor]];
}
- (void)setFlatColor:(UIColor *)flatColor
{
_flatColor = flatColor;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4.0];
CGContextSaveGState(context);
[path addClip];
[_flatColor setFill];
CGContextFillRect(context, rect);
CGColorSpaceRelease(colorSpace);
}
#end
When I add a button to my auto layout using [HAFlatButton defaultButton], it's just barely taller than the button text, but when I add a [UIButton buttonWithType:UIButtonTypeRoundedRect] to my layout, it has the proper insets around the label.
What am I doing wrong?
You need to do one or more of the following:
Add a height constraint.
Override the intrinsicSize return value.
Adjust the vertical content hugging priority.

set cornerRadius and setbackgroundimage to UIButton

I am trying to set cornerRadius of UIButton but I dont know how to do it.
If I do like this:
button.layer.cornerRadius = 5;
works well, if I do like this :
button.layer.cornerRadius = 5;
[button setBackgroundColor:[UIColor colorWithPatternImage:radialGradient]];
the corners are not rounded.
I know I could solve this whit
[button.layer setMasksToBounds:YES];
but I specifically looking for different solution, because I add some arrows to the button and if I set mask to bounds the arrow are masked.
EDIT :
radialGradient is made whit func
+ (UIImage *)getRadialGradientImage:(CGSize)size centre:(CGPoint)centre radius:(float)radius startColor:(UIColor *)startColor endColor:(UIColor *)endColor{
// Initialise
UIGraphicsBeginImageContextWithOptions(size, YES, 1);
// Create the gradient's colours
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
const CGFloat *component_first = CGColorGetComponents([startColor CGColor]);
CGFloat red1 = component_first[0];
CGFloat green1 = component_first[1];
CGFloat blue1 = component_first[2];
const CGFloat *component_second = CGColorGetComponents([endColor CGColor]);
CGFloat red2 = component_second[0];
CGFloat green2 = component_second[1];
CGFloat blue2 = component_second[2];
const CGFloat components[8] = { red1,green1,blue1,1,red2,green2,blue2,1}; // End color
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef myGradient = CGGradientCreateWithColorComponents (myColorspace, components, locations, num_locations);
// Normalise the 0-1 ranged inputs to the width of the image
CGPoint myCentrePoint = CGPointMake(centre.x * size.width, centre.y * size.height);
float myRadius = MIN(size.width, size.height) * radius;
// Draw it!
CGContextDrawRadialGradient (UIGraphicsGetCurrentContext(), myGradient, myCentrePoint,
0, myCentrePoint, myRadius,
kCGGradientDrawsAfterEndLocation);
// Grab it as an autoreleased image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// Clean up
CGColorSpaceRelease(myColorspace); // Necessary?
CGGradientRelease(myGradient); // Necessary?
UIGraphicsEndImageContext(); // Clean up
return image;
}
Here is how I would create a button through code and set its background color.
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(100, 100, 100,50);
[btn setTitle:#"Hello" forState:UIControlStateNormal];
[btn setBackgroundColor:[UIColor colorWithRed:128.0/255.0f green:0.0/255.0f blue:0.0/255.0f alpha:0.7]];
btn.frame = CGRectMake(100.0, 100.0, 120.0, 50.0);//width and height should be same value
btn.clipsToBounds = YES;
btn.layer.cornerRadius = 20;//half of the width
btn.layer.borderColor=[UIColor redColor].CGColor;
btn.layer.borderWidth=2.0f;
[self.view addSubview:btn];
Below is the image of the button that is related with the above code
You can always play around with code and create the colors that you need for background and border. Hope this would help you out.
btn.clipsToBounds = YES;
i Just added this and it worked for me. I was actually able to set corner radius to UIButton with setting an image to that particular UIButton.
If you are using the image in your button background then you need to set clipsTobounds to true.
button.layer.cornerRadius = 10
button.clipsToBounds = true
You can also have:
btn.clipsToBounds = true;
//create button like this
UIButton *cancel=[[UIButton alloc]initWithFrame:CGRectMake(9, 9,35,35)];
cancel.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:#"BackX.png"]];
[cancel setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[cancel.layer setBorderColor: [[UIColor blackColor] CGColor]];
[cancel.layer setBorderWidth: 1.0];
cancel.contentMode=UIViewContentModeScaleAspectFill;
cancel.clipsToBounds=YES;
cancel.layer.cornerRadius=8.0;
[cancel addTarget:self action:#selector(cancelbtnclk1:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:cancel];
Before that add QuartzCore Framework and import QuartzCore/CoreAnimation.h in your .h file.
hope it will helps you..
Using UIGraphicsImageRenderer, you can use the cgContext property of UIGraphicsImageRendererContext to access the CGContext and add a rounded path:
UIGraphicsImageRenderer(size: size).image { context in
let rect = CGRect(origin: .zero, size: size)
let clipPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
context.cgContext.addPath(clipPath)
context.cgContext.setFillColor(self.cgColor)
context.cgContext.fillPath()
}
Added as an extension to UIColor:
extension UIColor {
public func image(_ size: CGSize = CGSize(width: 10, height: 10), cornerRadius: CGFloat = 4) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { context in
let rect = CGRect(origin: .zero, size: size)
let clipPath = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
context.cgContext.addPath(clipPath)
context.cgContext.setFillColor(self.cgColor)
context.cgContext.fillPath()
}
}
}
This approach will also allow you to add a shadow to the button unlike using clipsToBounds.
Set corner Radius in Identity Inspector for Button. See Pic
Set Background image in Attribute Inspector for Button. See pic

How to flip an image and have it display correctly in all states

I needed to flip and image and then add it to a button. I used the following code
UIButton *playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playButton.frame = CGRectMake(xing, ying, dx,dy);
[playButton setTitle:text forState:UIControlStateNormal];
[playButton addTarget:self action:function forControlEvents:UIControlEventTouchUpInside];
UIImage *buttonImageNormal = [UIImage imageNamed:#"Begin-Set-Button.png"];
UIImage * other = [UIImage imageWithCGImage:buttonImageNormal.CGImage
scale:1.0 orientation: UIImageOrientationUpMirrored];
UIImage * awesome = [other stretchableImageWithLeftCapWidth:12 topCapHeight:0];
[playButton setBackgroundImage:awesome forState:UIControlStateNormal];
It displays the image correctly, yet when I click on it, the buttons displays the unflipped image.
In my attempt to fix this I added the following line of code
[playButton setBackgroundImage:awesome forState:UIControlStateHighlighted];
Yet when I click on the button, it does not darken the color like it does for buttons that I create with the unflipped image.
How do I code it so that when I use a flipped image, it shows the flipped image darkened when it is pressed? I know how to manually darken the image, but I was wondering if there was a way to automatically do it with a simple function call?
Instead of flipping the image. Have you tried flipping the imageView?
UIImage *img = [UIImage imageNamed:#"path/to-image.png"];
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
button.imageView.transform = CGAffineTransformMakeRotation(M_PI); // flip the image view
[button setImage:img forState:UIControlStateNormal];
This is what I did to keep the selection state highlighting and not have to deal with the image flipping.
I had the same problem as well and so I did a search to see this post. I suspected that perhaps the image is not "solidified" and so the mirror operation is not permanent.
Checking out the docs for "imageWithCGImage:" shows the following:
Discussion
This method does not cache the image object. You can use the methods of the Core Graphics framework to create a Quartz image reference.
And so, the mirroring of the image does not hold for events unless a new image is created in a new context. I've also been working on a method to draw an image on an arbitrary size canvas to create buttons with the same background. You can use this method to create an image that maintains CG operations, such as mirroring:
+ (UIImage *)imageWithCanvasSize:(CGSize)canvasSize withImage:(UIImage *)anImage
{
if (anImage.size.width > canvasSize.width ||
anImage.size.height > canvasSize.height)
{
// scale image first
anImage = [anImage scaleWithAspectToSize:canvasSize];
}
CGRect targetRect = CGRectZero;
CGFloat xOrigin = (canvasSize.width - anImage.size.width) / 2;
CGFloat yOrigin = (canvasSize.height - anImage.size.height) / 2;
targetRect.origin = CGPointMake(xOrigin, yOrigin);
targetRect.size = anImage.size;
UIGraphicsBeginImageContextWithOptions(canvasSize, NO, anImage.scale);
[anImage drawInRect:targetRect];
UIImage *canvasedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return canvasedImage;
}
- (UIImage*)scaleWithAspectToSize:(CGSize)newSize
{
CGSize scaledSize = newSize;
float scaleFactor = 1.0;
if (self.size.width > self.size.height)
{
scaleFactor = self.size.width / self.size.height;
scaledSize.width = newSize.width;
scaledSize.height = newSize.height / scaleFactor;
}
else
{
scaleFactor = self.size.height / self.size.width;
scaledSize.height = newSize.height;
scaledSize.width = newSize.width / scaleFactor;
}
UIGraphicsBeginImageContextWithOptions(scaledSize, NO, 0.0);
CGRect scaledImageRect = CGRectMake(0.0, 0.0, scaledSize.width, scaledSize.height);
[self drawInRect:scaledImageRect];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}

How do you change UIButton image alpha on disabled state?

I have a UIButton with an image and on its disabled state, this image should have .3 alpha.
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *arrowImage = [UIImage imageNamed:#"arrow.png"];
[button setImage:arrowImage forState:UIControlStateNormal];
// The arrow should be half transparent here
[button setImage:arrowImage forState:UIControlStateDisabled];
How do I accomplish this?
UPDATE: I noticed, by default UIButton does reduce the alpha of the image on disabled state (probably at .5?). But I'd like to know how to fine-tune this value.
If setting alpha while the button is disabled doesn't work, then just make your disabled image at the alpha value you desire.
Just tested this, you can set the alpha on the UIButton, regardless of state and it works just fine.
self.yourButton.alpha = 0.25;
Credit goes to #bryanmac for his helpful answer. I used his code as a starting point, but found the same thing can be achieved without using a UIImageView.
Here's my solution:
- (UIImage *)translucentImageFromImage:(UIImage *)image withAlpha:(CGFloat)alpha
{
CGRect rect = CGRectZero;
rect.size = image.size;
UIGraphicsBeginImageContext(image.size);
[image drawInRect:rect blendMode:kCGBlendModeScreen alpha:alpha];
UIImage * translucentImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return translucentImage;
}
Set the button's background image for disabled state:
UIImage * disabledBgImage = [self translucentImageFromImage:originalBgImage withAlpha:0.5f];
[button setBackgroundImage:disabledBgImage forState:UIControlStateDisabled];
EDIT:
I refined my solution further by creating a category on UIImage with this method:
- (UIImage *)translucentImageWithAlpha:(CGFloat)alpha
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
[self drawInRect:bounds blendMode:kCGBlendModeScreen alpha:alpha];
UIImage * translucentImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return translucentImage;
}
Set the button's background image for disabled state:
UIImage * disabledBgImage = [originalBgImage translucentImageWithAlpha:0.5f];
[button setBackgroundImage:disabledBgImage forState:UIControlStateDisabled];
You need two instances of UIImage, one for enabled and one for disabled.
The tough part is for the disabled one, you can't set alpha on UIImage. You need to set it on UIImageView but button doesn't take an UIImageView, it takes a UIImage.
If you really want to do this, you can load the same image into the disabled button state after creating a resultant image from the UIImageView that has the alpha set on it.
UIImageView *uiv = [UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrow.png"];
// get resultant UIImage from UIImageView
UIGraphicsBeginImageContext(uiv.image.size);
CGRect rect = CGRectMake(0, 0, uiv.image.size.width, uiv.image.size.height);
[uiv.image drawInRect:rect blendMode:kCGBlendModeScreen alpha:0.2];
UIImage *disabledArrow = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[button setImage:disabledArrow forState:UIControlStateDisabled];
That's a lot to go through to get an alpha controlled button image. There might be an easier way but that's all I could find. Hope that helps.
Subclassing UIButton and extending the setEnabled: method seems to work:
- (void) setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
if (enabled) {
self.imageView.alpha = 1;
} else {
self.imageView.alpha = .25;
}
}
I haven't tested it thoroughly, but did find the value resets if the screen is rotated. Adding the same code to the setFrame: method fixes that, but I'm not sure if there are other situations where this value is changed.
In case anyone needs it, here's an answer in Swift:
Subclass UIButton and override enabled
class MyCustomButton: UIButton {
override var enabled: Bool {
didSet{
alpha = enabled ? 1.0 : 0.3
}
}
Set the class of any button you want to have this property to "MyCustomButton" (or whatever you choose to name it)
#IBOutlet weak var nextButton: MyCustomButton!
I found that none of these solutions really work. The cleanest way of doing it is to subclass, and instead of using self.imageView, just add your own custom imageView like this:
_customImageView = [[UIImageview alloc] initWithImage:image];
Then add your custom alpha like so:
- (void)setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
if (enabled) {
_customImageView.alpha = 1.0f;
} else {
_customImageView.alpha = 0.25f;
}
}
Swift 3:
extension UIImage {
func copy(alpha: CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(at: CGPoint.zero, blendMode: .normal, alpha: alpha)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
Usage:
button.setImage(image.copy(alpha: 0.3), for: .disabled)
The button’s image view. (read-only)
#property(nonatomic, readonly, retain) UIImageView *imageView
Although this property is read-only, its own properties are read/write.
A swift 4 version of Steph Sharp's answer:
extension UIImage {
func translucentImageWithAlpha(alpha: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0)
let bounds = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
self.draw(in: bounds, blendMode: .screen, alpha: alpha)
let translucentImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return translucentImage!
}
}
Which you can use like this:
if let image = self.image(for: .normal) {
self.setImage(image.translucentImageWithAlpha(alpha: 0.3), for: .disabled)
}
- (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image; }
Use: [btnRegister setBackgroundImage:[self imageWithColor:[UIColor lightGrayColor]] forState:UIControlStateDisabled];
btnRegister.enabled = false;
Here's a solution that extends the UIButton class. No need to sub-class. In my example, button alpha is set to 0.55 if .isEnabled is false, and 1.0 if it's true.
Swift 5:
extension UIButton {
override open var isEnabled: Bool {
didSet {
self.alpha = isEnabled ? 1.0 : 0.55
}
}
}

Resources