'UIButton not respect Aspect Fill contentMode after resizing animation
I am use auto layout. Following is the initial state of the view.
In the centre is a button contained in a view. The button has contentMode Aspect Fill, and the image is set as the background image of the button.
Then I use the following code to transform the view, which will enlarge the centre card to fill the screen, and move the image to the top of the view:
cardTrailingSpaceConstraint.constant = 0
cardLeadingSpaceConstraint.constant = 0
cardView.removeConstraint(cardAspectRatioConstraint)
let cardHeightConstraint = NSLayoutConstraint(item: cardView, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: 1.0, constant: 0)
view.addConstraint(cardHeightConstraint)
dishImageButton.removeConstraint(dishButtonBottomSpaceConstraint)
let dishButtonHeightConstraint = NSLayoutConstraint(item: dishImageButton, attribute: .Height, relatedBy: .Equal, toItem: cardView, attribute: .Height, multiplier: 0.2, constant: 0)
cardView.addConstraint(dishButtonHeightConstraint)
cardView.setNeedsUpdateConstraints()
UIView.animateWithDuration(0.7, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: nil, animations: { [unowned self] () -> Void in
self.cardHeader.alpha = 0
self.cardView.layer.cornerRadius = 0
self.cardView.layoutIfNeeded()
}) { [unowned self] (finished) -> Void in
}
The result is:
However, it is not what I want. The button is not respecting the contentMode and the image then get stretched.
Can anyone tell me how to maintain the Aspect Fill contentMode of the button?
Solution 1:[1]
- Set the button type to
UIButtonTypeCustom
(“Custom” in a storyboard or xib). - Set the button's image, not the button's background image.
- In
viewDidLoad
, setbutton.imageView.contentMode
toUIViewContentMode.ScaleAspectFill
.
Solution 2:[2]
Swift 3
Going off of Rob's answer:
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 80, height: 30))
btn.setImage(UIImage(named: "albumsBtn"), for: UIControlState.normal)
btn.imageView?.contentMode = UIViewContentMode.scaleAspectFit
btn.addTarget(self.navigationController, action: #selector(CustomGalleryViewController.showAlbums(_:)), for: UIControlEvents.touchUpInside)
let item = UIBarButtonItem(customView: btn)
self.navigationItem.leftBarButtonItem = item
Solution 3:[3]
In Xcode 10.1 you can use interface builder. In the attributes inspector on the right hand side, use the Control section as follows:
Solution 4:[4]
Try this before [btn setImage:forState:] usage:
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
btn.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
- Swift 4
//Center
btn.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.center
//Left
btn.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.left
//Right
btn.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
Solution 5:[5]
Swift 2.x version:
let myButton = UIButton(type: .Custom)
myButton.frame = CGRectMake(0, 0, 200, 20)
myButton.backgroundColor = UIColor.blackColor()
myButton.imageView.contentMode = .ScaleAspectFill // ALTERNATIVE: .ScaleAspectFit
myButton.setImage(UIImage(named: "myImageName"), forState: .Normal)
myButton.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
view.addSubview(myButton)
Solution 6:[6]
Swift 2
button.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
all contentMode:
.ScaleToFill
.ScaleAspectFit
.ScaleAspectFill
.Redraw
.Center
.Top
.Bottom
.Left
.Right
.TopLeft
.TopRight
.BottomLeft
.BottomRight
Solution 7:[7]
Swift 4.2
Extension
// Inspectable image content mode
extension UIButton {
/// 0 => .ScaleToFill
/// 1 => .ScaleAspectFit
/// 2 => .ScaleAspectFill
@IBInspectable
var imageContentMode: Int {
get {
return self.imageView?.contentMode.rawValue ?? 0
}
set {
if let mode = UIViewContentMode(rawValue: newValue),
self.imageView != nil {
self.imageView?.contentMode = mode
}
}
}
}
UPDATE: This and other answers doesn't work for me in ios 11. The most closest answer is by @Jaro but i think it's better to make an UIImageView and over it add a button, or create a custom class of UIImageView that would have a gesture recognizer and click animation.
Solution 8:[8]
The language seems to have had an update.
I had to dig a little deeper to get xscoder solution for:
myButton.imageView.contentMode = .ScaleAspectFill
the updated version is as follows:
myButton.imageView?.contentMode = UIView.ContentMode.scaleAspectFit
Solution 9:[9]
Swift
for button has contentMode Aspect Fill:
btn.contentHorizontalAlignment = .fill
btn.contentVerticalAlignment = .fill
btn.imageView?.contentMode = .scaleAspectFill
Solution 10:[10]
Swift 5.0
code for a custom UIButton
with .scaleAspectFill
var mainImageButton : UIButton = {
var imageButton = UIButton()
imageButton.translatesAutoresizingMaskIntoConstraints = false
imageButton.contentMode = .scaleAspectFit
imageButton.backgroundColor = .white
imageButton.layer.cornerRadius = 80 / 2
// The short way
imageButton.contentMode = .scaleAspectFit
// The long way
imageButton.imageView?.contentMode = UIView.ContentMode.scaleAspectFill
imageButton.clipsToBounds = true
return imageButton
}()
Solution 11:[11]
might help someone
button.subviews.first?.contentMode = .scaleAspectFit
Solution 12:[12]
You can subclass the button and add these:
class FitButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
self.imageView?.contentMode = .scaleAspectFill
self.contentHorizontalAlignment = .fill
self.contentVerticalAlignment = .fill
super.layoutSubviews()
}
}
Solution 13:[13]
This solution worked for me when I was using the Custom Button for the BarButtonItem
1. Loading image from Server
2. Crop image
3. Set the image for the button
Here is the function for crop image:
extension UIImage {
func crop(to:CGSize) -> UIImage {
guard let cgimage = self.cgImage else { return self }
let contextImage: UIImage = UIImage(cgImage: cgimage)
let contextSize: CGSize = contextImage.size
//Set to square
var posX: CGFloat = 0.0
var posY: CGFloat = 0.0
let cropAspect: CGFloat = to.width / to.height
var cropWidth: CGFloat = to.width
var cropHeight: CGFloat = to.height
if to.width > to.height { //Landscape
cropWidth = contextSize.width
cropHeight = contextSize.width / cropAspect
posY = (contextSize.height - cropHeight) / 2
} else if to.width < to.height { //Portrait
cropHeight = contextSize.height
cropWidth = contextSize.height * cropAspect
posX = (contextSize.width - cropWidth) / 2
} else { //Square
if contextSize.width >= contextSize.height { //Square on landscape (or square)
cropHeight = contextSize.height
cropWidth = contextSize.height * cropAspect
posX = (contextSize.width - cropWidth) / 2
}else{ //Square on portrait
cropWidth = contextSize.width
cropHeight = contextSize.width / cropAspect
posY = (contextSize.height - cropHeight) / 2
}
}
let rect: CGRect = CGRect(x : posX, y : posY, width : cropWidth, height : cropHeight)
// Create bitmap image from context using the rect
let imageRef: CGImage = contextImage.cgImage!.cropping(to: rect)!
// Create a new image based on the imageRef and rotate back to the original orientation
let cropped: UIImage = UIImage(cgImage: imageRef, scale: self.scale, orientation: self.imageOrientation)
cropped.draw(in: CGRect(x : 0, y : 0, width : to.width, height : to.height))
return cropped
}
}
Solution 14:[14]
Anyone experiencing this problem for IOS 15 should check out Matt's answer in this other question.
Xcode has changed a default style value for UIButtons. Switching style in the Buttons attributes from plain to default solves the issue.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow