Adding a layer of color over a UIImage - ios

I was hoping to make my UIImage "highlight" briefly upon being tapped. Not sure of the color yet, but let's say blue for arguments sake. So you tap the image, it briefly looks blue and then it navigates you to a details page to edit something on another screen.
From some initial reading it seems the right course of action is to use the Quartz framework and do this:
imageView.layer.backgroundColor = UIColor.blueColor()
imageView.layer.opacity = 0.7
I guess the idea would be you change the background of the layer behind the image, and then by setting the opacity of the image, the blue "bleeds through" a little bit, giving you a slightly blue image?
When I try the above, however, a blue border goes around the image itself, and based upon the opacity, the blue is either dark or light. The actual image does not become any more blue, but it does react to the opacity (meaning if I set it to something like .1, the image is very faded and barely visible). Why does the image react correctly, but not show blue?
Thanks so much!

As far as I know changing the opacity will change the opacity for the WHOLE view, meaning not just the UIImage that the UIImageView holds. So instead of fading to reveal the UIImageView's background color, instead the opacity of the whole view is just decreased as you're seeing.
Another way you could do it though would be to add an initially transparent UIView on top of your UIImageView and change its opacity instead:
UIView *blueCover = [[UIView alloc] initWithFrame: myImageView.frame];
blueCover.backgroundColor = [UIColor blueColor];
blueCover.layer.opacity = 0.0f;
[self.view addSubview: blueCover];
[UIView animateWithDuration:0.2f animations^{
blueCover.layer.opacity = 0.5f
}];

Here's how I use tint and tint opacities in IOS 9 with Swift -
//apply a color to an image
//ref - http://stackoverflow.com/questions/28427935/how-can-i-change-image-tintcolor
//ref - https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming
func getTintedImage() -> UIImageView {
var image : UIImage
var imageView : UIImageView
image = UIImage(named: "someAsset")!
let size : CGSize = image.size
let frame : CGRect = CGRectMake((UIScreen.mainScreen().bounds.width-86)/2, 600, size.width, size.height)
let redCover : UIView = UIView(frame: frame)
redCover.backgroundColor = UIColor.redColor()
redCover.layer.opacity = 0.75
imageView = UIImageView()
imageView.image = image.imageWithRenderingMode(UIImageRenderingMode.Automatic)
imageView.addSubview(redCover)
return imageView
}

Is this tap highlight perhaps something you could do with a UIButton? UIButton has all these states off the bat and might be a bit easier to work with, specially if there's something that actually needs to happen after you tap it. Worst case scenario is you have a UIButton that does not trigger any method when tapped.

