How to adjust an UIButton's imageSize? - ios

How can I adjust the image size of the UIButton? I am setting the image like this:
[myLikesButton setImage:[UIImage imageNamed:#"icon-heart.png"] forState:UIControlStateNormal];
However this fills up the image to the full button, how do I make the image smaller?
Historic note:
For this now 10+ yr old question, you now typically just set the point size using setPreferredSymbolConfiguration

If I understand correctly what you're trying to do, you need to play with the buttons image edge inset. Something like:
myLikesButton.imageEdgeInsets = UIEdgeInsets(top: 30, left: 30, bottom: 30, right: 30)

Tim's answer is correct, however I wanted to add another suggestion, because in my case there was a simpler solution altogether.
I was looking to set the UIButton image insets because I didn't realize that I could set the content mode on the button's UIImageView, which would have prevented the need to use UIEdgeInsets and hard-coded values altogether. Simply access the underlying imageview on the button and set the content mode:
myButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
See UIButton doesn't listen to content mode setting?
Swift 3
myButton.imageView?.contentMode = .scaleAspectFit

Swift 3:
button.setImage(UIImage(named: "checkmark_white"), for: .normal)
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill
button.imageEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10)

Here is the other solution to scale an imageView of UIButton.
button.imageView?.layer.transform = CATransform3DMakeScale(0.8, 0.8, 0.8)

You can also do that from inteface builder like this.
I think it's helpful.

If your image is too large (and you can't/don't want to just made the image smaller), a combination of the first two answers works great.
addButton.imageView?.contentMode = .scaleAspectFit
addButton.imageEdgeInsets = UIEdgeInsetsMake(15.0, 15.0, 15.0, 5.0)
Unless you get the image insets just right, the image will be skewed without changing the contentMode.

you can use imageEdgeInsets property
The inset or outset margins for the rectangle around the button’s image.
[self.btn setImageEdgeInsets:UIEdgeInsetsMake(6, 6, 6, 6)];
A positive value shrinks, or insets, that edge—moving. A negative value expands, or outsets, that edge.

Heres the Swift version:
myButton.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

Swift 4
You would need to use these two lines of code, in this specific order. All you need is to change the top and bottom value of the edge insets.
addButton.imageView?.contentMode = .scaleAspectFit
addButton.imageEdgeInsets = UIEdgeInsetsMake(10.0, 0.0, 10.0, 0.0)

Insetting the image works for me, but I also needed the button Type to be Custom and the button Style to be Default. The defaults for a button added in Xcode 13 are System for Type and Plain for Style.
self.imageEdgeInsets = UIEdgeInsets(top: 3.0, left: 3.0, bottom: 3.0, right: 3.0)

If you are using symbolic images for the button, then this solution is better:
button.setPreferredSymbolConfiguration(UIImage.SymbolConfiguration(pointSize: 48), forImageIn: .normal)

With the help of Tim C's answer, I was able to create an extension on UIButton using Swift that allows you to specify the image frame by using the .setImage() function with an extra frame parameter
extension UIButton{
func setImage(image: UIImage?, inFrame frame: CGRect?, forState state: UIControlState){
self.setImage(image, forState: state)
if let frame = frame{
self.imageEdgeInsets = UIEdgeInsets(
top: frame.minY - self.frame.minY,
left: frame.minX - self.frame.minX,
bottom: self.frame.maxY - frame.maxY,
right: self.frame.maxX - frame.maxX
)
}
}
}
Using this, if you wanted to set the frame of a UIButton to CGRectMake(0, 0, 64, 64), and set the image of it to myImage with a frame of CGRectMake(8, 8, 48, 48), you could use
let button: UIButton = UIButton(frame: CGRectMake(0, 0, 64, 64))
button.setImage(
myImage,
inFrame: CGRectMake(8, 8, 48, 48),
forState: UIControlState.Normal
)

When changing icon size with
UIEdgeInsetsMake(top, left, bottom, right), keep in mind button dimensions and the ability of UIEdgeInsetsMake to work with negative values as if they are positive.
Example: Two buttons with height 100 and aspect 1:1.
left.imageEdgeInsets = UIEdgeInsetsMake(40, 0, 40, 0)
right.imageEdgeInsets = UIEdgeInsetsMake(40, 0, 40, 0)
left.imageEdgeInsets = UIEdgeInsetsMake(40, 0, 40, 0)
right.imageEdgeInsets = UIEdgeInsetsMake(45, 0, 45, 0)
left.imageEdgeInsets = UIEdgeInsetsMake(40, 0, 40, 0)
right.imageEdgeInsets = UIEdgeInsetsMake(60, 0, 60, 0)
Examples 1 and 3 are identical since ABS(100 - (40 + 40)) = ABS(100 - (60 + 60))

Updated for Swift > 5
set the size:
button.frame = CGRect(x: 0, y: 0, width: 44, height: 44)
set margins:
button.imageEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

One approach is to resize the UIImage in code like the following. Note: this code only scales by height, but you can easily adjust the function to scale by width as well.
let targetHeight = CGFloat(28)
let newImage = resizeImage(image: UIImage(named: "Image.png")!, targetHeight: targetHeight)
button.setImage(newImage, for: .normal)
fileprivate func resizeImage(image: UIImage, targetHeight: CGFloat) -> UIImage {
// Get current image size
let size = image.size
// Compute scaled, new size
let heightRatio = targetHeight / size.height
let newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
// Create new image
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Return new image
return newImage!
}

Swift 3
I set myButton width and height to 40 and my padding from EdgeInsetsMake is 15 all sides. I suggest to add a background color to your button to see the actual padding.
myButton.backgroundColor = UIColor.gray // sample color to check padding
myButton.imageView?.contentMode = .scaleAspectFit
myButton.imageEdgeInsets = UIEdgeInsetsMake(15, 15, 15, 15)

Updated for Swift 3
yourButtonName.imageEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10)