You can try changing the tint color instead:
UIImage *image = [UIImage imageNamed:#"yourImageAsset"];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.tintColor = [UIColor blueColor];
imageView.image = image;

Related

UINavigationBar Custom Color Hairline Border

First of all, I did search this before posting this question but if there is an answer out there, it is buried under the millions of questions about how to remove the default bottom border of a navigation bar.
I do NOT want to remove the bottom border ("shadow") of the navigation bar.
I am trying to "theme" my app by the usual method of using appearance proxies.
I can globaly change most visual attributes of UINavigationBar with code like the following:
let navigationBarProxy = UINavigationBar.appearance()
navigationBarProxy.isTranslucent = false
navigationBarProxy.barTintColor = myBarBackgroundColor
navigationBarProxy.tintColor = myBarTextColor
Regarding the 'hairline' bottom border of the bar (or as it is known, the "shadow"), I can either set the default one by doing nothing or specifying nil:
navigationBarProxy.shadowImage = nil
...or I can specify a custom color by assigning a solid image of the color I'm after:
navigationBarProxy.shadowImage = UIImage.withColor(myBorderColor)
(uses helper extension:)
extension UIImage {
public static func withColor(_ color: UIColor?, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
let rect = CGRect(origin: CGPoint.zero, size: size)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
let actualColor = color ?? .clear
context?.setFillColor(actualColor.cgColor)
context?.fill(rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
However, the approach above gives me (on retina devices) a 1pt, 2px border, whereas the default, light gray one is actually 0.5pt, 1px (a.k.a. "hairline").
Is there any way to achieve a 0.5 pt (1px), custom-colored bottom border (shadow) for UINavigationBar?
I guess I could use a runtime generated, background image that is for the most part solid, but has a 1px border of my choice color "baked in" at the bottom. But this seems inelegant at best, and I'm not sure how it would work when that navigation bar height changes: is the image sliced, or simply stretched, or what?
Based on the chosen answer found here (with small changes because it was old):
How to change the border color below the navigation bar?
// in viewDidLoad
UIView * navBorder = [[UIView alloc] initWithFrame:CGRectMake(0,
self.navigationController.navigationBar.frame.size.height, // <-- same height, not - 1
self.navigationController.navigationBar.frame.size.width,
1/[UIScreen mainScreen].scale)]; // <-- 5/5S/SE/6 will be 0.5, 6+/X will be 0.33
// custom color here
[navBorder setBackgroundColor:customColor];
[self.navigationController.navigationBar addSubview:navBorder];
Credit here for finding scale programmatically:
Get device image scale (e.g. #1x, #2x and #3x)
*NOTE:
iPhone 6+/X are x3, so 1px height will be 0.33pt
iPhone 5/5S/SE/6 are x2, so 1px height will be 0.5pt
Tested in simulator, may need to verify on actual devices.
Visually same as default nav bar with a custom color.
I believe you want to remove the shadow. This should help with that.
[[UINavigationBar appearance] setShadowImage:[UIImage new]];
If you want to have different coloured shadow then you can create a image with your desired colour and use it instead of
[UIImage new]
You can use something like this for generating an image yourself
+ (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
const CGFloat alpha = CGColorGetAlpha(color.CGColor);
const BOOL opaque = alpha == 1;
UIGraphicsBeginImageContextWithOptions(rect.size, opaque, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

IOS Button border thickness lssue,why?

UIButton top border appears thicker than the following ,but sometimes correct ,why?
code:
UIImage * sanImage = [UIimage imageNamed:#"product_bt1_normal"];
[self.saveBtn setBackgroundImage:[sanImage
stretchableImageWithLeftCapWidth:sanImage.size.width/3
topCapHeight:sanImage.size.height/3] forState:UIControlStateNormal];
Are you trying to make a button? If so, perhaps use a UIButton instead? You can control the border with button.layer.borderWidth = 1.0f
If you're set on using an image, create a UIImageView, and modify the border thickness that way:
UIImageView *iv = [[UIImageView alloc] initWithImage:sanImage];
[iv.layer setBorderWidth:0.5f];
It could be because of off-pixel boundaries. Since you are using height/3.0f, your image is maybe not returning a well-behaved image.
Also, there is a new stretchable image method you should be using, resizableImageWithCapInsets:.
So try this code out:
[self.saveBtn setBackgroundImage:[sanImage resizableImageWithCapInsets:UIEdgeInsetsMake(3.0f, 3.0f, 3.0f, 3.0f)] forState:UIControlStateNormal];
You might need to mess with the values for the insets a bit, I don't know the dimensions of your button image.

How to set shadow gradient image on UIImageView iOS

Hey I'm new to iPhone and I have been trying to set shadow for UIImageView using shadow gradient image i.e. "Image-Shadow.png" using below code.
imageView.layer.shadowColor = [UIColor colorWithPatternImage:[UIImage
imageNamed:#"Image-Shadow.png"]].CGColor;
imageView.layer.shadowOffset = CGSizeMake(0, 1);
imageView.layer.shadowOpacity = 1;
imageView.clipsToBounds = NO;
imageView.layer.shouldRasterize = YES;
My problem is, i am not getting shadow in my UIImageView using above code. Can you please tell me that is this the correct way to add shadow image in UIImageView or it is possible with some other way?
Problem is with the image. I don't think you can provide a pattern image to create a shadow!
Give a proper color like [UIColor blackColor].CGColor and it should work.
The problem is with this line:
imageView.layer.shadowOffset = CGSizeMake(0, 1);
where you are setting the shadow, but not giving it a visible space to display itself.
Positive or negative values should be given to both parameters.
Positive values stretch "down" and "to the right" of your view, negative values stretch "to the left" and "to the up" of your view.
here is my solution as per my requirement :
//set shadow gradient on image view
UIView *shadowView = [[UIView alloc] initWithFrame:imageView.frame]; //add view behind the image view
shadowView.layer.contents = (id) [UIImage imageNamed:#"Image-Shadow.png"].CGImage;
[imageView addSubview:shadowView];
Thanks to all.

Why is there a rough black edge when rounding corner of UIButton?

I'm adding a UIButton (with background image) to the navigation bar and setting rounded corners with a border. I'm getting a strange black outline on the corners:
Here's the code I'm using to create the button from viewDidLoad:
ProfileImageService *profileImageService = [ProfileImageService getService];
CGRect frame = CGRectMake(0, 0, 32, 32);
UIButton *button = [[UIButton alloc] initWithFrame:frame];
[button setBackgroundImage:profileImageService.profileImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(showMenu) forControlEvents:UIControlEventTouchUpInside];
button.layer.cornerRadius = button.frame.size.height / 2.0;
button.layer.masksToBounds = YES;
button.clipsToBounds = YES;
button.layer.borderWidth = 3;
button.layer.borderColor = [UIColor colorWithRed:0 green:0.67 blue:0.97 alpha:1].CGColor;
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
How can I make the border smooth and get rid of the subtle black outline?
What you're seeing is your profile image (the person's face) bleeding through the edge. This is because the border is anti-aliased and so has small amounts of transparency through which the profile image can bleed.
Since you're clipping the profile image and the border together, there's nothing stopping the profile image from extending through the border. (i.e. clipsToBounds won't clip the content to the inside of the border; it clips everything to the outside of the border) You can prove this by using a bright red image: you'll see a bright red fringe.
If you can just make the profile image circular and the right size beforehand (either offline or in code) then you'll avoid this problem.
The other solutions I see are to either implement your own drawRect: method and do the drawing yourself or to create another sub-layer of the button to hold the image which is clipped in the same way, but without the border. (Either way, you probably need to make your own UIControl for this rather than using UIButton, as manipulating the button's layers could lead to weird behavior.)
Edit: Here are some other solutions, too: iOS: Rounded rectangle with border bleeds color

How do I prevent a button's background image from stretching?

I'm allocating a UIButtonTypeCustom to a UIView with a background image that is smaller than the button's frame.
Reason why the image is smaller is because I'm trying to add more of a "target area" for the UIButton. However, the image is being scaled to the full size of the frame, rather than just being the image's size.
I have tried setting the UIButton and UIButton's imageView's contentMode property to UIViewContentModeScaleAspectFit, but no luck, the image still gets stretched out.
Is there a way to do what I'm trying to do programmatically?
Thanks in advance!
A lot of people make the same mistake you do in regards to button images and then jump through hoops trying to make the button behave as they expect it to. Let's clear this up once and for all:
A UIButton has two types of images it can display -- a foreground image and a background image. The background image for a button is expected to replace the button's background texture. As such, it makes sense that it stretches to fill the entire background. However, the button's foreground image is expected to be an icon that may or may not display alongside text; it will not stretch. It may shrink if the frame is smaller than the image, but it will not stretch. You can even set the alignment of the foreground image using the Control alignment properties in Interface Builder.
A button's foreground and background image can be set in code like this:
// stretchy
[self setBackgroundImage:backgroundImage forState:UIControlStateNormal];
// not stretchy
[self setImage:forgroundImage forState:UIControlStateNormal];
You don't have access to the background imageView, but there is fully working workaround:
EDIT: There is an even better workaround then what I posted originally.
You can create a UIImage from any color, and call -setBackgroundImage:forState.
See bradley's answer, here:
https://stackoverflow.com/a/20303841/1147286
Original answer:
Instead of calling -setBackgroundImage:forState:, create a new UIImageView and add it as a subview of the button.
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:img];
bgImageView.contentMode = UIViewContentModeScaleAspectFit;
[bgImageView setFrame:CGRectMake(0, 0, videoButton.frame.size.width, videoButton.frame.size.height)];
bgImageView.tag = 99;
[yourButton addSubview:bgImageView];
[yourButton bringSubviewToFront:yourButton.imageView];
Create the imageview
Set the content mode and frame
I also set a recognizable tag, so that when the screen rotates I can easily find my custom imageView in the button's subviews and reset its frame
Add it as a subview to the button
Bring the frontal imageView of the button to the front so our custom imageView doesn't overlap it
When the button needs to rotate just find the imageView by its tag and reset its frame:
UIImageView *bgImageView = (UIImageView *)[button viewWithTag:99];
[bgImageView setFrame:CGRectMake(0, 0, newWidth, newHeight)];
The cleanest and easiest way it probably to use the title insets of the button.
You set your image as the button image, and then you change the left title inset to match minus the width of your image:
myButton.titleEdgeInsets = UIEdgeInsetsMake(0, -myImage.width, 0, 0)
This will move the text back where it was before the image was added to its left. You can also use this value to add some padding to you button.
Stumbled on this problem too.
Adding image programmatically, as memmons thoroughly explained, did not help:(
I had a button 100x40 and image 100x100, it would appear squeezed, not fitted, as one would infer from "Aspect Fit" option. Actually, non of those view options had an effect.
I just had to rescale it so it would fit on a button, then use setImage:
UIImage *img=[UIImage imageNamed:#"myimage.png"];
CGImageRef imgRef = [img CGImage];
CGFloat imgW = CGImageGetWidth(imgRef);
CGFloat imgH = CGImageGetHeight(imgRef);
CGFloat btnW = myBttn.frame.size.width;
CGFloat btnH = myBttn.frame.size.height;
//get lesser button dimension
CGFloat minBtn=btnW;
if (btnW>btnH) {
minBtn=btnH;
}
//calculate scale using greater image dimension
CGFloat scl=imgH/minBtn;
if (imgW>imgH) {
scl=imgW/minBtn;
}
//scale image
UIImage *scaledImage = [UIImage imageWithCGImage:[img CGImage] scale:(img.scale * scl) orientation:(img.imageOrientation)];
//clean up
UIGraphicsEndImageContext();
//set it on a button
[myBttn setImage:scaledImage forState:UIControlStateNormal];
It is simple as:
ImageBtn.imageView?.contentMode = .scaleAspectFit
ImageBtn.setImage(chosenImage, for: .normal)
Another consideration is the BaseLine constraint. If your buttons have this constraint set (depicted as a horizontal or vertical line through multiple controls on your layout), it will cause your images to stretch without stretching the underlying button control. If your button is otherwise properly constrained (leading/trailing and top/bottom spaces, and etc...) removing the BaseLine constraint should have no impact on the layout, while allowing the foreground image to scale properly to the underlying button shape.
Answerbot answers the question with what is proper and correct to do. Don't fight the OS and use things as intended is always good advice. However, sometimes you need to break the rules.
I was able to mask the enlarged background image (not prevent it) by overlaying it with a black CAlayer then overlaying again with a properly resized image CAlayer. This was all done by creating a subclass of UIButton and overwriting the setHighlighted method.
NEED CODE?
- (void)setHighlighted:(BOOL)highlighted
{
super.highlighted = highlighted;
//
//Whenever an image needs to be highlighted, create a dimmed new image that is correctly sized. Below it is a englarged stretched image.
//
if (highlighted != _previousHighlightedSate)
{
_previousHighlightedSate = highlighted;
if (highlighted)
{
//Create a black layer so image can dim
_blackLayer = [CALayer layer];
_blackLayer.bounds = self.bounds;
CGRect rect = _blackLayer.bounds;
rect.size.width = rect.size.width*2;
rect.size.height = rect.size.height*2;
_blackLayer.bounds = rect;
_blackLayer.backgroundColor = [[UIColor blackColor] CGColor];
//create image layer
_nonStretchImageLayer = [CALayer layer];
_nonStretchImageLayer.backgroundColor = [UIColor blackColor].CGColor;
_nonStretchImageLayer.bounds = CGRectMake(0 , 0, self.bounds.size.width, self.bounds.size.height);
_nonStretchImageLayer.frame = CGRectMake(0 , 0, self.bounds.size.width, self.bounds.size.height);
_nonStretchImageLayer.contentsGravity = kCAGravityResizeAspect;//default is to resize
_nonStretchImageLayer.contents = (id)self.imageView.image.CGImage;
_nonStretchImageLayer.opacity = 0.5;
//add layers to image view
[self.imageView.layer addSublayer:_blackLayer];
[self.imageView.layer addSublayer:_nonStretchImageLayer];
}
else
{
//remove from image view
[_blackLayer removeFromSuperlayer];
[_nonStretchImageLayer removeFromSuperlayer];
//nil them out.
_blackLayer = nil;
_nonStretchImageLayer = nil;
}
}
Inspiration for this work around came from here
I guess the easiest solution is to use UIImage's resizableImageWithCapInsets method. Use UIEdgeInsetsMake to configure the free spaces.
might help someone
button.subviews.first?.contentMode = .scaleAspectFit
Swift version of Zoltán Matók answer
Just a copy of my code using SnapKit to do auto layout and syntatic sugar Then library for initilizations, it should work similar for normal Apples way of programatic layout.
let backButton = UIButton(type: .custom).then { (button) in
let image = UIImage(named: "backButtonIcon")?.withRenderingMode(.alwaysOriginal)
let imageView = UIImageView(image: image)
button.addSubview(imageView)
imageView.snp.makeConstraints { (make) in
make.center.equalTo(button.snp.center)
}
button.bringSubviewToFront(imageView)
button.contentMode = .scaleAspectFit
button.backgroundColor = .clear
button.isUserInteractionEnabled = true
button.isEnabled = true
button.imageView?.contentMode = .scaleAspectFit
button.imageView?.snp.makeConstraints({ (make) in
make.height.width.equalTo(24)
})
button.snp.makeConstraints { (make) in
make.height.width.equalTo(24)
}
}
You can use uibutton.imageView.contentMode for no stretching:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(posX, posY, widthButton, heightButton);
[button setTitle:#"" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"imageNamed"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"imageNamedHighlighted"] forState:UIControlStateHighlighted];
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
[button addTarget:self action:#selector(functionMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
What you need to do is add your image as a UIImageView.
Than add a button with transperent background (UIColor ClearColor) on top of it with your desired width and height.

Resources