If you don't want to play around with image insets and you are using auto layout (which I assume you do), another solution is to add size constraints to the image view. Make sure the constraints have a priority of 999, otherwise auto layout will complain.
guard let imageView = button.imageView else { return }
let size = CGSize(width: 20, height: 20)
let sizeConstraints = [
imageView.widthAnchor.constraint(equalToConstant: size.width),
imageView.heightAnchor.constraint(equalToConstant: size.height)
]
imageView.translatesAutoresizingMaskIntoConstraints = false
sizeConstraints.forEach { $0.priority = UILayoutPriority(rawValue: priority) }
NSLayoutConstraint.activate(sizeConstraints)
imageView.contentMode = .scaleAspectFit

i think, your image size is also same as button size then you put image in background of the button like :
[myLikesButton setBackgroundImage:[UIImage imageNamed:#"icon-heart.png"] forState:UIControlStateNormal];
you mast have same size of image and button.i hope you understand my point.

Related

UIButton ImageView contentmode doesn't work

I just started an app for iOS in Swift, I'm doing the design programmatically (I mean I'm not using storyboard)
I have a UIButton with an image inside, I would like the image to fill the entire button. Right now it just show the image in my button, but smaller than the button, center in top of the button frame.
My code is :
let buttonLocation = UIButton(frame: CGRect(x: 0, y: 0, width: buttonRoundedSize, height: buttonRoundedSize))
buttonLocation.setImage(UIImage(named: "locate_button"), for: .normal)
buttonLocation.backgroundColor = .white
buttonLocation.layer.cornerRadius = frame.width / 2
buttonLocation.myExtension()
Here are the things I've tried (none of theses worked) :
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.autoresizingMask = .flexibleWidth
self.autoresizingMask = .flexibleHeight
self.imageView?.contentMode = .scaleAspectFill
If I just put the contentMode line it doesn't work either. The imageView doesn't return nil i've checked..
Thanks!
Set the contentMode of buttonLocation's imageView as scaleAspectFill or scaleToFill to cover the entire frame of button.
let buttonLocation = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 500))
buttonLocation.setImage(UIImage(named: "image"), for: .normal)
buttonLocation.backgroundColor = .gray
buttonLocation.layer.cornerRadius = buttonLocation.frame.width / 2
buttonLocation.imageView?.contentMode = .scaleAspectFill //Here........
Example:
Try using below code and remove other properties which are being set for this button :
let buttonLocation = UIButton(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
buttonLocation.setImage(UIImage(named: "cat"), for: .normal)
buttonLocation.backgroundColor = .white
buttonLocation.layer.cornerRadius = 50
buttonLocation.imageView?.contentMode = .scaleAspectFill
buttonLocation.clipsToBounds = true
self.view.addSubview(buttonLocation)
Here make sure that the image which you are setting in the button is larger than the frame of the button. You can change frame and corner radius values in the above code. I just tried with the above code and getting below output.
You can comment if this is not your expected output.
Hope this helps.
Answer on your question: https://stackoverflow.com/a/26263623/3047318
TLDR: set contentMode in viewDidLoad
you need to use setBackgroundImage property instead of setImage.
buttonLocation.setBackgroundImage(UIImage(named: "locate_button"), for: .normal)
Maybe this can help you:
let buttonLocation = UIButton(type: .custom)
buttonLocation.frame = CGRect(x: 0, y: 0, width: buttonRoundedSize, height: buttonRoundedSize)
buttonLocation.setImage(UIImage(named: "locate_button"), for: .normal)
buttonLocation.imageView?.contentMode = .scaleAspectFill
buttonLocation.contentHorizontalAlignment = .fill
buttonLocation.contentVerticalAlignment = .fill
buttonLocation.backgroundColor = .white
buttonLocation.layer.cornerRadius = frame.width / 2
buttonLocation.clipsToBounds = true
You need to add contentHorizontalAlignment and contentVerticalAlignment properties and don't forget set clipsToBounds to true to see the corner raius effect.

CALayer's contentsCenter not working in swift

I want to increase an image button's touch area, and as I searched, I found a way to use contentsCenter property of CALayer.
https://developer.apple.com/documentation/quartzcore/calayer/1410740-contentscenter
The example of the document above shows an image centered inside the layer that can be resized as the size of layer changes, while maintaining the ratio defined in contentsCenter property.
But when I used contentsCenter property, it just stretches the image's center - (0.5, 0.5).
So I added contentsGravity property to .center, but it results in contentsCenter property not working.
button.frame = CGRect(x: 0, y: 0, width: 300, height: 300)
button.center = view.center
button.backgroundColor = .clear
let image = UIImage(named: "profileImage")
button.layer.contents = image?.cgImage
button.layer.contentsCenter = CGRect(x: 0.25, y: 0.25, width: 0.5, height: 0.5)
button.layer.contentsGravity = .center
//button.layer.contentsScale = image?.scale
button.layer.borderColor = UIColor.black.cgColor
button.layer.borderWidth = 2
button.layer.cornerRadius = 5
button.layer.masksToBounds = true
How can I make the image center inside a button with insets maintaining ratio?
You can increase the UIButton Size using the below Code
button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

titleView in NavigationItem doesn't consider frame height in iOS 11

I've updated to Xcode 9, and I have a titleView in my NavigationItem created in this way:
let logo = UIImageView(frame: CGRect(x: 0, y: 0, width: 70, height: 25))
logo.image = UIImage.logo
logo.contentMode = .scaleAspectFit
self.navigationItem.titleView = logo
The result is that it doesn't consider anymore the frame height.
we can control the size and position of UINavigationbar titleview. Don't use to set the imageview as titleview directly. in here create a custom UIView and then set the frame as what you need requirement and add the logo as its subview
do like
let supportVie = UIView(frame: CGRect(x: 0, y: 0, width: 70, height: 25))
// Here you can set View width and height as per your requirement for displaying supportVie position in navigationbar
//supportVie.backgroundColor = UIColor.red
let logo = UIImageView(image: UIImage.logo ) //UIImage(named: "SelectAnAlbumTitleLettering")
logo.frame = CGRect(x: 45, y: 5, width: supportVie.frame.size.width, height: supportVie.frame.size.height)
// customize the origin as (45,5) but can pass them as your requirement.
supportVie.addSubview(logo)
//supportVie.contentMode = .center;
navigationItem.titleView = supportVie

How to change the selection area of a UIBarButtonItem?

I'm trying to change the selection area of a UIBarButtonItem within the navigation controller. Basically I'm trying to change the allowable area that will make that UIBarButtonItem selectable. Right now, the size of my UIBarButtonItem is 40x40, but I can easily select it even if my finger is not touching the button at all.
Here's to illustrate what I mean:
The green represents the size of the UIBarButtonItem. The red represents the allowable area that makes the UIBarButtonItem selectable.
How can I change the width of the red area?
Here's a snippet of code if helpful:
changeMuscleMap = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40) )
changeMuscleMap.setImage(UIImage(named: "change"), forState: .Normal)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: changeMuscleMap)
Thanks!
If you want larger click area than required, try the below code, then making a UIBarButtonItem With custom button:
#implementation CustomButton
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
UIEdgeInsets extendTouchInsets = UIEdgeInsetsMake(20, 0, 20, 0);
CGRect bounds = self.bounds;
bounds.origin.x -= extendTouchInsets.left;
bounds.origin.y -= extendTouchInsets.top;
bounds.size.width += extendTouchInsets.left + extendTouchInsets.right;
bounds.size.height += extendTouchInsets.top + extendTouchInsets.bottom;
return CGRectContainsPoint(bounds, point);
}
#end
You can use the following code to increase or decrease UIBarButtonItem button width.
changeMuscleMap = UIButton(frame: CGRect(x: 0, y: 0, width: 120, height: 40) )
changeMuscleMap.setImage(UIImage(named: "change"), forState: .Normal)
changeMuscleMap.contentHorizontalAlignment = .left
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: changeMuscleMap)
It can be achieved by making a UIBarButtonItem with custom item as UIImageView instead of UIButton. Try the below code. I hope this will solve it now.
changeMuscleMap = UIImageView(image:UIImage(named: "change"))
imageView.frame = CGRectMake(0, 0, 40, 40)
let leftBarBtn = UIBarButtonItem(customView: imageView)
self.navigationItem.leftBarButtonItem = leftBarBtn
I was able to achieve what I wanted by using #Damien Romito's provided answer here:
UINavigationBar UIBarButtonItems much larger click area than required
Updated for Swift 3.0:
let buttonContainer: UIView = UIView(frame: CGRect(x: 0, y: 0, width: 27, height: 30) )
let barButton: UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30) )
buttonContainer.addSubview(barButton)
barButton = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
barButton(#imageLiteral(resourceName: "image"), for: UIControlState.normal)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: buttonContainer)
barButton(self, action: #selector(ViewController.doSomething), for: UIControlEvents.touchUpInside)

Insets to UIImageView?

I would like to achieve :
Scale to fill mode of image view
Insets to UIImageView so that image
does not meet the edges of the UIImageView
Bottomline: I want to increase the touch area of the UIImageView without stretching the image beyond certain size.
Is it possible through storyboard ? If not can anyone tell how to do it programmatically ?
I can achieve similar functionality through a UIButton and setting image inset through storyboard but I can't find any such thing with UIImageView.
You can add inset to UIImage instead of UIImageView.
Swift 4
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.image = UIImage(named: "example")?.withAlignmentRectInsets(UIEdgeInsets(top: -4, left: 0, bottom: -4, right: 0))
Using the method given here :
UIImage *image= [MyUtil imageWithImage:[UIImage imageNamed:#"MyImage"] scaledToSize:CGSizeMake(80, 80)];
UIImageView* imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imgView.contentMode = UIViewContentModeCenter;
[imgView setImage:image];
Now your insets become 100-80 i.e. 20 .
This is a workaround to provide insets to an UIImageView. Better answers are welcomed.
It looks like you want to have a touchable imageView, so why not using an UIButton with image and imageEdgeInsets?
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[b setFrame:someFrame];
[b setImage:img forState:UIControlStateNormal];
b.imageEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
[b addTarget:self action:#selector(imageClicked:) forControlEvents:UIControlEventTouchUpInside];
My Code
minus inset: smaller image
plus inset: larger image
Swift4
private lazy var imageView: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFit
let insetValue: CGFloat = -8
view.image = image.withAlignmentRectInsets(UIEdgeInsets(top: insetValue, left: insetValue, bottom: insetValue, right: insetValue))
return view
}()
Do not suffer, try this:
After trying for a while I found this solution:
extension UIImage {
func withInset(_ insets: UIEdgeInsets) -> UIImage? {
let cgSize = CGSize(width: self.size.width + insets.left * self.scale + insets.right * self.scale,
height: self.size.height + insets.top * self.scale + insets.bottom * self.scale)
UIGraphicsBeginImageContextWithOptions(cgSize, false, self.scale)
defer { UIGraphicsEndImageContext() }
let origin = CGPoint(x: insets.left * self.scale, y: insets.top * self.scale)
self.draw(at: origin)
return UIGraphicsGetImageFromCurrentImageContext()?.withRenderingMode(self.renderingMode)
}
}
Usage example:
let margin:CGFloat = 16
self.imageView.image = image?.withInset(UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin))
Source
One option is to use withAlignmentRectInsets, but it doesn't work with the layer border.
Another way is to use resizableImage(withCapInsets: resizingMode: .stretch) where you'd want to use positive inset values for smaller images.
Sample code:
private func setupQRImageView() -> UIImageView {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.layer.borderColor = Default.imageBorderColor.cgColor
imageView.layer.borderWidth = 4.0
imageView.layer.cornerRadius = 6.0
let imageInset: CGFloat = 8.0
imageView.image = UIImage(named: "QR")?.resizableImage(withCapInsets: UIEdgeInsets(top: imageInset, left: imageInset, bottom: imageInset, right: imageInset), resizingMode: .stretch)
return imageView
}
Produces the following result:
You can add a UIView first and then you add your UIImageView inside the UIView with a padding of whatever you like, and if you want your gesture to get affected only in UIImageView, make it UserInteractionEnable to yes or if you want the gesture to be in the whole then you can add the gesture to the whole UIView
Try work around with UIImage within UIView like this ;

Resources